NTP-28813| pause case changes| varshitha (#1338)

* NTP-28813| pause case changes| varshitha

* NTP-28813| pause case changes| varshitha

* NTP-28813| integration changes| varshitha

* NTP-28813| integration changes| varshitha

* NTP-28813| integration changes| varshitha

* NTP-28813| integration changes| varshitha

* NTP-28813| integration changes| varshitha

* NTP-28813| integration changes| varshitha
This commit is contained in:
Podili Varshitha
2025-01-23 20:21:55 +05:30
committed by GitHub
parent c66b19d728
commit 4830923461
18 changed files with 147 additions and 37 deletions

View File

@@ -66,7 +66,8 @@ export const TemplateTypes = {
MESSAGE_WAIVE_UNHOLD_NOTIFICATION_TEMPLATE: 'MESSAGE_WAIVE_UNHOLD_NOTIFICATION_TEMPLATE',
COLLECTION_PAUSE_EFFORTS_CASE_DEALLOCATED: 'COLLECTION_PAUSE_EFFORTS_CASE_DEALLOCATED',
ANOMALY_TRACKER_DETECTION: 'ANOMALY_TRACKER_DETECTION_V2',
ANOMALY_TRACKER_RESOLUTION: 'ANOMALY_TRACKER_RESOLUTION'
ANOMALY_TRACKER_RESOLUTION: 'ANOMALY_TRACKER_RESOLUTION',
COLLECTION_PAUSE_EFFORTS_CASE_PAUSED: 'COLLECTION_PAUSE_EFFORTS_CASE_PAUSED'
};
export const PaymentSuccessTemplateTypes = [
@@ -119,7 +120,8 @@ export const NotificationTabTemplateMap = {
TemplateTypes.BUSY_SCHEDULED_NOTIFICATION_TEMPLATE,
TemplateTypes.REVISIT_SCHEDULED_NOTIFICATION_TEMPLATE,
TemplateTypes.FIELD_BOT_REVISIT_SCHEDULED_NOTIFICATION_TEMPLATE,
TemplateTypes.MESSAGE_WAIVE_UNHOLD_NOTIFICATION_TEMPLATE
TemplateTypes.MESSAGE_WAIVE_UNHOLD_NOTIFICATION_TEMPLATE,
TemplateTypes.COLLECTION_PAUSE_EFFORTS_CASE_PAUSED
],
[NotificationTabTypes.All]: [
TemplateTypes.PAYMENT_MADE_TEMPLATE,
@@ -150,7 +152,8 @@ export const NotificationTabTemplateMap = {
TemplateTypes.SUPPORT_REQUEST_RESOLVED,
TemplateTypes.MESSAGE_WAIVE_UNHOLD_NOTIFICATION_TEMPLATE,
TemplateTypes.ANOMALY_TRACKER_DETECTION,
TemplateTypes.ANOMALY_TRACKER_RESOLUTION
TemplateTypes.ANOMALY_TRACKER_RESOLUTION,
TemplateTypes.COLLECTION_PAUSE_EFFORTS_CASE_PAUSED
],
[NotificationTabTypes.Tasks]: [
TemplateTypes.SUPPORT_REQUEST_RECEIVED,
@@ -257,6 +260,10 @@ export const TemplateInfoMap = {
label: 'Case De-allocated',
icon: <InfoNotification />
},
[TemplateTypes.COLLECTION_PAUSE_EFFORTS_CASE_PAUSED]: {
label: 'Case Paused',
icon: <ErrorIcon />
},
[TemplateTypes.AMEYO_CALL_DROP]: {
label: 'Ameyo Call Drop',
icon: <AmeyoCallDropIcon />

View File

@@ -1,3 +1,5 @@
import { Roles } from '../pages/auth/constants/AuthConstants';
export const fullMonthNames = [
'January',
'February',
@@ -70,3 +72,10 @@ export const ameyo_errorenous_message =
'You are marked as offline on Ameyo. please set yourself online again';
export const errorenous_break_reason = 'erroneous.channel.system.initiated.break';
export const PAUSE_CASE_LH_VISIBILITY_ROLES = [
Roles.ROLE_COLLECTION_EFFORTS_PAUSE_ACCESS,
Roles.ROLE_GLOBAL_ACCESS,
Roles.ROLE_COLLECTION_HEAD,
Roles.ROLE_USER_MANAGEMENT_ADMIN
];

View File

@@ -26,7 +26,8 @@ export const getWAInteractions =
customerReferenceId: customerRefrenceId,
size: 10,
page: 0
}
},
caseReferenceId: string
) =>
(dispatch: Dispatch) => {
dispatch(
@@ -38,7 +39,10 @@ export const getWAInteractions =
})
);
axiosInstance
.get(getApiUrl(ApiKeys.WA_INTERACTIONS), { params: waInteractionsPayload })
.get(getApiUrl(ApiKeys.WA_INTERACTIONS), {
headers: { caseReferenceId },
params: waInteractionsPayload
})
.then(res => {
dispatch(
setCaseDetail({

View File

@@ -8,6 +8,7 @@ import { Roles } from '@cp/src/pages/auth/constants/AuthConstants';
export const getBankStatementResponse = async (
lan: string,
customerId: string,
caseReferenceId: string,
bankStatementErrorCallback?: () => void
) => {
try {
@@ -16,7 +17,8 @@ export const getBankStatementResponse = async (
headers: {
customerreferenceid: customerId,
loanaccountnumber: lan,
donotHandleError: true
donotHandleError: true,
caseReferenceId: caseReferenceId
}
});
return response?.data?.data;

View File

@@ -32,6 +32,8 @@ import {
import { RootState } from '@cp/src/store';
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@cp/src/service/clickStream.constant';
import { createKey } from '@cp/src/utils/CaseDetail.utils';
import { PAUSE_CASE_LH_VISIBILITY_ROLES } from '@cp/src/constants/Common.constants';
const BankStatement = ({
isOnModal,
@@ -40,7 +42,7 @@ const BankStatement = ({
isOnModal: boolean;
setIsExpandedView: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
const { loanId = '', customerId = '' } = useParams();
const { loanId = '', customerId = '', caseReferenceId = '' } = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
@@ -79,10 +81,24 @@ const BankStatement = ({
updateAndNavigate(queryParams);
};
const isSnoozed = useSelector(
(state: RootState) =>
state.caseDetail.pageData?.[createKey(loanId, customerId)]?.pause?.data?.isPaused
);
const userRole = useSelector((state: RootState) => state.common?.userData?.roles);
const hasVisibilityRole = userRole?.some(role => PAUSE_CASE_LH_VISIBILITY_ROLES.includes(role));
useEffect(() => {
getBankStatementResponse(loanId, customerId, bankStatementErrorCallback).then(response => {
getBankStatement(response);
});
if (!isSnoozed || (isSnoozed && !hasVisibilityRole)) {
getBankStatementResponse(
loanId,
customerId,
caseReferenceId,
bankStatementErrorCallback
).then(response => {
getBankStatement(response);
});
}
setInitialQueryParams(queryParams);
return () => {
bankStatementInstance.current?.resetData();

View File

@@ -15,6 +15,7 @@ import { useNavigate, useParams } from 'react-router-dom';
import CircularLoader from '@navi/web-ui/lib/icons/CircularLoaderIcon';
import { Roles } from '@cp/pages/auth/constants/AuthConstants';
import { createKey } from '@cp/src/utils/CaseDetail.utils';
import { PAUSE_CASE_LH_VISIBILITY_ROLES } from '@cp/src/constants/Common.constants';
function AllDocumentsV2() {
const dispatch = useDispatch();
@@ -44,6 +45,10 @@ function AllDocumentsV2() {
state.caseDetail.pageData?.[createKey(loanId, customerId)]?.pause?.data?.isPaused
);
const hasVisibilityRole = user?.roles?.some(role =>
PAUSE_CASE_LH_VISIBILITY_ROLES.includes(role)
);
const tabs = createDynamicTabs(documents || { documentDetails: {} }, isSnoozed) || [];
const { [DOCUMENTS]: queryParams } = readQueryParams();
@@ -73,7 +78,7 @@ function AllDocumentsV2() {
const mergedTabs = [...tabs];
if (isBankStatementAccessible) {
if (isBankStatementAccessible && !(isSnoozed && !hasVisibilityRole)) {
mergedTabs.push(...staticTabs);
}
useEffect(() => {

View File

@@ -8,7 +8,7 @@ type TabType = {
component: React.ReactNode;
};
export const createDynamicTabs = (documents: Documents, isSnoozed: boolean) => {
export const createDynamicTabs = (documents: Documents, isSnoozed: boolean | undefined) => {
if (Object.keys(documents?.documentDetails || {}).length === 0) {
return [];
}

View File

@@ -2,7 +2,7 @@ import TabItem from '@navi/web-ui/lib/components/Tabs/TabItem';
import Tabs from '@navi/web-ui/lib/components/Tabs/Tabs';
import React, { useCallback, useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { COMMUNICATION_TABS } from '../../constants';
import { COMMUNICATION_TAB_KEYS, COMMUNICATION_TABS } from '../../constants';
import styles from './index.module.scss';
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@cp/src/service/clickStream.constant';
@@ -14,6 +14,7 @@ import { RootState } from '@cp/src/store';
import { DPD_BUCKET_30 } from './constants';
import { GenericObject } from '@cp/src/types/CommonConstans';
import { createQueryParams, readQueryParams } from '@cp/utils/QueryParamsHelper';
import { PAUSE_CASE_LH_VISIBILITY_ROLES } from '@cp/src/constants/Common.constants';
function CommunicationHistory() {
const { loanId = '', customerId = '' } = useParams();
@@ -52,6 +53,15 @@ function CommunicationHistory() {
updateAndNavigate(updatedParams);
}, []);
const isSnoozed = useSelector(
(state: RootState) =>
state.caseDetail.pageData?.[createKey(loanId, customerId)]?.pause?.data?.isPaused
);
const userRole = useSelector((state: RootState) => state.common.userData?.roles);
const hasVisibilityRole = userRole?.some(role => PAUSE_CASE_LH_VISIBILITY_ROLES.includes(role));
const hideTab = isSnoozed && !hasVisibilityRole;
return (
<div className={cx(styles.tabContainer, 'tabContentScroll')}>
<Tabs
@@ -63,7 +73,9 @@ function CommunicationHistory() {
selectedTabKey={queryParams?.selectedTab}
onTabChange={event => handleTabChange(event as string)}
>
{COMMUNICATION_TABS.map(tab => (
{COMMUNICATION_TABS.filter(
tab => !(hideTab && tab.key === COMMUNICATION_TAB_KEYS.NAVI_WHATSAPP)
).map(tab => (
<TabItem key={tab.key} label={tab.value}>
{tab.component}
</TabItem>

View File

@@ -27,6 +27,7 @@ import { getPauseDate } from '../Pause/action';
import AlertCircleWithBackdrop from '@cp/src/assets/icons/AlertCircleWithBackdrop';
import { CaseStatus } from '../../interfaces/CaseDetail.type';
import { PAUSE_CASE_LH_VISIBILITY_ROLES } from '@cp/src/constants/Common.constants';
const CustomerSummary = () => {
const {
@@ -97,6 +98,28 @@ const CustomerSummary = () => {
const escalationStatus = escalationsDetails?.status;
const isCasePaused = useSelector(
(state: RootState) =>
state?.caseDetail?.pageData?.[createKey(loanId, customerId)]?.pause?.data?.isPaused
);
const userRole = useSelector((state: RootState) => state.common.userData?.roles);
const hasVisibilityRole = userRole?.some(role => PAUSE_CASE_LH_VISIBILITY_ROLES.includes(role));
const isRiskSegmentationVisibleOnPause = isCasePaused && !hasVisibilityRole;
const showRedRiskSegmentation =
!isRiskSegmentationVisibleOnPause &&
isRiskSegmentationVisible &&
riskSegment === RiskRating.HIGH_RISK;
const showYellowRiskSegmentation =
!isRiskSegmentationVisibleOnPause &&
isRiskSegmentationVisible &&
riskSegment === RiskRating.MEDIUM_RISK;
const showGreenRiskSegmentation =
!isRiskSegmentationVisibleOnPause &&
isRiskSegmentationVisible &&
riskSegment === RiskRating.LOW_RISK;
return (
<div className={styles.customerDetailSection}>
{isPaused ? (
@@ -104,7 +127,7 @@ const CustomerSummary = () => {
<div className="p-3 !bg-[var(--pale-red)] rounded-lg flex gap-2 items-center">
<AlertCircleWithBackdrop />
<Typography variant="p3">
Collections is snoozed for this LAN. This will be un-snoozed on{' '}
Collections is paused for this LAN. This will be un-paused after{' '}
<strong>
{dateFormat(new Date(pauseDate!), DateFormat.LONG_DATE_FORMAT_WITHOUT_TIME)}
</strong>
@@ -116,10 +139,9 @@ const CustomerSummary = () => {
<div
className={cx(styles.customerDetailContainer, {
[styles.containerWithAgentName]: !!assignedFieldAgent,
'!bg-[--pale-red]': isRiskSegmentationVisible && riskSegment === RiskRating.HIGH_RISK,
'!bg-[--yellow-medium]':
isRiskSegmentationVisible && riskSegment === RiskRating.MEDIUM_RISK,
'!bg-[--green-light]': isRiskSegmentationVisible && riskSegment === RiskRating.LOW_RISK
'!bg-[--pale-red]': showRedRiskSegmentation,
'!bg-[--yellow-medium]': showYellowRiskSegmentation,
'!bg-[--green-light]': showGreenRiskSegmentation
})}
>
{assignedFieldAgent ? (

View File

@@ -70,7 +70,7 @@ const WAINTERACTIONS_COL_DEFS = [
];
const Interactions = () => {
const { loanId = '', customerId = '' } = useParams();
const { loanId = '', customerId = '', caseReferenceId = '' } = useParams();
const pageData = useSelector(
(state: RootState) => state.caseDetail.pageData?.[createKey(loanId, customerId)]
);
@@ -92,7 +92,7 @@ const Interactions = () => {
const getWAInteractionsData = () => {
const payload = createPayloadForWAInteractions();
dispatch(fetchWAInteractions(loanId, customerId, payload));
dispatch(fetchWAInteractions(loanId, customerId, payload, caseReferenceId));
};
useEffect(() => {

View File

@@ -32,20 +32,20 @@ export const defaultValues = {
};
export enum PAUSE_MESSAGE {
UPDATE_PAUSE = 'Update snooze on this LAN',
PAUSE_COLLECTIONS = 'Snooze collections for this LAN',
UPDATE_PAUSE = 'Update pause on this LAN',
PAUSE_COLLECTIONS = 'Pause collections for this LAN',
UPDATE_PAUSE_DAYS = 'Update pause days',
PAUSE_TILL = 'Snooze till',
PAUSE_IMMEDIATE_TODAY = 'The update will be effective from today',
PAUSE_TILL = 'Pause till',
PAUSE_IMMEDIATE_TODAY = 'The update will be effective from now onwards',
DATE_ERROR = 'Please enter some other date',
REASON_UPDATE = 'Reason for update',
REASON_PAUSE = 'Reason for snoozing',
REASON_PAUSE = 'Reason for pausing',
OTHER_REASONS = 'Other reasons',
UPDATE_PAUSE_DATE = 'Update snooze date',
PAUSE_COLLECTIONS_EFFORTS = 'Snooze collections efforts'
UPDATE_PAUSE_DATE = 'Update pause date',
PAUSE_COLLECTIONS_EFFORTS = 'Pause collections efforts'
}
export enum PAUSE_ACTION_MESSAGE {
FAILED_TO_PAUSE = 'Failed to snooze collections',
COLLECTIONS_PAUSED = 'Collections snoozed successfully'
FAILED_TO_PAUSE = 'Failed to pause collections',
COLLECTIONS_PAUSED = 'Collections paused successfully'
}

View File

@@ -17,11 +17,12 @@ import styles from './index.module.scss';
import cx from 'classnames';
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@cp/src/service/clickStream.constant';
import { getDateAfterCertainMonths } from '@cp/src/utils/commonUtils';
const PauseAction = () => {
const todayDate = getFormatDate(new Date(), DateFormat.YYYY_MM_DD);
const threeMonthsLaterDate = getFormatDate(
new Date(new Date().setMonth(new Date().getMonth() + 3)),
getDateAfterCertainMonths(new Date(), 12),
DateFormat.YYYY_MM_DD
);

View File

@@ -409,7 +409,7 @@ const CallBridge = (props: CallBridgeProps) => {
};
const tooltipContent = caseSnoozed
? 'Calling is disabled as collections is snoozed for this LAN'
? 'Calling is disabled as collections is paused for this LAN'
: `You can call the customer from ${formatToStandardTimeInHours(getCallingWindowStartTime)} ${
isPostMidnight ? 'tomorrow' : 'today'
}`;

View File

@@ -20,7 +20,7 @@ import styles from './index.module.scss';
import { readQueryParams } from '@cp/utils/QueryParamsHelper';
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@cp/src/service/clickStream.constant';
import { IPhoneNumberLimit, LIMIT_TYPE } from '../../../interfaces/CaseDetail.type';
import { CaseStatus, IPhoneNumberLimit, LIMIT_TYPE } from '../../../interfaces/CaseDetail.type';
import { Tooltip, TooltipContent, TooltipTrigger } from 'src/components/TooltipV2/TooltipV2';
import cx from 'classnames';
import { setIsFeedbackFormDirty } from '../../reducer/feedbackSlice';
@@ -33,7 +33,7 @@ import { Roles } from '@cp/pages/auth/constants/AuthConstants';
import ClockIcon from '@cp/assets/icons/ClockIcon';
import dayjs from 'dayjs';
import { CALL_SOURCE_LIMIT } from '@cp/pages/CaseDetails/constants/Overview.constant';
import { LIMIT_CALLS } from '@cp/src/constants/Common.constants';
import { LIMIT_CALLS, PAUSE_CASE_LH_VISIBILITY_ROLES } from '@cp/src/constants/Common.constants';
interface DefaultContactsProps {
feedback: IFeedbackForm;
@@ -70,6 +70,14 @@ const DefaultContacts: React.FC<DefaultContactsProps> = props => {
const addedIdentifier = useSelector((state: RootState) => state.feedBack.addedIdentifier);
const [isOptionDisabled, setIsOptionDisabled] = React.useState(false);
const isGlobalAccessGiven = user?.roles?.includes(Roles.ROLE_GLOBAL_ACCESS);
const isCasePaused = useSelector(
(state: RootState) =>
state?.caseDetail?.pageData?.[createKey(loanId, customerId)]?.pause?.data?.isPaused
);
const hasVisibilityRole = user?.roles?.some(role =>
PAUSE_CASE_LH_VISIBILITY_ROLES.includes(role)
);
const disableCTAs = isGlobalAccessGiven ? !editAccessFlag : false;
const toSlashUserPage = () => {
@@ -263,6 +271,8 @@ const DefaultContacts: React.FC<DefaultContactsProps> = props => {
const disableGoogleSearchButton = !feedback.telephone;
const disableCTAsForSlash = disableCTAs || (isCasePaused && !hasVisibilityRole);
return (
<div
key={`${feedback.telephone}_${feedback.initialOptionId}_${feedback.initialQuestionId}_${feedback.selectedChip}`}
@@ -273,7 +283,7 @@ const DefaultContacts: React.FC<DefaultContactsProps> = props => {
<Typography as={'p'} className={styles.lightText} variant={'p4'}>
Select number
</Typography>
<Button onClick={toSlashUserPage} disabled={disableCTAs} variant={'text'}>
<Button onClick={toSlashUserPage} disabled={disableCTAsForSlash} variant={'text'}>
Open Slash
</Button>
</div>

View File

@@ -3,11 +3,22 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@cp/src/components/Tool
import { getRiskBarColor } from '@cp/src/utils/commonUtils';
import EllipsisText from '@cp/src/components/EllipsisText';
import styles from './Cases.module.scss';
import { useSelector } from 'react-redux';
import { RootState } from '@cp/src/store';
import { createKey } from '@cp/src/utils/CaseDetail.utils';
import { useParams } from 'react-router-dom';
import { CaseStatus } from '../../CaseDetails/interfaces/CaseDetail.type';
import { PAUSE_CASE_LH_VISIBILITY_ROLES } from '@cp/src/constants/Common.constants';
const CasesCustomerName = (props: { data: AllCasesSummary }) => {
const customerName = props?.data?.customerName;
const riskSegment = props?.data?.riskSegment;
const barColor = getRiskBarColor(riskSegment || '');
const isSnoozed = props?.data?.status === CaseStatus.ON_HOLD;
const userRole = useSelector((state: RootState) => state.common.userData?.roles);
const hasVisibilityRole = userRole?.some(role => PAUSE_CASE_LH_VISIBILITY_ROLES.includes(role));
const isRiskSegmentationVisibleOnPause = isSnoozed && !hasVisibilityRole;
const barColor = isRiskSegmentationVisibleOnPause ? getRiskBarColor(riskSegment || '') : '';
return (
<div className="flex flex-row gap-4 items-center">

View File

@@ -54,6 +54,7 @@ export interface AllCasesSummary {
riskSegment?: string;
businessVertical?: string;
caseReferenceId?: string;
status?: string;
}
export type AggregationType = 'SUM' | 'MAX' | 'MIN' | 'COUNT';

View File

@@ -4,6 +4,7 @@ import { formatAmount, getRiskBarColor } from 'src/utils/commonUtils';
import { AgentTypes } from './interface';
import styles from './TeamLead.module.scss';
import { NaviTableColumnDef } from '@cp/src/components/NaviTable/interfaces';
import { CaseStatus } from '../CaseDetails/interfaces/CaseDetail.type';
export const TEAM_LEAD_COLUMN_DEFS_NAVI_TABLE: NaviTableColumnDef<any, any>[] = [
{
@@ -11,7 +12,10 @@ export const TEAM_LEAD_COLUMN_DEFS_NAVI_TABLE: NaviTableColumnDef<any, any>[] =
header: '',
cell: info => {
const barColor = getRiskBarColor(info?.row?.original?.riskSegment);
return <div className={'w-2 h-full'} style={{ backgroundColor: barColor }} />;
const isSnoozed = info?.row?.original?.status === CaseStatus.ON_HOLD;
return (
<div className={'w-2 h-full'} style={{ backgroundColor: !isSnoozed ? barColor : '' }} />
);
},
cellStyle: {
padding: 0

View File

@@ -594,3 +594,9 @@ export const getRiskBarColor = (riskSegment: string): string => {
return riskSegmentColors[riskSegment] || '';
};
export const getDateAfterCertainMonths = (date: Date, months: number) => {
const newDate = new Date(date);
newDate.setMonth(date.getMonth() + months);
return newDate;
};