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:
Aman Singh
2025-01-07 17:23:03 +05:30
committed by GitHub
parent 3b6951833c
commit 24d4fe0ae6
18 changed files with 279 additions and 54 deletions

View 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;

View File

@@ -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();

View File

@@ -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 || '',

View File

@@ -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}

View File

@@ -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,

View File

@@ -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 => {

View File

@@ -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'
}

View File

@@ -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;
}
}
}
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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';

View File

@@ -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}>

View File

@@ -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={() => {

View File

@@ -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;
};
}

View File

@@ -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 };

View File

@@ -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({

View File

@@ -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;
};

View File

@@ -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)',