NTP-28655| Pause Collection Efforts on Cosmos (#1066)

This commit is contained in:
Aishwarya Srivastava
2025-01-23 19:58:53 +05:30
committed by GitHub
parent 421dd9d242
commit ca2575c24d
24 changed files with 316 additions and 89 deletions

View File

@@ -13,7 +13,10 @@ import {
setUngroupedAddresses,
setUngroupedAddressesLoading,
} from '@reducers/ungroupedAddressesSlice';
import { setSkipTracingAddresses, setSkipTracingAddressesLoading } from '@reducers/skipTracingAddressesSlice';
import {
setSkipTracingAddresses,
setSkipTracingAddressesLoading,
} from '@reducers/skipTracingAddressesSlice';
import { VisitType } from '@screens/caseDetails/interface';
export const getAddressesGeolocation =
@@ -157,7 +160,7 @@ export const getSkipTracingAddress =
includeFeedbacks,
caseReferenceId: caseReferenceId,
caseBusinessVertical: caseBusinessVertical,
requestSourceTypeFilter: VisitType.SKIP_TRACING
requestSourceTypeFilter: VisitType.SKIP_TRACING,
}
);
axiosInstance

View File

@@ -1,14 +1,17 @@
import { COLORS } from '@rn-ui-lib/colors';
import { IconProps } from '@rn-ui-lib/icons/types';
import React from 'react';
import { Path, Svg } from 'react-native-svg';
const TagPlaceholderIcon = () => {
const TagPlaceholderIcon: React.FC<IconProps> = (props) => {
const { width = '70', height = '27', fillColor = COLORS.BACKGROUND.LIGHT_BLUE_1, backColor = COLORS.BACKGROUND.LIGHT_BLUE_4 } = props;
return (
<Svg width="70" height="27" viewBox="0 0 70 27" fill="none">
<Svg width={width} height={height} viewBox="0 0 70 27" fill="none">
<Path
d="M70 2C70 0.895431 69.1046 0 68 0H2C0.895432 0 0 0.895431 0 2V18C0 19.1046 0.895432 20 2 20H70V2Z"
fill="#D1E7FF"
fill={fillColor}
/>
<Path d="M66 20L66 26.5L70 20L66 20Z" fill="#A0CBFD" />
<Path d="M66 20L66 26.5L70 20L66 20Z" fill={backColor} />
</Svg>
);
};

View File

@@ -57,6 +57,8 @@ export enum PushNotificationTypes {
CUSTOMER_TRIED_CALLING_NOTIFICATION = 'CUSTOMER_TRIED_CALLING_NOTIFICATION',
ANOMALY_TRACKER_DETECTION='ANOMALY_TRACKER_DETECTION_V2',
ANOMALY_TRACKER_RESOLUTION='ANOMALY_TRACKER_RESOLUTION',
COLLECTION_PAUSE_EFFORTS_CASE_PAUSED='COLLECTION_PAUSE_EFFORTS_CASE_PAUSED',
CASE_UNPAUSED_NOTIFICATION_TEMPLATE = 'CASE_UNPAUSED_NOTIFICATION_TEMPLATE',
}
type NotificationContent = (notification: INotification) => {
@@ -456,6 +458,44 @@ const getAnomalyResolvedNotificationContent = (notification: INotification) => {
return { title, body, actions, data, defaultPressAction };
};
//Case paused notification content
const getCasePausedNotificationContent = (notification: INotification) => {
const { params } = notification || {};
const { customerName, date } = params || {};
const title = '<span style="color: #1C1C1C">Case paused</span>';
const body = `<span style="color: #969696">${customerName}'s case has been paused till ${date}</span>`;
const actions = [] as AndroidAction[];
const defaultPressAction = actionContentMap[NotificationAction.DEFAULT].pressAction;
const data = {
caseId: notification?.collectionCaseId || '',
templateId: notification?.template?.id,
templateName: PushNotificationTypes.COLLECTION_PAUSE_EFFORTS_CASE_PAUSED,
notificationId: notification?.id,
deepLinks: {},
}
return { title, body, actions, data, defaultPressAction };
};
//Case unpaused notification content
const getCaseUnPausedNotificationContent = (notification: INotification) => {
const { params } = notification || {};
const { customerName } = params || {};
const title = '<span style="color: #1C1C1C">Case Unpaused</span>';
const body = `<span style="color: #969696">${customerName}'s case has been unpaused</span>`;
const actions = [] as AndroidAction[];
const defaultPressAction = actionContentMap[NotificationAction.DEFAULT].pressAction;
const data = {
caseId: notification?.collectionCaseId || '',
templateId: notification?.template?.id,
templateName: PushNotificationTypes.CASE_UNPAUSED_NOTIFICATION_TEMPLATE,
notificationId: notification?.id,
deepLinks: {},
}
return { title, body, actions, data, defaultPressAction };
};
// Map of notification templates to notification content
export const notificationContentMap: Record<string, NotificationContent> = {
[PushNotificationTypes.PAYMENT_MADE_TEMPLATE_V2]: getPaymentMadeNotificationContent,
@@ -476,6 +516,8 @@ export const notificationContentMap: Record<string, NotificationContent> = {
[PushNotificationTypes.CUSTOMER_TRIED_CALLING_NOTIFICATION]: getCustomerTriedCallingNotificationContent,
[PushNotificationTypes.ANOMALY_TRACKER_DETECTION]: getAnomalyDetectedNotificationContent,
[PushNotificationTypes.ANOMALY_TRACKER_RESOLUTION]: getAnomalyResolvedNotificationContent,
[PushNotificationTypes.COLLECTION_PAUSE_EFFORTS_CASE_PAUSED]: getCasePausedNotificationContent,
[PushNotificationTypes.CASE_UNPAUSED_NOTIFICATION_TEMPLATE]: getCaseUnPausedNotificationContent,
};
const notificationDismissed = (notificationEvent: Event, isBgHandler = false) => {

View File

@@ -7,7 +7,12 @@ import { getCurrentScreen, navigateToScreen } from '../../../components/utlis/na
import { useAppDispatch, useAppSelector } from '../../../hooks';
import { setPinnedRank, setSelectedTodoListMap } from '../../../reducer/allCasesSlice';
import CaseItemAvatar from '../CaseItemAvatar';
import { CaseStatuses, ICaseItemAvatarCaseDetailObj, ICaseListItem } from '../interface';
import {
CaseStatuses,
CaseStatusUIMapping,
ICaseItemAvatarCaseDetailObj,
ICaseListItem,
} from '../interface';
import { COLORS } from '../../../../RN-UI-LIB/src/styles/colors';
import { CLICKSTREAM_EVENT_NAMES } from '../../../common/Constants';
import { addClickstreamEvent } from '../../../services/clickstreamEventService';
@@ -19,11 +24,11 @@ import { COMPLETED_STATUSES, ToastMessages } from '../constants';
import { VisitPlanStatus } from '../../../reducer/userSlice';
import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack';
import { PageRouteEnum } from '@screens/auth/ProtectedRouter';
import VisitPlanTag from './VisitPlanTag';
import CaseStatus from './CaseStatus';
import FeedbackStatus from './FeedbackStatus';
import CaseDetailKeyValue from './CaseDetailKeyValue';
import Escalation from '../Escalation/Escalation';
import CaseTag from './CaseTag';
const CaseListItem: React.FC<ICaseListItem> = (props) => {
const {
@@ -49,6 +54,7 @@ const CaseListItem: React.FC<ICaseListItem> = (props) => {
escalationData,
} = caseListItemDetailObj;
const isCasePaused = caseStatus === CaseStatuses.ON_HOLD;
const isVisitPlanStatusLocked = useAppSelector(
(state) => state.user?.lock?.visitPlanStatus === VisitPlanStatus.LOCKED
);
@@ -141,10 +147,10 @@ const CaseListItem: React.FC<ICaseListItem> = (props) => {
!isTodoItem &&
!isCompleted &&
!isTeamLead &&
!nearbyCaseView;
const showInVisitPlanTag = isCaseItemPinnedMainView && !caseCompleted;
!nearbyCaseView &&
!isCasePaused;
const showInVisitPlanTag = isCaseItemPinnedMainView && !caseCompleted && !isCasePaused;
return (
<Pressable
style={[
@@ -160,6 +166,7 @@ const CaseListItem: React.FC<ICaseListItem> = (props) => {
GenericStyles.centerAlignedRow,
GenericStyles.justifyStart,
isCaseSelected ? styles.backgroundBlue : styles.backgroundSilverLight,
isCasePaused ? styles.backgroundRed : null,
GenericStyles.w100,
GenericStyles.p12,
!totalEscalationsCount ? GenericStyles.brt8 : {},
@@ -185,8 +192,14 @@ const CaseListItem: React.FC<ICaseListItem> = (props) => {
</Pressable>
) : null}
{showInVisitPlanTag ? <VisitPlanTag totalEscalationsCount={totalEscalationsCount} /> : null}
{showInVisitPlanTag ? <CaseTag totalEscalationsCount={totalEscalationsCount} /> : null}
{isCasePaused ? (
<CaseTag
inputLabel={CaseStatusUIMapping.ON_HOLD}
isCasePaused={isCasePaused}
totalEscalationsCount={totalEscalationsCount}
/>
) : null}
<View style={GenericStyles.p12}>
<Text light ellipsizeMode="tail" style={[GenericStyles.fontSize13, GenericStyles.mb16]}>
{address ? address : 'Address not available'}
@@ -249,6 +262,9 @@ const styles = StyleSheet.create({
backgroundBlueLight: {
backgroundColor: COLORS.BACKGROUND.BLUE_LIGHT_3,
},
backgroundRed: {
backgroundColor: COLORS.BACKGROUND.RED,
},
});
export default memo(CaseListItem);

View File

@@ -6,12 +6,14 @@ import React from 'react';
import { StyleSheet } from 'react-native';
import { View } from 'react-native';
interface IVisitPlanTag {
interface ICaseTag {
totalEscalationsCount: number;
inputLabel?: string;
isCasePaused?: boolean;
}
const VisitPlanTag = (props: IVisitPlanTag) => {
const { totalEscalationsCount } = props;
const CaseTag = (props: ICaseTag) => {
const { totalEscalationsCount, inputLabel = 'In Visit plan', isCasePaused = false } = props;
return (
<View
style={[
@@ -20,8 +22,20 @@ const VisitPlanTag = (props: IVisitPlanTag) => {
totalEscalationsCount ? styles.top42 : {},
]}
>
<TagPlaceholderIcon />
<Text style={[GenericStyles.fontSize10, styles.visitPlanText]}>In visit plan</Text>
{isCasePaused ? (
<TagPlaceholderIcon fillColor={COLORS.TEXT.RED} width={64} backColor={COLORS.BACKGROUND.LIGHT_RED} />
) : (
<TagPlaceholderIcon />
)}
<Text
style={[
GenericStyles.fontSize10,
styles.visitPlanText,
isCasePaused && styles.casePausedText,
]}
>
{inputLabel}
</Text>
</View>
);
};
@@ -39,8 +53,12 @@ const styles = StyleSheet.create({
left: 9,
color: COLORS.TEXT.DARK,
fontWeight: '500',
lineHeight: 18
lineHeight: 18,
},
casePausedText: {
left: 16,
color: COLORS.TEXT.WHITE,
},
});
export default VisitPlanTag;
export default CaseTag;

View File

@@ -185,7 +185,6 @@ const ListItem: React.FC<IListItem> = (props) => {
const isCaseItemPinnedMainView = getCaseItemAvatarCaseDetailObj.isPinned && allCasesView;
const caseCompleted = COMPLETED_STATUSES.includes(caseStatus);
const showVisitPlanBtn =
!(caseCompleted || isCaseItemPinnedMainView) &&
!isTodoItem &&

View File

@@ -75,6 +75,7 @@ export enum CaseStatuses {
CLOSED = 'CLOSED', // if any of the task become verif success
FORCE_CLOSED = 'FORCE_CLOSED', // backend force closes the task
EXPIRED = 'EXPIRED', // unattended case for 30 days.
ON_HOLD='ON_HOLD', // case paused
}
export enum CaseStatusUIMapping {
@@ -85,6 +86,7 @@ export enum CaseStatusUIMapping {
CLOSED = 'Closed',
FORCE_CLOSED = 'Force closed',
EXPIRED = 'Expired',
ON_HOLD = 'Paused'
}
export enum CaseType {
@@ -365,6 +367,7 @@ export interface ICaseListItem {
isAgentDashboard?: boolean;
nearbyCaseView?: boolean;
isVisitPlan?: boolean;
isCasePaused?: boolean;
}
export interface ICaseDetailKeyValue {

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { Text, StyleSheet, View } from 'react-native';
import { COLORS } from '@rn-ui-lib/colors';
import { GenericStyles } from '@rn-ui-lib/styles';
import ErrorExclamationIcon from '@assets/icons/ErrorExclamationIcon';
interface CaseDetailPausedNudgeProps {
casePausedTillDate: string;
}
const CaseDetailPausedNudge: React.FC<CaseDetailPausedNudgeProps> = ({ casePausedTillDate }) => {
return (
<View style={[GenericStyles.row, GenericStyles.alignCenter, styles.header]}>
<ErrorExclamationIcon />
<View style={styles.textContainer}>
<Text style={styles.headerLabel}>
{`Case paused: No collection efforts should be made\ntill ${casePausedTillDate}`}
</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
textContainer: {},
header: {
backgroundColor: COLORS.BACKGROUND.RED,
height: 64,
paddingLeft: 20,
paddingRight: 40,
paddingTop: 12,
paddingBottom: 12,
width: '100%',
},
headerLabel: {
color: COLORS.BACKGROUND.DARK,
marginLeft: 16,
fontSize: 12,
fontWeight: '500',
lineHeight: 20,
},
});
export default CaseDetailPausedNudge;

View File

@@ -11,13 +11,15 @@ import { RootState } from '@store';
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { CaseDetailStackEnum } from './CaseDetailStack';
import { CaseStatuses } from '@screens/allCases/interface';
interface ICollectMoneySection {
caseId: string;
}
const CollectMoneySection = ({ caseId }: ICollectMoneySection) => {
const caseDetail = useAppSelector((state: RootState) => state.allCases?.caseDetails?.[caseId]) || {};
const caseDetail =
useAppSelector((state: RootState) => state.allCases?.caseDetails?.[caseId]) || {};
const { loanAccountNumber } = caseDetail;
const collectMoneyCta = () => {
@@ -28,7 +30,28 @@ const CollectMoneySection = ({ caseId }: ICollectMoneySection) => {
caseId,
});
};
const isCasePaused = caseDetail?.caseStatus === CaseStatuses.ON_HOLD;
if (isCasePaused)
return (
<View
style={[
GenericStyles.whiteBackground,
styles.secondSection,
getShadowStyle(2),
GenericStyles.mt16,
styles.paymentLinkTextContainer,
]}
>
<View style={styles.pausedCaseLeftButtonIcon}>
<Text style={[styles.collectLeftStyle, GenericStyles.row, GenericStyles.centerAligned]}>
</Text>
</View>
<Text style={[GenericStyles.fw500, GenericStyles.centerAligned, GenericStyles.ml4]}>
{'Contact your manager for payment link'}
</Text>
</View>
);
return (
<View
style={[
@@ -109,6 +132,23 @@ export const styles = StyleSheet.create({
justifyContent: 'flex-end',
paddingRight: 22,
},
pausedCaseLeftButtonIcon: {
backgroundColor: COLORS.TEXT.RED,
borderRadius: 100,
height: 20,
width: 20,
marginRight: 6,
display: 'flex',
alignContent: 'center',
justifyContent: 'center',
position: 'relative',
marginLeft: 8,
},
paymentLinkTextContainer: {
flexDirection: 'row',
alignItems: 'center',
padding: 16,
},
});
export default CollectMoneySection;

View File

@@ -23,6 +23,10 @@ import { syncActiveCallDetails } from '@actions/callRecordingActions';
import { getSkipTracingAddress, getUngroupedAddress } from '@actions/addressGeolocationAction';
import EscalationsSection from '@screens/escalations/EscalationsSection';
import TopFeedbacks from './feedback/TopFeedbacks';
import { CaseStatuses } from '@screens/allCases/interface';
import { BUSINESS_DATE_FORMAT, ISO_DATE_FORMAT } from '@rn-ui-lib/utils/dates';
import dayjs from 'dayjs';
import CaseDetailPausedNudge from './CaseDetailPausedNudge';
interface ICaseDetails {
route: {
@@ -43,6 +47,8 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
const isCallActive = useAppSelector(
(state: RootState) => state?.activeCall?.activeCallDetails?.callActive
);
const isCasePaused = caseDetail?.caseStatus === CaseStatuses.ON_HOLD;
const pausedTillDate = caseDetail?.pausedTillDate;
const caseBusinessVertical = useAppSelector(
(state) => state?.allCases?.caseDetails?.[caseId]?.businessVertical
);
@@ -55,7 +61,6 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
const isFocused = useIsFocused();
const [isDocumentsLoading, setIsDocumentsLoading] = React.useState(false);
const { escalationData } = caseDetail || {};
const activeEscalationCount = Number(escalationData?.activeEscalationCount);
const pastEscalationCount = Number(escalationData?.pastEscalationCount);
@@ -68,6 +73,7 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
};
}, [caseId]);
const casePausedTillDate = dayjs(pausedTillDate, ISO_DATE_FORMAT).format(BUSINESS_DATE_FORMAT);
useFocusEffect(
React.useCallback(() => {
if (loanAccountNumber) {
@@ -118,6 +124,7 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
<SafeAreaView style={[GenericStyles.fill, GenericStyles.whiteBackground]}>
<ScrollView style={styles.contentContainer} stickyHeaderIndices={[0]}>
<CaseDetailsHeader caseDetail={caseDetail} />
{isCasePaused ? <CaseDetailPausedNudge casePausedTillDate={casePausedTillDate} /> : null}
<UserDetailsSection caseDetail={caseDetail} isDocumentsLoading={isDocumentsLoading} />
<Animated.View
style={[

View File

@@ -7,7 +7,7 @@ import Button from '@rn-ui-lib/components/Button';
import ArrowSolidIcon from '@rn-ui-lib/icons/ArrowSolidIcon';
import { GenericStyles } from '@rn-ui-lib/styles';
import { MILLISECONDS_IN_A_MINUTE, _map } from '@rn-ui-lib/utils/common';
import { CaseAllocationType, TaskTitleUIMapping } from '@screens/allCases/interface';
import { CaseAllocationType, CaseStatuses, TaskTitleUIMapping } from '@screens/allCases/interface';
import { addClickstreamEvent } from '@services/clickstreamEventService';
import { RootState } from '@store';
import React, { useEffect } from 'react';
@@ -42,7 +42,7 @@ const CollectionCaseDetailFooter = ({ caseId, notificationId }: ICollectionCaseD
caseId,
});
};
const isCasePaused = caseDetail?.caseStatus === CaseStatuses.ON_HOLD;
const handleAddFeedback = () => {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_CASE_DETAILS_ADD_FEEDBACK_CLICKED, {
caseId: caseId,
@@ -111,9 +111,10 @@ const CollectionCaseDetailFooter = ({ caseId, notificationId }: ICollectionCaseD
variant="secondary"
onPress={handleCustomerCall}
testID={'test_call_customer'}
disabled={isCasePaused}
/>
<Button
style={[addingNewFeedbackDisabled ? styles.disabledButton : styles.feedbackButton]}
style={[(addingNewFeedbackDisabled && !isCasePaused) ? styles.disabledButton : styles.feedbackButton]}
title={preFilledFormData ? 'Continue feedback' : 'Add new feedback'}
rightIcon={
preFilledFormData ? (
@@ -127,6 +128,7 @@ const CollectionCaseDetailFooter = ({ caseId, notificationId }: ICollectionCaseD
testID="test_add_feedback"
pressableWidthChange={!addingNewFeedbackDisabled}
opacityChangeOnPress={!addingNewFeedbackDisabled}
disabled={isCasePaused}
/>
</View>
);

View File

@@ -4,6 +4,11 @@ import DocumentDataItem from './DocumentDataItem';
import SuspenseLoader from '@rn-ui-lib/components/suspense_loader/SuspenseLoader';
import LineLoader from '@rn-ui-lib/components/suspense_loader/LineLoader';
import { IDocumentItem } from './interface';
import { RootState } from '@store';
import { useAppSelector } from '@hooks';
import { CaseStatuses } from '@screens/allCases/interface';
import { NAVI_TAB_NAME } from './constants';
import { useMemo } from 'react';
interface IDocumentItems {
lan: string;
@@ -22,6 +27,16 @@ const DocumentItems: React.FC<IDocumentItems> = ({
isDocumentsLoading,
scrollByOffset,
}) => {
const caseDetail =
useAppSelector((state: RootState) => state.allCases?.caseDetails?.[caseId]) || {};
const isCasePaused = caseDetail?.caseStatus === CaseStatuses.ON_HOLD;
const updatedDocumentsList = useMemo(() => {
if (isCasePaused) {
return documentsList.filter((document) => document?.documentCategory === NAVI_TAB_NAME);
}
return documentsList;
}, [isCasePaused, documentsList]);
return (
<>
<View style={[GenericStyles.ml12, GenericStyles.mr8, GenericStyles.mt8]}>
@@ -40,7 +55,7 @@ const DocumentItems: React.FC<IDocumentItems> = ({
</>
}
>
{documentsList?.map((document: IDocumentItem, index: number) => {
{updatedDocumentsList?.map((document: IDocumentItem, index: number) => {
return selectedTab === document.documentCategory ? (
<DocumentDataItem
lan={lan}

View File

@@ -1,7 +1,7 @@
import CustomTabs from '@rn-ui-lib/components/customTabs/CustomTabs';
import { GenericStyles } from '@rn-ui-lib/styles';
import store from '@store';
import { useEffect, useState } from 'react';
import store, { RootState } from '@store';
import { useEffect, useMemo, useState } from 'react';
import DocumentItems from './DocumentItems';
import { IDocument, IDocumentItem, Tab } from './interface';
import { useAppSelector } from '@hooks';
@@ -9,8 +9,8 @@ import { ActivityIndicator, StyleSheet } from 'react-native';
import { COLORS } from '@rn-ui-lib/colors';
import { addClickstreamEvent } from '@services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
import { AGENT_TAB_KEY, AGENT_TAB_NAME } from './constants';
import { AGENT_TAB_KEY, AGENT_TAB_NAME, NAVI_TAB_NAME } from './constants';
import { CaseStatuses } from '@screens/allCases/interface';
interface IDocumentTabs {
lan: string;
@@ -19,16 +19,21 @@ interface IDocumentTabs {
scrollByOffset: (val: number) => void;
}
const DocumentTabs: React.FC<IDocumentTabs> = ({ lan, caseId, isDocumentsLoading, scrollByOffset }) => {
const DocumentTabs: React.FC<IDocumentTabs> = ({
lan,
caseId,
isDocumentsLoading,
scrollByOffset,
}) => {
const documentsData = useAppSelector((state) => state?.documentsSlice?.documentsData);
const data = documentsData?.[lan];
const agentsData = useAppSelector((state) => state?.documentsSlice?.agentDocumentsData);
const [documentTabs, setDocumentTabs] = useState<Tab[]>([]);
const [documentsList, setDocumentsList] = useState<IDocumentItem[]>([]);
const [selectedTab, setSelectedTab] = useState<string>(documentTabs?.[0]?.key || '');
const caseDetail =
useAppSelector((state: RootState) => state.allCases?.caseDetails?.[caseId]) || {};
useEffect(() => {
const docTabs: Tab[] = [];
const docsList: IDocumentItem[] = [];
@@ -63,48 +68,47 @@ const DocumentTabs: React.FC<IDocumentTabs> = ({ lan, caseId, isDocumentsLoading
}
setDocumentsList(docsList);
}, [data, agentsData]);
const isCasePaused = caseDetail?.caseStatus === CaseStatuses.ON_HOLD;
useEffect(() => {
setSelectedTab(documentTabs?.[0]?.key || '');
}, [documentTabs]);
const handleTabChange = (tabKey: string) => {
addClickstreamEvent(
CLICKSTREAM_EVENT_NAMES.FA_DOCUMENT_TAB_CLICKED,
{
lan: lan,
caseId: caseId,
tabName: tabKey,
}
);
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_DOCUMENT_TAB_CLICKED, {
lan: lan,
caseId: caseId,
tabName: tabKey,
});
setSelectedTab(tabKey);
};
return (
isDocumentsLoading ? (
<ActivityIndicator
size={'large'}
style={styles.loader}
color={COLORS.BACKGROUND.DARK_BLUE}
/>
) : (
<>
<CustomTabs
tabs={documentTabs}
currentTab={selectedTab}
onTabChange={handleTabChange}
containerStyle={[GenericStyles.ml20]}
/>
<DocumentItems
lan={lan}
caseId={caseId}
documentsList={documentsList}
selectedTab={selectedTab}
isDocumentsLoading={isDocumentsLoading}
scrollByOffset={scrollByOffset}
/>
</>
)
const updatedDocumentsTab = useMemo(() => {
if (isCasePaused) {
return documentTabs.filter((tab) => tab.key === NAVI_TAB_NAME);
}
return documentTabs;
}, [isCasePaused, documentTabs]);
return isDocumentsLoading ? (
<ActivityIndicator size={'large'} style={styles.loader} color={COLORS.BACKGROUND.DARK_BLUE} />
) : (
<>
<CustomTabs
tabs={updatedDocumentsTab}
currentTab={selectedTab}
onTabChange={handleTabChange}
containerStyle={[GenericStyles.ml20]}
/>
<DocumentItems
lan={lan}
caseId={caseId}
documentsList={documentsList}
selectedTab={selectedTab}
isDocumentsLoading={isDocumentsLoading}
scrollByOffset={scrollByOffset}
/>
</>
);
};
@@ -113,5 +117,5 @@ export default DocumentTabs;
const styles = StyleSheet.create({
loader: {
paddingVertical: 12,
}
});
},
});

View File

@@ -6,7 +6,7 @@ import { COLORS } from '@rn-ui-lib/colors';
import Text from '@rn-ui-lib/components/Text';
import { GenericStyles, getShadowStyle } from '@rn-ui-lib/styles';
import { _map } from '@rn-ui-lib/utils/common';
import { InteractionStatuses } from '@screens/allCases/interface';
import { CaseStatuses, InteractionStatuses } from '@screens/allCases/interface';
import { addClickstreamEvent } from '@services/clickstreamEventService';
import { RootState } from '@store';
import React from 'react';
@@ -59,6 +59,7 @@ const FeedbackDetailsSection = ({ caseId }: IFeedbackDetailsSection) => {
});
return notSyncedCases;
};
const isCasePaused = caseDetail?.caseStatus === CaseStatuses.ON_HOLD;
return (
<View style={[GenericStyles.pb24, GenericStyles.w100]}>
<View
@@ -74,7 +75,8 @@ const FeedbackDetailsSection = ({ caseId }: IFeedbackDetailsSection) => {
<Pressable
onTouchStart={touchStartHandler}
onPress={openAllFeedbacksHandler}
style={({ pressed }) => [{ opacity: pressed ? 0.7 : 1 }]}
style={({ pressed }) => [{ opacity: isCasePaused ? 0.5 : pressed ? 0.7 : 1 }]}
disabled={isCasePaused}
>
<Text style={[styles.textContainer, styles.feedbackBtn]}>View all</Text>
</Pressable>

View File

@@ -15,7 +15,7 @@ import { CaseDetail, DOCUMENT_TYPE } from './interface';
import { addClickstreamEvent } from '../../services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
import { navigateToScreen } from '../../components/utlis/navigationUtlis';
import { ICaseItemAvatarCaseDetailObj } from '../allCases/interface';
import { CaseStatuses, ICaseItemAvatarCaseDetailObj } from '../allCases/interface';
import CachedImage from '../../common/CachedImage';
import useFetchDocument from '../../hooks/useFetchDocument';
@@ -79,7 +79,7 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = (props) => {
const getImageURI = () => {
return findDocumentByDocumentType(getDocumentList(caseDetail), DOCUMENT_TYPE.SELFIE)?.uri || '';
};
const isCasePaused = caseDetail?.caseStatus === CaseStatuses.ON_HOLD;
const { setRetryForUnsignedDocuments } = useFetchDocument(
{
caseId: caseDetail?.id,
@@ -100,7 +100,7 @@ const UserDetailsSection: React.FC<IUserDetailsSection> = (props) => {
};
return (
<View style={styles.contentContainer}>
<View style={[styles.contentContainer, isCasePaused? GenericStyles.pt16: null]}>
<CachedImage
highQualityUri={getImageURI()}
cacheFileKey={caseDetail.id}

View File

@@ -12,6 +12,7 @@ import React from 'react';
import { StyleSheet, View } from 'react-native';
import { CaseDetailStackEnum } from './CaseDetailStack';
import { AddressGeolocationTabEnum, AddressTabType } from '@screens/addressGeolocation/constant';
import { CaseStatuses } from '@screens/allCases/interface';
interface IViewAddressSection {
caseId: string;
@@ -27,7 +28,7 @@ const ViewAddressSection = ({ caseId }: IViewAddressSection) => {
}
return AddressGeolocationTabEnum.ADDRESS;
};
const isCasePaused = caseDetail?.caseStatus === CaseStatuses.ON_HOLD;
const viewAllAddressHandler = () => {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_VIEW_ADDRESSES_CLICKED, {
lan: getLoanAccountNumber(caseDetail),
@@ -67,6 +68,7 @@ const ViewAddressSection = ({ caseId }: IViewAddressSection) => {
underlayColor="transparent"
pressableWidthChange={false}
opacityChangeOnPress={true}
disabled={isCasePaused}
/>
</View>
);

View File

@@ -49,4 +49,5 @@ export enum NAVI_ACCOUNT_SHARE_CHANNELS{
}
export const AGENT_TAB_KEY = 'AGENT';
export const AGENT_TAB_NAME = 'Agent';
export const AGENT_TAB_NAME = 'Agent';
export const NAVI_TAB_NAME = 'NAVI';

View File

@@ -22,6 +22,8 @@ import { feedbackTypeIcon } from '@screens/caseDetails/feedback/FeedbackDetailIt
import Tag, { TagVariant } from '@rn-ui-lib/components/Tag';
import { feedbackStatusColorMapping } from '@screens/allCases/utils';
import { PAST_FEEDBACK_PAGE_SIZE } from '@screens/caseDetails/feedback/pastFeedbackCommon';
import { CaseStatuses } from '@screens/allCases/interface';
import store from '@store';
interface IFeedbackListItem {
feedbackItem: IFeedback | IUnSyncedFeedbackItem;
@@ -58,7 +60,8 @@ const FeedbackListItem: React.FC<IFeedbackListItem> = ({
caseDetails: state?.allCases?.caseDetails?.[caseId] || null,
}));
const throttledSendToWhatsapp = React.useRef(debounce(shareToWhatsapp, 500));
const caseDetail = store?.getState()?.allCases?.caseDetails?.[caseId] || {};
const isCasePaused = caseDetail?.caseStatus === CaseStatuses.ON_HOLD;
return (
<View style={[!isTopFeedbackItem ? GenericStyles.ph16 : {}]}>
<TouchableOpacity
@@ -68,6 +71,7 @@ const FeedbackListItem: React.FC<IFeedbackListItem> = ({
GenericStyles.pv16,
isTopFeedbackItem ? styles.topFeedbackItem : styles.feedbackItem,
]}
disabled={isCasePaused}
>
{isTopFeedbackItem ? (
<View style={[GenericStyles.absolute]}>
@@ -124,13 +128,13 @@ const FeedbackListItem: React.FC<IFeedbackListItem> = ({
</Text>
) : null}
</View>
{!(feedbackItem.isSynced === false || !(feedbackItem as IFeedback)?.referenceId) ? (
{!(feedbackItem.isSynced === false || !(feedbackItem as IFeedback)?.referenceId) && !isCasePaused ? (
<View style={GenericStyles.alignCenter}>
<RightChevronIcon />
</View>
) : null}
</View>
{feedbackItem?.type === FEEDBACK_TYPE.FIELD_VISIT && !isTopFeedbackItem ? (
{feedbackItem?.type === FEEDBACK_TYPE.FIELD_VISIT && !isTopFeedbackItem && !isCasePaused ? (
<TouchableOpacity
style={styles.ShareButton}
hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }}
@@ -144,7 +148,7 @@ const FeedbackListItem: React.FC<IFeedbackListItem> = ({
setIsWhatsappSendLoading
);
}}
disabled={isWhastappSendLoading}
disabled={isWhastappSendLoading || isCasePaused}
>
<IconLabel
text="Share"

View File

@@ -327,6 +327,7 @@ export interface CaseDetail {
escalationData ?: EscalationData;
daysTillDeallocation: number;
businessVertical: string;
pausedTillDate?: string;
}
export interface recentEscalationDetails {

View File

@@ -24,12 +24,7 @@ interface UpdateCasesProps {
}
export const updateCases = (props: UpdateCasesProps) => (dispatch: AppDispatch) => {
const {
selectedAgent,
isVisitPlanLocked = false,
querySnapshot,
caseUpdateList,
} = props;
const { selectedAgent, isVisitPlanLocked = false, querySnapshot, caseUpdateList } = props;
const caseDetails = store?.getState()?.allCases?.caseDetails || {};
const casesList = store?.getState()?.allCases?.casesList || [];
const loading = store?.getState()?.allCases?.loading || false;

View File

@@ -63,6 +63,7 @@ export interface INotification {
callTime?: number;
anomalyId?: number;
anomalySubType?: string;
pauseTillDate?: string;
};
template: {
id: number;

View File

@@ -45,6 +45,7 @@ const NotificationTemplate: React.FC<INotificationTemplateProps> = ({ data }) =>
todaysPtpCustomerCount,
callTime,
anomalySubType,
pauseTillDate
} = params || {};
switch (templateName) {
@@ -396,6 +397,26 @@ const NotificationTemplate: React.FC<INotificationTemplateProps> = ({ data }) =>
<Text light>{anomalySubType?.replace(/\[.*?\]/g, '')}</Text>
</View>
);
case NotificationTypes.COLLECTION_PAUSE_EFFORTS_CASE_PAUSED:
return (
<Text>
<Text light>case has been </Text>
<Text bold dark>
paused
</Text>
<Text light> till </Text>
{pauseTillDate}
</Text>
);
case NotificationTypes.CASE_UNPAUSED_NOTIFICATION_TEMPLATE:
return (
<Text>
<Text light>case has been </Text>
<Text bold dark>
unpaused
</Text>
</Text>
);
default:
return <Text>New notification </Text>;
}

View File

@@ -68,6 +68,8 @@ export enum NotificationTypes {
CASE_ESCALATION_NOTIFICATION_TEMPLATE = 'CASE_ESCALATION_NOTIFICATION_TEMPLATE',
ANOMALY_TRACKER_DETECTION = 'ANOMALY_TRACKER_DETECTION_V2',
ANOMALY_TRACKER_RESOLUTION = 'ANOMALY_TRACKER_RESOLUTION',
COLLECTION_PAUSE_EFFORTS_CASE_PAUSED = 'COLLECTION_PAUSE_EFFORTS_CASE_PAUSED',
CASE_UNPAUSED_NOTIFICATION_TEMPLATE = 'CASE_UNPAUSED_NOTIFICATION_TEMPLATE'
}
export const NotificationIconsMap = {
@@ -131,6 +133,8 @@ export const NotificationIconsMap = {
strokeColor={COLORS.BORDER.DARK_GREEN_1}
/>
),
[NotificationTypes.CASE_UNPAUSED_NOTIFICATION_TEMPLATE]: <PaymentSuccessIcon />,
[NotificationTypes.COLLECTION_PAUSE_EFFORTS_CASE_PAUSED]: <PaymentFailedIcon />,
};
export enum WidgetStatus {