NTP-21559|snooze|aman singh (#1283)
* NTP-21559|snooze|aman singh * NTP-21559|snooze|aman singh * NTP-21033|snooze fix|aman singh * NTP-25436| Start showing scrapped WhatsApp chats again on Longhorn | Aman singh * NTP-21033|snooze fix|aman singh * NTP-21033|snooze fix|aman singh * NTP-21033|snooze fix|aman singh * NTP-21033|snooze fix|aman singh
This commit is contained in:
29
src/assets/icons/AlertCircleWithBackdrop.tsx
Normal file
29
src/assets/icons/AlertCircleWithBackdrop.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
|
||||
const AlertCircleWithBackdrop = () => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<circle cx="10" cy="10" r="10" fill="#F69E9E" />
|
||||
<circle cx="10" cy="10" r="6" fill="#E92C2C" />
|
||||
<mask
|
||||
id="mask0_4642_53990"
|
||||
style={{ maskType: 'alpha' }}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="4"
|
||||
y="4"
|
||||
width="12"
|
||||
height="12"
|
||||
>
|
||||
<rect x="4" y="4" width="12" height="12" fill="#D9D9D9" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_4642_53990)">
|
||||
<path
|
||||
d="M10 10.9922C9.85833 10.9922 9.73958 10.9443 9.64375 10.8484C9.54792 10.7526 9.5 10.6339 9.5 10.4922V6.99219C9.5 6.85052 9.54792 6.73177 9.64375 6.63594C9.73958 6.5401 9.85833 6.49219 10 6.49219C10.1417 6.49219 10.2604 6.5401 10.3562 6.63594C10.4521 6.73177 10.5 6.85052 10.5 6.99219V10.4922C10.5 10.6339 10.4521 10.7526 10.3562 10.8484C10.2604 10.9443 10.1417 10.9922 10 10.9922ZM10 13.4922C9.85833 13.4922 9.73958 13.4443 9.64375 13.3484C9.54792 13.2526 9.5 13.1339 9.5 12.9922C9.5 12.8505 9.54792 12.7318 9.64375 12.6359C9.73958 12.5401 9.85833 12.4922 10 12.4922C10.1417 12.4922 10.2604 12.5401 10.3562 12.6359C10.4521 12.7318 10.5 12.8505 10.5 12.9922C10.5 13.1339 10.4521 13.2526 10.3562 13.3484C10.2604 13.4443 10.1417 13.4922 10 13.4922Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default AlertCircleWithBackdrop;
|
||||
@@ -14,6 +14,7 @@ import { GenericObject } from '@cp/src/types/CommonConstans';
|
||||
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';
|
||||
|
||||
function AllDocumentsV2() {
|
||||
const dispatch = useDispatch();
|
||||
@@ -38,7 +39,12 @@ function AllDocumentsV2() {
|
||||
caseReferenceId = ''
|
||||
} = useParams();
|
||||
|
||||
const tabs = createDynamicTabs(documents || { documentDetails: {} }) || [];
|
||||
const isSnoozed = useSelector(
|
||||
(state: RootState) =>
|
||||
state.caseDetail.pageData?.[createKey(loanId, customerId)]?.pause?.data?.isPaused
|
||||
);
|
||||
|
||||
const tabs = createDynamicTabs(documents || { documentDetails: {} }, isSnoozed) || [];
|
||||
|
||||
const { [DOCUMENTS]: queryParams } = readQueryParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -8,11 +8,28 @@ type TabType = {
|
||||
component: React.ReactNode;
|
||||
};
|
||||
|
||||
export const createDynamicTabs = (documents: Documents) => {
|
||||
export const createDynamicTabs = (documents: Documents, isSnoozed: boolean) => {
|
||||
if (Object.keys(documents?.documentDetails || {}).length === 0) {
|
||||
return [];
|
||||
}
|
||||
const tabs: TabType[] = [];
|
||||
|
||||
if (isSnoozed) {
|
||||
if (documents?.documentDetails?.NAVI) {
|
||||
tabs.push({
|
||||
key: documents?.documentDetails?.NAVI?.tabKey || '',
|
||||
value: documents?.documentDetails?.NAVI?.tabName || '',
|
||||
component: (
|
||||
<DynamicDocumentTab
|
||||
documentTabKey={documents?.documentDetails?.NAVI?.tabKey || ''}
|
||||
key={documents?.documentDetails?.NAVI?.tabKey}
|
||||
/>
|
||||
)
|
||||
});
|
||||
}
|
||||
return tabs;
|
||||
}
|
||||
|
||||
Object.keys(documents?.documentDetails || {}).forEach(key => {
|
||||
tabs.push({
|
||||
key: documents?.documentDetails?.[key]?.tabKey || '',
|
||||
|
||||
@@ -17,7 +17,14 @@ import {
|
||||
getTelephones,
|
||||
getTelephonesV2
|
||||
} from '../actions/casesDetailsActions';
|
||||
import { CONTACT_TYPES, CUSTOMER_TAGS, CUSTOMER_TAGS_READABLE, TABS, TAB_KEYS } from '../constants';
|
||||
import {
|
||||
CONTACT_TYPES,
|
||||
CUSTOMER_TAGS,
|
||||
CUSTOMER_TAGS_READABLE,
|
||||
TABS,
|
||||
TAB_KEYS,
|
||||
TabsTORemove
|
||||
} from '../constants';
|
||||
// styles, images and icons
|
||||
import { TabItemKey, TabsChild } from '@navi/web-ui/lib/components/Tabs/types';
|
||||
import { TagColors } from '@navi/web-ui/lib/primitives/Tag/types';
|
||||
@@ -81,12 +88,26 @@ const CaseDetail: React.FC<React.PropsWithChildren> = () => {
|
||||
const bankStatementStatus = useSelector(
|
||||
(state: RootState) => state.bankStatementSlice.bankStatementStatus
|
||||
);
|
||||
const shouldNotHidePauseCasesTab = useSelector(
|
||||
(state: RootState) => state.common.shouldNotHidePauseCasesTab
|
||||
);
|
||||
|
||||
const isSnoozed = useSelector(
|
||||
(state: RootState) =>
|
||||
state.caseDetail.pageData?.[createKey(loanId, customerId)]?.pause?.data?.isPaused
|
||||
);
|
||||
|
||||
const filteredTabs = useMemo(() => {
|
||||
const tabs: TabsChild[] = [];
|
||||
TABS.forEach(tab => {
|
||||
// Checking if the isVisibleKey flag key is true in featureFlags
|
||||
// or by default showing the tab
|
||||
if (isSnoozed && !shouldNotHidePauseCasesTab) {
|
||||
if (TabsTORemove[tab.key]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const isFeatureFlagEnabled =
|
||||
tab.featureFlagKey && caseDetailTabsFeatureFlag?.[tab?.featureFlagKey];
|
||||
const isDocumentsTab = tab.key === TAB_KEYS.DOCUMENTS;
|
||||
@@ -104,7 +125,7 @@ const CaseDetail: React.FC<React.PropsWithChildren> = () => {
|
||||
}
|
||||
});
|
||||
return tabs;
|
||||
}, [caseDetailTabsFeatureFlag, bankStatementStatus]);
|
||||
}, [caseDetailTabsFeatureFlag, bankStatementStatus, isSnoozed, shouldNotHidePauseCasesTab]);
|
||||
|
||||
useEffect(() => {
|
||||
setCustomerId(customerId);
|
||||
@@ -215,6 +236,7 @@ const CaseDetail: React.FC<React.PropsWithChildren> = () => {
|
||||
{!!filteredTabs.length && (
|
||||
<div className={cx([styles.tabContainer, { [styles.heightAdjusting]: tags.length }])}>
|
||||
<Tabs
|
||||
key={filteredTabs.length}
|
||||
tabsClassName={styles.containerClass}
|
||||
selectedTabKey={selectedTab}
|
||||
onTabChange={handleTabChange}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Button, Chip, Typography } from '@navi/web-ui/lib/primitives';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import CircularProgress from 'src/components/ProgressBars/circularProgress/CircularProgress';
|
||||
import { RISK_TAGS, RISK_TAG_SUBSTRING } from 'src/pages/Cases/constants/CasesConstants';
|
||||
@@ -23,6 +23,10 @@ import { Roles } from '@cp/pages/auth/constants/AuthConstants';
|
||||
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
|
||||
import { AgentTrackingEvents } from '@cp/src/service/clickStream.constant';
|
||||
import EscalationInfo from '@cp/pages/CaseDetails/components/CustomerSummary/EscalationInfo';
|
||||
import { getPauseDate } from '../Pause/action';
|
||||
|
||||
import AlertCircleWithBackdrop from '@cp/src/assets/icons/AlertCircleWithBackdrop';
|
||||
import { CaseStatus } from '../../interfaces/CaseDetail.type';
|
||||
|
||||
const CustomerSummary = () => {
|
||||
const {
|
||||
@@ -31,6 +35,7 @@ const CustomerSummary = () => {
|
||||
caseReferenceId = '',
|
||||
caseBusinessVertical = ''
|
||||
} = useParams();
|
||||
const dispatch = useDispatch();
|
||||
const pageDetail = useSelector(
|
||||
(state: RootState) => state.caseDetail.pageData?.[createKey(loanId, customerId)]
|
||||
);
|
||||
@@ -58,6 +63,14 @@ const CustomerSummary = () => {
|
||||
roles?.includes(Roles.ROLE_IN_HOUSE_CSA_AGENT) || roles?.includes(Roles.ROLE_EXTERNAL_CSA),
|
||||
[roles]
|
||||
);
|
||||
const pauseDate = pageDetail?.pause?.data?.pauseTillDate;
|
||||
const isPaused = pageDetail?.pause?.data?.isPaused;
|
||||
|
||||
useEffect(() => {
|
||||
if (pageDetail?.details?.data?.status === CaseStatus.ON_HOLD) {
|
||||
dispatch(getPauseDate(loanId, customerId, caseReferenceId, caseBusinessVertical));
|
||||
}
|
||||
}, [pageDetail?.details?.data]);
|
||||
|
||||
enum EscalationStatus {
|
||||
ERROR = 'ERROR',
|
||||
@@ -83,9 +96,24 @@ const CustomerSummary = () => {
|
||||
const escalationsDetails = pageDetail?.details?.data?.escalationDetails || null;
|
||||
|
||||
const escalationStatus = escalationsDetails?.status;
|
||||
ON_HOLD;
|
||||
|
||||
return (
|
||||
<div className={styles.customerDetailSection}>
|
||||
{isPaused ? (
|
||||
<div className="p-3">
|
||||
<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{' '}
|
||||
<strong>
|
||||
{dateFormat(new Date(pauseDate!), DateFormat.LONG_DATE_FORMAT_WITHOUT_TIME)}
|
||||
</strong>
|
||||
.
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className={cx(styles.customerDetailContainer, {
|
||||
[styles.containerWithAgentName]: !!assignedFieldAgent,
|
||||
|
||||
@@ -2,6 +2,11 @@ import axiosInstance, { ApiKeys, getApiUrl, logError } from '@cp/src/utils/ApiHe
|
||||
import { toast } from '@navi/web-ui/lib/primitives/Toast';
|
||||
import { PAUSE_ACTION_MESSAGE } from './const';
|
||||
import { isFunction } from '@cp/src/utils/commonUtils';
|
||||
import { Dispatch } from '@reduxjs/toolkit';
|
||||
import { setCaseDetail } from '../../reducers/caseDetailSlice';
|
||||
import { STORE_KEYS } from '../../constants';
|
||||
import { addClickstreamEvent } from '@cp/src/service/clickStreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@cp/src/service/clickStream.constant';
|
||||
|
||||
export const getLanPauseStatus = async (caseId: string, caseBusinessVertical: string) => {
|
||||
try {
|
||||
@@ -13,6 +18,22 @@ export const getLanPauseStatus = async (caseId: string, caseBusinessVertical: st
|
||||
}
|
||||
};
|
||||
|
||||
export const getPauseDate =
|
||||
(lan: string, customerID: string, caseId: string, caseBusinessVertical: string) =>
|
||||
(dispatch: Dispatch) => {
|
||||
const url = getApiUrl(ApiKeys.GET_PAUSE_STATUS, { caseId });
|
||||
axiosInstance.get(url, { headers: { caseBusinessVertical } }).then(response => {
|
||||
dispatch(
|
||||
setCaseDetail({
|
||||
lan,
|
||||
customerRefrenceId: customerID,
|
||||
key: STORE_KEYS.PAUSE_STATUS,
|
||||
data: response.data
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
interface PauseActionInput {
|
||||
caseId: string;
|
||||
payload: any;
|
||||
@@ -34,6 +55,11 @@ export const submitPauseAction = ({
|
||||
.post(url, payload)
|
||||
.then(() => {
|
||||
toast.success(PAUSE_ACTION_MESSAGE.COLLECTIONS_PAUSED);
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_SNOOZE_EFFORTS_UPDATED, {
|
||||
caseId,
|
||||
reason: payload.pauseEffortsReason,
|
||||
pauseEffortsTillDate: payload.pauseEffortsTillDate
|
||||
});
|
||||
successCallback();
|
||||
})
|
||||
.catch(error => {
|
||||
|
||||
@@ -32,20 +32,20 @@ export const defaultValues = {
|
||||
};
|
||||
|
||||
export enum PAUSE_MESSAGE {
|
||||
UPDATE_PAUSE = 'Update pause on this LAN',
|
||||
PAUSE_COLLECTIONS = 'Pause collections for this LAN',
|
||||
UPDATE_PAUSE = 'Update snooze on this LAN',
|
||||
PAUSE_COLLECTIONS = 'Snooze collections for this LAN',
|
||||
UPDATE_PAUSE_DAYS = 'Update pause days',
|
||||
PAUSE_TILL = 'Pause till',
|
||||
PAUSE_IMMEDIATE_TODAY = 'The pause will be immediate from today',
|
||||
PAUSE_TILL = 'Snooze till',
|
||||
PAUSE_IMMEDIATE_TODAY = 'The update will be effective from today',
|
||||
DATE_ERROR = 'Please enter some other date',
|
||||
REASON_UPDATE = 'Reason for update',
|
||||
REASON_PAUSE = 'Reason for pause',
|
||||
REASON_PAUSE = 'Reason for snoozing',
|
||||
OTHER_REASONS = 'Other reasons',
|
||||
UPDATE_PAUSE_DATE = 'Update pause date',
|
||||
PAUSE_COLLECTIONS_EFFORTS = 'Pause collections efforts'
|
||||
UPDATE_PAUSE_DATE = 'Update snooze date',
|
||||
PAUSE_COLLECTIONS_EFFORTS = 'Snooze collections efforts'
|
||||
}
|
||||
|
||||
export enum PAUSE_ACTION_MESSAGE {
|
||||
FAILED_TO_PAUSE = 'Failed to pause collections',
|
||||
COLLECTIONS_PAUSED = 'Collections paused successfully'
|
||||
FAILED_TO_PAUSE = 'Failed to snooze collections',
|
||||
COLLECTIONS_PAUSED = 'Collections snoozed successfully'
|
||||
}
|
||||
|
||||
@@ -63,5 +63,11 @@
|
||||
padding: 16px 16px;
|
||||
border-top: 1px solid var(--navi-color-gray-border);
|
||||
height: fit-content;
|
||||
|
||||
button {
|
||||
&:hover {
|
||||
background-color: var(--navi-color-red-dark) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,18 @@ import { DATE_TIME_TYPE } from '@cp/src/components/DateTimePicker/constants';
|
||||
import { DateFormat, getFormatDate, getFormattedDateWithFullYear } from '@cp/src/utils/DateHelper';
|
||||
import { defaultValues, PAUSE_MESSAGE, PAUSE_REASON_TYPES, PauseFormValues } from './const';
|
||||
import InfoFilledIcon from '@cp/src/assets/icons/InfoFilledIcon';
|
||||
import { getLanPauseStatus, submitPauseAction } from './action';
|
||||
import { getPauseDate, submitPauseAction } from './action';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { RootState } from '@cp/src/store';
|
||||
import { createKey } from '@cp/src/utils/CaseDetail.utils';
|
||||
import { logError } from '@cp/src/utils/ApiHelper';
|
||||
import PauseIcon from '@cp/src/assets/icons/PauseIcon';
|
||||
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';
|
||||
|
||||
const PauseAction = () => {
|
||||
const todayDate = getFormatDate(new Date(), DateFormat.YYYY_MM_DD);
|
||||
@@ -24,13 +26,18 @@ const PauseAction = () => {
|
||||
);
|
||||
|
||||
const [isSelectedActionUpdatePause, setIsSelectedActionUpdatePause] = useState(false);
|
||||
const [pausedDate, setPausedDate] = useState('');
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const { loanId = '', customerId = '', caseBusinessVertical = '' } = useParams();
|
||||
const pageDetail = useSelector(
|
||||
(state: RootState) => state.caseDetail.pageData?.[createKey(loanId, customerId)]
|
||||
);
|
||||
|
||||
const caseSnoozeDate = pageDetail?.pause?.data?.pauseTillDate || '';
|
||||
const isSnoozed = pageDetail?.pause?.data?.isPaused || '';
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const caseReferenceId = pageDetail?.details?.data?.caseReferenceId || '';
|
||||
|
||||
const handleSubmitState = (state: boolean) => {
|
||||
@@ -50,9 +57,7 @@ const PauseAction = () => {
|
||||
|
||||
const fetchPauseStatus = async () => {
|
||||
try {
|
||||
const getPauseStatusData = await getLanPauseStatus(caseReferenceId, caseBusinessVertical);
|
||||
setIsSelectedActionUpdatePause(getPauseStatusData?.isPaused);
|
||||
setPausedDate(getPauseStatusData?.pauseTillDate);
|
||||
dispatch(getPauseDate(loanId, customerId, caseReferenceId, caseBusinessVertical));
|
||||
} catch (error) {
|
||||
logError(error, 'Error while fetching pause status');
|
||||
}
|
||||
@@ -70,6 +75,13 @@ const PauseAction = () => {
|
||||
pauseEffortsReason: reason === 'OTHERS' ? data.details : reason
|
||||
};
|
||||
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.LH_SNOOZE_EFFORTS_UPDATED, {
|
||||
caseId: caseReferenceId,
|
||||
reason: payload.pauseEffortsReason,
|
||||
pauseEffortsTillDate: pauseDate,
|
||||
loanId: loanId
|
||||
});
|
||||
|
||||
submitPauseAction({
|
||||
caseId: caseReferenceId,
|
||||
payload: payload,
|
||||
@@ -87,35 +99,19 @@ const PauseAction = () => {
|
||||
(watch('reason') === 'OTHERS' && !watch('details'));
|
||||
|
||||
return (
|
||||
<div className={styles.container} key={isSelectedActionUpdatePause as unknown as string}>
|
||||
<div className={styles.container} key={String(isSnoozed)}>
|
||||
<div className={styles.feedback}>
|
||||
<div className={'mt-6 mb-4'}>
|
||||
<Typography variant="h3">
|
||||
{isSelectedActionUpdatePause
|
||||
? PAUSE_MESSAGE.UPDATE_PAUSE
|
||||
: PAUSE_MESSAGE.PAUSE_COLLECTIONS}
|
||||
{isSnoozed ? PAUSE_MESSAGE.UPDATE_PAUSE : PAUSE_MESSAGE.PAUSE_COLLECTIONS}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
{isSelectedActionUpdatePause ? (
|
||||
<div className="flex bg-[var(--navi-color-gray-bg-secondary)] pt-3 pb-3 pr-3.5 pl-3 rounded">
|
||||
<InfoFilledIcon width={20} height={20} fill={'var(--blue-base)'} />
|
||||
|
||||
<Typography variant="p4" className="ml-2">
|
||||
Collections efforts have been paused till{' '}
|
||||
<Typography as="span" variant="h5" color="var(--navi-color-gray-c1)">
|
||||
{pausedDate ? getFormattedDateWithFullYear(new Date(pausedDate)) : ''}
|
||||
</Typography>
|
||||
</Typography>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="pauseDate"
|
||||
rules={{
|
||||
validate: value =>
|
||||
!(isSelectedActionUpdatePause && value === pausedDate) || PAUSE_MESSAGE.DATE_ERROR
|
||||
!(isSelectedActionUpdatePause && value === caseSnoozeDate) || PAUSE_MESSAGE.DATE_ERROR
|
||||
}}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<DateTimePickerComponent
|
||||
@@ -195,7 +191,6 @@ const PauseAction = () => {
|
||||
type={'submit'}
|
||||
onClick={handleSubmit(handleSubmitForm)}
|
||||
className="bg-[var(--navi-color-red-base)]"
|
||||
startAdornment={<PauseIcon />}
|
||||
disabled={isFormDisabled}
|
||||
>
|
||||
{isSelectedActionUpdatePause
|
||||
|
||||
@@ -65,9 +65,22 @@ const Payments = () => {
|
||||
(state: RootState) =>
|
||||
state?.caseDetail?.pageData?.[createKey(loanId, customerId)]?.details?.data?.editAccessFlag
|
||||
);
|
||||
const pauseEfforts = useSelector(
|
||||
(state: RootState) =>
|
||||
state?.caseDetail?.pageData?.[createKey(loanId, customerId)]?.pause?.data?.isPaused
|
||||
);
|
||||
|
||||
const shouldDisableCTA = () => {
|
||||
if (isGlobalAccessGiven) {
|
||||
return !editAccessFlag;
|
||||
}
|
||||
|
||||
return pauseEfforts ? true : false;
|
||||
};
|
||||
|
||||
const user = useSelector((state: RootState) => state?.common?.userData);
|
||||
const isGlobalAccessGiven = user?.roles?.includes(Roles.ROLE_GLOBAL_ACCESS);
|
||||
const disableCTAs = isGlobalAccessGiven ? !editAccessFlag : false;
|
||||
const disableCTAs = shouldDisableCTA();
|
||||
const [copyingLink, setCopyingLink] = useState<boolean>(false);
|
||||
const [sendLink, setSendLink] = useState<boolean>(false);
|
||||
const [foreclosureAmount, setForeclosureAmount] = useState(null);
|
||||
|
||||
@@ -54,6 +54,11 @@ type Tabs = {
|
||||
featureFlagKey?: string;
|
||||
};
|
||||
|
||||
export const TabsTORemove = {
|
||||
[TAB_KEYS.ADDRESSES]: true,
|
||||
[TAB_KEYS.CONTACT]: true
|
||||
};
|
||||
|
||||
export const TABS: Tabs[] = [
|
||||
{
|
||||
key: TAB_KEYS.OVERVIEW,
|
||||
@@ -216,7 +221,8 @@ export enum STORE_KEYS {
|
||||
EMAILS = 'emails',
|
||||
GEOLOCATIONS_ADDRESSES = 'geolocationsAndAddresses',
|
||||
SIMILAR_GEOLOCATIONS = 'similarGeolocations',
|
||||
SECONDARY_SOURCES = 'secondarySources'
|
||||
SECONDARY_SOURCES = 'secondarySources',
|
||||
PAUSE_STATUS = 'pause'
|
||||
}
|
||||
|
||||
const COSMOS_PATH = '/cosmos';
|
||||
|
||||
@@ -107,6 +107,11 @@ const CallBridge = (props: CallBridgeProps) => {
|
||||
(state: RootState) => state?.common?.blankDisposition?.disableBlankDisposition
|
||||
);
|
||||
|
||||
const caseSnoozed = useSelector(
|
||||
(state: RootState) =>
|
||||
state.caseDetail.pageData?.[createKey(loanId, customerId)]?.pause?.data?.isPaused
|
||||
);
|
||||
|
||||
const user = useSelector((state: RootState) => state?.common?.userData);
|
||||
const telephones = useSelector(
|
||||
(state: RootState) => state?.caseDetail?.pageData?.[createKey(loanId, customerId)]?.telephonesv2
|
||||
@@ -402,6 +407,13 @@ const CallBridge = (props: CallBridgeProps) => {
|
||||
});
|
||||
onStartCallClick(phone);
|
||||
};
|
||||
|
||||
const tooltipContent = caseSnoozed
|
||||
? 'Calling is disabled as collections is snoozed for this LAN'
|
||||
: `You can call the customer from ${formatToStandardTimeInHours(getCallingWindowStartTime)} ${
|
||||
isPostMidnight ? 'tomorrow' : 'today'
|
||||
}`;
|
||||
|
||||
return inlineCallButton ? (
|
||||
<CallIcon onClick={initiateCall} />
|
||||
) : (
|
||||
@@ -410,7 +422,9 @@ const CallBridge = (props: CallBridgeProps) => {
|
||||
<Tooltip placement="top">
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
disabled={disableButtons || disableCallButton || isCallingWindowDisabled}
|
||||
disabled={
|
||||
disableButtons || disableCallButton || isCallingWindowDisabled || caseSnoozed
|
||||
}
|
||||
onClick={() => {
|
||||
if (disableButtons) return;
|
||||
handleCallClick(feedback.telephone || '');
|
||||
@@ -421,13 +435,9 @@ const CallBridge = (props: CallBridgeProps) => {
|
||||
Call
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
{isCallingWindowDisabled && (
|
||||
<TooltipContent className="tooltipWrapper">
|
||||
{`You can call the customer from
|
||||
${formatToStandardTimeInHours(getCallingWindowStartTime)}
|
||||
${isPostMidnight ? 'tomorrow' : 'today'}`}
|
||||
</TooltipContent>
|
||||
)}
|
||||
{isCallingWindowDisabled || caseSnoozed ? (
|
||||
<TooltipContent className="tooltipWrapper">{tooltipContent}</TooltipContent>
|
||||
) : null}
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Typography variant="p5" className={styles.lightText}>
|
||||
|
||||
@@ -100,6 +100,12 @@ const FeedbackFrom = (props: FeedbackFromProps) => {
|
||||
const telephones = useSelector(
|
||||
(state: RootState) => state.caseDetail.pageData?.[createKey(loanId, customerId)]?.telephonesv2
|
||||
);
|
||||
|
||||
const snoozedCase = useSelector(
|
||||
(state: RootState) =>
|
||||
state.caseDetail.pageData?.[createKey(loanId, customerId)]?.pause?.data?.isPaused
|
||||
);
|
||||
|
||||
const user = useSelector((state: RootState) => state.common.userData);
|
||||
const ameyoCallDetails = useSelector((state: RootState) => state.common.ameyoCallDetails);
|
||||
const isTeamLead = useSelector((state: RootState) => state.common?.isTeamLead);
|
||||
@@ -616,6 +622,14 @@ const FeedbackFrom = (props: FeedbackFromProps) => {
|
||||
return false;
|
||||
};
|
||||
|
||||
const disableWhenSnoozed = (callingTypeValue: string) => {
|
||||
if (snoozedCase && callingTypeValue === ECallingType.SKIP_TRACING) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const isCallingTypeOptionDisable = (callingTypeValue: string) => {
|
||||
return (
|
||||
callingTypeValue === ECallingType.CALL_BRIDGE ||
|
||||
@@ -766,7 +780,9 @@ const FeedbackFrom = (props: FeedbackFromProps) => {
|
||||
disableInitialQuestions ||
|
||||
disableWhenLimitIsReached(callingType.value) ||
|
||||
disableWhenCallingWindowOver(callingType.value) ||
|
||||
disableWhenSnoozed(callingType.value) ||
|
||||
disableSelfCall(callingType.value)
|
||||
|
||||
}
|
||||
labelClasses={styles.text}
|
||||
onChipClick={() => {
|
||||
|
||||
@@ -218,6 +218,11 @@ export interface IEscalationStatus {
|
||||
totalEscalations: number;
|
||||
}
|
||||
|
||||
export enum CaseStatus {
|
||||
ON_HOLD = 'ON_HOLD',
|
||||
ACTIVE = 'ACTIVE'
|
||||
}
|
||||
|
||||
export interface CustomerDetailApiResponse {
|
||||
riskSegment: keyof typeof RiskRating;
|
||||
caseReferenceId: string;
|
||||
@@ -242,7 +247,7 @@ export interface CustomerDetailApiResponse {
|
||||
startDate: string;
|
||||
activationDate: string;
|
||||
closingDate?: unknown;
|
||||
status: string;
|
||||
status: CaseStatus;
|
||||
pendingEmiSchedules: PendingEmiSchedule[];
|
||||
partPrePayments: PartPrePayment[];
|
||||
repaymentHistory: Repayment[];
|
||||
@@ -552,6 +557,7 @@ export interface CaseDetailsPageData {
|
||||
emails: Email[];
|
||||
geolocationsAndAddresses: IAddressesGeolocations;
|
||||
similarGeolocations: ISimilarGeolocations;
|
||||
pause: IPause;
|
||||
}
|
||||
export interface PageData {
|
||||
[id: string]: CaseDetailsPageData | null;
|
||||
@@ -605,3 +611,10 @@ export interface ISimilarGeolocations extends IApiData {
|
||||
clusterId: string;
|
||||
timestamps: number[];
|
||||
}
|
||||
|
||||
export interface IPause extends IApiData {
|
||||
data: {
|
||||
isPaused: boolean;
|
||||
pauseTillDate: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import { setGlobalUserData } from '../constants/Global';
|
||||
import { Roles } from '../pages/auth/constants/AuthConstants';
|
||||
import {
|
||||
checkFieldTLAndClusterAndZonalManagers,
|
||||
isInternalTeamLead
|
||||
isInternalTeamLead,
|
||||
notHideTabsInCaseOfSnooze
|
||||
} from 'src/utils/RoleManagementHelper';
|
||||
import { getServerTime } from 'src/utils/DateHelper';
|
||||
import {
|
||||
@@ -355,6 +356,7 @@ export interface CommonState {
|
||||
slashCallStatusFromExtension: SlashCallStatus;
|
||||
agentBusinessVertical: string;
|
||||
callingWindow?: CallingWindow;
|
||||
shouldNotHidePauseCasesTab: boolean;
|
||||
}
|
||||
|
||||
export const CHECK_LOGIN = 'CHECK_LOGIN';
|
||||
@@ -424,7 +426,9 @@ const initialState = {
|
||||
callingWindow: {
|
||||
callingStartTime: '08:00:00',
|
||||
callingEndTime: '19:00:00'
|
||||
}
|
||||
},
|
||||
shouldNotHidePauseCasesTab: false,
|
||||
isTeamLead: false
|
||||
} as CommonState;
|
||||
|
||||
setGlobalUserData({ token: initialState.userData.token });
|
||||
@@ -467,6 +471,7 @@ export const commonSlice = createSlice({
|
||||
roles,
|
||||
isNaviUser
|
||||
);
|
||||
state.shouldNotHidePauseCasesTab = notHideTabsInCaseOfSnooze(roles, isNaviUser);
|
||||
}
|
||||
state.extensionVersion = action.payload.ameyoExtensionVersion;
|
||||
state.userData = { ...state.userData, ...action.payload };
|
||||
|
||||
@@ -886,6 +886,18 @@ export const CLICKSTREAM_EVENT_NAMES = Object.freeze({
|
||||
name: 'LH_UniversalCallSDK_CLICKSTREAM_PROCESSOR',
|
||||
description: 'Universal Call SDK Clickstream Processor'
|
||||
},
|
||||
LH_SNOOZE_EFFORTS_SUBMITTED: {
|
||||
name: 'LH_SNOOZE_EFFORTS_SUBMITTED',
|
||||
description: 'Snooze Efforts Submitted'
|
||||
},
|
||||
LH_SNOOZE_EFFORTS_UPDATED: {
|
||||
name: 'LH_SNOOZE_EFFORTS_UPDATED',
|
||||
description: 'Snooze Efforts Updated'
|
||||
},
|
||||
LH_SNOOZE_EFFORTS_Filter_CLICKED: {
|
||||
name: 'LH_SNOOZE_EFFORTS_Filter_CLICKED',
|
||||
description: 'Snooze Efforts Filter Clicked'
|
||||
},
|
||||
// Anomaly Tracker
|
||||
LH_ANOMALY_TRACKER_TAB_CHANGE: {
|
||||
name: 'LH_ANOMALY_TRACKER_TAB_CHANGE',
|
||||
@@ -922,7 +934,7 @@ export const CLICKSTREAM_EVENT_NAMES = Object.freeze({
|
||||
LH_ANOMALY_TRACKER_ANOMALY_RESOLVED_SUBMIT_BUTTON_CLICKED: {
|
||||
name: 'LH_ANOMALY_TRACKER_ANOMALY_RESOLVED_SUBMIT_BUTTON_CLICKED',
|
||||
description: 'Anomaly Tracker anomaly resolved'
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const CLICKSTREAM_EVENT_NAMES_FA = Object.freeze({
|
||||
|
||||
@@ -14,6 +14,13 @@ const fieldTLAndClusterAndZonalManagers = {
|
||||
[Roles.ROLE_NAVI_FIELD_TEAM_LEAD]: Roles.ROLE_NAVI_FIELD_TEAM_LEAD
|
||||
};
|
||||
|
||||
const rolesForHidePauseCasesTabCases = {
|
||||
[Roles.ROLE_COLLECTION_EFFORTS_PAUSE_ACCESS]: Roles.ROLE_COLLECTION_EFFORTS_PAUSE_ACCESS,
|
||||
[Roles.ROLE_GLOBAL_ACCESS]: Roles.ROLE_GLOBAL_ACCESS,
|
||||
[Roles.ROLE_COLLECTION_HEAD]: Roles.ROLE_COLLECTION_HEAD,
|
||||
[Roles.ROLE_USER_MANAGEMENT_ADMIN]: Roles.ROLE_USER_MANAGEMENT_ADMIN
|
||||
};
|
||||
|
||||
// Check if the user is a team lead
|
||||
// criteria: user has role USER_ROLE_TEAM_LEAD, USER_ROLE_INTERNAL_TEAM_LEAD and isNaviUser true
|
||||
export const isInternalTeamLead = (roles: Array<string>, isNaviUser: boolean): boolean => {
|
||||
@@ -44,3 +51,14 @@ export const checkFieldTLAndClusterAndZonalManagers = (
|
||||
}
|
||||
return isInternalTeamLead;
|
||||
};
|
||||
|
||||
export const notHideTabsInCaseOfSnooze = (roles: Array<string>, isNaviUser: boolean): boolean => {
|
||||
if (!roles.length) return false;
|
||||
let notHideTabsInCaseOfSnooze = false;
|
||||
for (let i = 0; i < roles.length; i++) {
|
||||
if (rolesForHidePauseCasesTabCases[roles[i] as keyof typeof rolesForHidePauseCasesTabCases]) {
|
||||
notHideTabsInCaseOfSnooze = true;
|
||||
}
|
||||
}
|
||||
return notHideTabsInCaseOfSnooze;
|
||||
};
|
||||
|
||||
@@ -79,6 +79,9 @@ module.exports = {
|
||||
'green-dark-1': 'var(--green-dark-1)',
|
||||
'green-dark-2': 'var(--green-dark-2)'
|
||||
},
|
||||
backgroundColor: {
|
||||
'red-background': 'var(--pale-red)'
|
||||
},
|
||||
boxShadow: {
|
||||
base: 'var(--box-shadow-4)',
|
||||
3: 'var(--box-shadow-3)',
|
||||
|
||||
Reference in New Issue
Block a user