NTP-6736 | integration done
This commit is contained in:
@@ -105,6 +105,7 @@ export enum ApiKeys {
|
||||
DOWNLOAD_LATEST_APP = 'DOWNLOAD_LATEST_APP',
|
||||
GET_SIGNED_URL_V2 = 'GET_SIGNED_URL_V2',
|
||||
GET_SIGNED_URL_FOR_REPORTEE_V2 = 'GET_SIGNED_URL_FOR_REPORTEE_V2',
|
||||
ALL_ESCALATIONS = 'ALL_ESCALATIONS',
|
||||
}
|
||||
|
||||
export const API_URLS: Record<ApiKeys, string> = {} as Record<ApiKeys, string>;
|
||||
@@ -200,6 +201,7 @@ API_URLS[ApiKeys.FETCH_AGENT_DOCUMENTS] = '/documents/agent';
|
||||
API_URLS[ApiKeys.FETCH_DOCUMENT_SPECIFIC_LANGUAGE] = '/documents/language/{loanAccountNumber}';
|
||||
API_URLS[ApiKeys.SEND_COMMUNICATION_NAVI_ACCOUNT] = '/navi-communications/{loanAccountNumber}';
|
||||
API_URLS[ApiKeys.GENERATE_DYNAMIC_DOCUMENT] = '/documents/generate/{loanAccountNumber}';
|
||||
API_URLS[ApiKeys.ALL_ESCALATIONS] = '/customer-escalation';
|
||||
API_URLS[ApiKeys.DOWNLOAD_LATEST_APP] = 'https://longhorn.navi.com/api/app/download';
|
||||
|
||||
export const API_STATUS_CODE = {
|
||||
|
||||
47
src/reducer/escalationSlice.ts
Normal file
47
src/reducer/escalationSlice.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
interface IEscalationsSlice {
|
||||
escalationData : IAllEscalationData[];
|
||||
isLoading : boolean;
|
||||
pageData :pageData;
|
||||
}
|
||||
|
||||
interface pageData {
|
||||
totalPages : number;
|
||||
totalElements : number;
|
||||
}
|
||||
|
||||
|
||||
interface IAllEscalationData {
|
||||
createdAt : string;
|
||||
loanAccountNumber :string;
|
||||
status : string;
|
||||
title : string;
|
||||
voc : string;
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
escalationData : [],
|
||||
isLoading : false,
|
||||
pageData: {} as pageData
|
||||
} as IEscalationsSlice;
|
||||
|
||||
const escalationsSlice = createSlice({
|
||||
name: 'escalations',
|
||||
initialState,
|
||||
reducers: {
|
||||
setEscalationData: (state, action) => {
|
||||
state.escalationData = action.payload;
|
||||
},
|
||||
setIsLoading: (state, action) => {
|
||||
state.isLoading = action.payload;
|
||||
},
|
||||
setPageData: (state, action) => {
|
||||
state.pageData = action.payload;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const { setEscalationData, setIsLoading, setPageData } = escalationsSlice.actions;
|
||||
|
||||
export default escalationsSlice.reducer;
|
||||
@@ -84,6 +84,7 @@ const ListItem: React.FC<IListItem> = (props) => {
|
||||
interactionStatus,
|
||||
totalOverdueAmount,
|
||||
distanceInKm,
|
||||
escalationData,
|
||||
} = caseListItemDetailObj;
|
||||
|
||||
const isVisitPlanStatusLocked = useAppSelector(
|
||||
@@ -206,20 +207,17 @@ const ListItem: React.FC<IListItem> = (props) => {
|
||||
: TAG_CONTAINER_WIDTH.DEFAULT,
|
||||
};
|
||||
|
||||
const isEscalationCase = true;
|
||||
const isActiveEscalationCase = true;
|
||||
const isPastEscalationCase = false;
|
||||
const escalationCount = 4;
|
||||
const activeEscalationCaseCount = 2;
|
||||
const pastEscalationCaseCount = 3;
|
||||
const totalEscalationCaseCount = activeEscalationCaseCount + pastEscalationCaseCount;
|
||||
const isEscalationCase = escalationData !== undefined;
|
||||
const isActiveEscalationCase = Number(escalationData?.activeEscalationCount) > 0;
|
||||
const activeEscalationCount = Number(escalationData?.activeEscalationCount);
|
||||
const pastEscalationCount = Number(escalationData?.pastEscalationCount);
|
||||
|
||||
return (
|
||||
<Pressable onPress={handleCaseClick}>
|
||||
|
||||
<View>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
styles.listItem,
|
||||
getShadowStyle(2),
|
||||
{
|
||||
@@ -229,107 +227,120 @@ const ListItem: React.FC<IListItem> = (props) => {
|
||||
? COLORS.BACKGROUND.SILVER
|
||||
: COLORS.BACKGROUND.PRIMARY,
|
||||
},
|
||||
|
||||
]}
|
||||
>
|
||||
{isEscalationCase ? (
|
||||
<View style={[styles.escalationContainer, GenericStyles.row, GenericStyles.alignCenter, GenericStyles.w100, {
|
||||
backgroundColor: isActiveEscalationCase ? COLORS.BACKGROUND.RED : COLORS.BACKGROUND.YELLOW_LIGHT
|
||||
}]}>
|
||||
<FlagIcon fillColor={isActiveEscalationCase ? '#FF0000' : '#FFB800'} />
|
||||
<Text small style={[isActiveEscalationCase ? styles.activeEscalationText : styles.pastEscalationText]}>
|
||||
{`${totalEscalationCaseCount} `}{isActiveEscalationCase ? 'Active Escalations' : 'Past Escalations'}
|
||||
</Text>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
|
||||
<CaseItemAvatar
|
||||
caseDetailObj={getCaseItemAvatarCaseDetailObj}
|
||||
shouldBatchAvatar={shouldBatchAvatar}
|
||||
/>
|
||||
{showVisitPlanBtn ? (
|
||||
<Pressable onPress={handleAvatarClick} style={styles.selectBtn}>
|
||||
<RoundCheckIcon focused={isCaseSelected} />
|
||||
</Pressable>
|
||||
) : null}
|
||||
{showInVisitPlanTag && (
|
||||
<View style={[GenericStyles.absolute, styles.visitPlanContainer]}>
|
||||
<Text style={[GenericStyles.fontSize12, styles.visitPlanText]}>In visit plan</Text>
|
||||
</View>
|
||||
)}
|
||||
{nearbyCaseView && distanceInKm && (
|
||||
<View style={[GenericStyles.absolute, styles.distanceContainer]}>
|
||||
<Text style={[GenericStyles.fontSize12, styles.distanceText]}>
|
||||
{relativeDistanceFormatter(distanceInKm)} km away
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={[styles.caseItemInfo]}>
|
||||
<View style={[styles.tagContainer, widthStyle]}>
|
||||
{paymentStatus ? (
|
||||
<View style={[GenericStyles.mr8, GenericStyles.mb8]}>
|
||||
<Tag
|
||||
variant={paymentStatusMapping[paymentStatus]?.variant || TagVariant.alert}
|
||||
text={(paymentStatusMapping[paymentStatus]?.label || paymentStatus) as string}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
{collectionTag ? (
|
||||
<View style={[GenericStyles.mr8]}>
|
||||
<Tag variant={TagVariant.gray} text={collectionTag} />
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{!isVisitPlan && distanceOfCaseItem ? (
|
||||
<View style={GenericStyles.mb4}>
|
||||
<Tag
|
||||
tagIcon={
|
||||
<LocationDistanceIcon
|
||||
iconColor={isNearestCaseView ? COLORS.TEXT.BLUE_DARK_2 : COLORS.TEXT.GREY_3}
|
||||
backgroundColor={
|
||||
isNearestCaseView ? COLORS.BACKGROUND.BLUE : COLORS.BACKGROUND.BLUE_LIGHT_3
|
||||
}
|
||||
/>
|
||||
}
|
||||
text={Number(distanceOfCaseItem?.toFixed(1)) + ' KM'}
|
||||
variant={isNearestCaseView ? TagVariant.darkBlue : TagVariant.darkGray}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
<Heading numberOfLines={1} type={'h5'} bold dark>
|
||||
{customerName}
|
||||
</Heading>
|
||||
{taskTitle ? (
|
||||
<Text dark bold ellipsizeMode="tail" style={styles.address}>
|
||||
<Text light>
|
||||
{/* @ts-ignore */}
|
||||
<Text>{TaskTitleUIMapping[taskTitle]}</Text>
|
||||
{displayAddress ? `: ${displayAddress}` : null}
|
||||
isActiveEscalationCase ? (
|
||||
<View style={[styles.escalationContainer, GenericStyles.row, GenericStyles.alignCenter, GenericStyles.w100, {
|
||||
backgroundColor: COLORS.BACKGROUND.RED
|
||||
}]}>
|
||||
<FlagIcon fillColor='#FF0000' />
|
||||
<Text small style={styles.activeEscalationText}>
|
||||
{`${activeEscalationCount} Active Escalation${activeEscalationCount === 1 ? '' : 's'}`}
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
<Text dark bold ellipsizeMode="tail" style={styles.address}>
|
||||
<Text light>{displayAddress}</Text>
|
||||
</Text>
|
||||
<View style={[styles.escalationContainer, GenericStyles.row, GenericStyles.alignCenter, GenericStyles.w100, {
|
||||
backgroundColor: COLORS.BACKGROUND.YELLOW_LIGHT
|
||||
}]}>
|
||||
<FlagIcon fillColor='#FFB800' />
|
||||
<Text small style={styles.pastEscalationText}>
|
||||
{`${pastEscalationCount} Past Escalation${pastEscalationCount === 1 ? '' : 's'}`}
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
) : null}
|
||||
|
||||
<View style={[GenericStyles.row, GenericStyles.p12]}>
|
||||
<CaseItemAvatar
|
||||
caseDetailObj={getCaseItemAvatarCaseDetailObj}
|
||||
shouldBatchAvatar={shouldBatchAvatar}
|
||||
/>
|
||||
{showVisitPlanBtn ? (
|
||||
<Pressable onPress={handleAvatarClick} style={styles.selectBtn}>
|
||||
<RoundCheckIcon focused={isCaseSelected} />
|
||||
</Pressable>
|
||||
) : null}
|
||||
{showInVisitPlanTag && (
|
||||
<View style={[GenericStyles.absolute, styles.visitPlanContainer]}>
|
||||
<Text style={[GenericStyles.fontSize12, styles.visitPlanText]}>In visit plan</Text>
|
||||
</View>
|
||||
)}
|
||||
<View>
|
||||
{is1To30FieldAgent ? (
|
||||
<Text small style={[styles.caseStatusText, styles.borderTop]} bold>
|
||||
Total due {formatAmount(totalOverdueAmount, false)}
|
||||
{' '}DPD Cycle {dpdCycle}
|
||||
{nearbyCaseView && distanceInKm && (
|
||||
<View style={[GenericStyles.absolute, styles.distanceContainer]}>
|
||||
<Text style={[GenericStyles.fontSize12, styles.distanceText]}>
|
||||
{relativeDistanceFormatter(distanceInKm)} km away
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={[styles.caseItemInfo]}>
|
||||
<View style={[styles.tagContainer, widthStyle]}>
|
||||
{paymentStatus ? (
|
||||
<View style={[GenericStyles.mr8, GenericStyles.mb8]}>
|
||||
<Tag
|
||||
variant={paymentStatusMapping[paymentStatus]?.variant || TagVariant.alert}
|
||||
text={(paymentStatusMapping[paymentStatus]?.label || paymentStatus) as string}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
{collectionTag ? (
|
||||
<View style={[GenericStyles.mr8]}>
|
||||
<Tag variant={TagVariant.gray} text={collectionTag} />
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{!isVisitPlan && distanceOfCaseItem ? (
|
||||
<View style={GenericStyles.mb4}>
|
||||
<Tag
|
||||
tagIcon={
|
||||
<LocationDistanceIcon
|
||||
iconColor={isNearestCaseView ? COLORS.TEXT.BLUE_DARK_2 : COLORS.TEXT.GREY_3}
|
||||
backgroundColor={
|
||||
isNearestCaseView ? COLORS.BACKGROUND.BLUE : COLORS.BACKGROUND.BLUE_LIGHT_3
|
||||
}
|
||||
/>
|
||||
}
|
||||
text={Number(distanceOfCaseItem?.toFixed(1)) + ' KM'}
|
||||
variant={isNearestCaseView ? TagVariant.darkBlue : TagVariant.darkGray}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
<Heading numberOfLines={1} type={'h5'} bold dark>
|
||||
{customerName}
|
||||
</Heading>
|
||||
{taskTitle ? (
|
||||
<Text dark bold ellipsizeMode="tail" style={styles.address}>
|
||||
<Text light>
|
||||
{/* @ts-ignore */}
|
||||
<Text>{TaskTitleUIMapping[taskTitle]}</Text>
|
||||
{displayAddress ? `: ${displayAddress}` : null}
|
||||
</Text>
|
||||
</Text>
|
||||
) : (
|
||||
<Text small style={[styles.caseStatusText, styles.borderTop]} bold>
|
||||
Total due {formatAmount(totalOverdueAmount, false)}
|
||||
{' '}DPD bucket {dpdBucket}
|
||||
<Text dark bold ellipsizeMode="tail" style={styles.address}>
|
||||
<Text light>{displayAddress}</Text>
|
||||
</Text>
|
||||
)}
|
||||
{caseInteractionStatus ? (
|
||||
<Text small style={styles.caseStatusText} bold>
|
||||
{caseInteractionStatus}
|
||||
</Text>
|
||||
) : null}
|
||||
<View>
|
||||
{is1To30FieldAgent ? (
|
||||
<Text small style={[styles.caseStatusText, styles.borderTop]} bold>
|
||||
Total due {formatAmount(totalOverdueAmount, false)}
|
||||
{' '}DPD Cycle {dpdCycle}
|
||||
</Text>
|
||||
) : (
|
||||
<Text small style={[styles.caseStatusText, styles.borderTop]} bold>
|
||||
Total due {formatAmount(totalOverdueAmount, false)}
|
||||
{' '}DPD bucket {dpdBucket}
|
||||
</Text>
|
||||
)}
|
||||
{caseInteractionStatus ? (
|
||||
<Text small style={styles.caseStatusText} bold>
|
||||
{caseInteractionStatus}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -339,17 +350,15 @@ const ListItem: React.FC<IListItem> = (props) => {
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
listItem: {
|
||||
padding: 12,
|
||||
borderRadius: 8,
|
||||
marginVertical: 6,
|
||||
marginBottom: 12,
|
||||
position: 'relative',
|
||||
overflow: 'hidden'
|
||||
},
|
||||
escalationContainer: {
|
||||
position: 'absolute',
|
||||
height: 26,
|
||||
paddingLeft: 8,
|
||||
paddingRight: 8,
|
||||
borderBottomRightRadius: 6,
|
||||
borderTopLeftRadius: 8,
|
||||
},
|
||||
activeEscalationText: {
|
||||
|
||||
@@ -344,3 +344,14 @@ export enum FiltePlaceholderText {
|
||||
CASES = 'cases',
|
||||
MY_CASES = 'my cases',
|
||||
}
|
||||
|
||||
export interface IRecentEscalationDetails {
|
||||
customerVoice: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface IEscalationSummary {
|
||||
pastEscalationCount: number;
|
||||
activeEscalationCount: number;
|
||||
recentEscalationDetails: IRecentEscalationDetails;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
|
||||
},
|
||||
} = props;
|
||||
const caseDetail = useAppSelector((state: RootState) => state.allCases.caseDetails[caseId]) || {};
|
||||
const isActiveEscalation = caseDetail.escalationData !== undefined;
|
||||
const isCallActive = useAppSelector(
|
||||
(state: RootState) => state?.activeCall?.activeCallDetails?.callActive
|
||||
);
|
||||
@@ -127,7 +128,7 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
|
||||
>
|
||||
<ViewAddressSection caseId={caseId} />
|
||||
<EmiDetailsSection caseId={caseId} />
|
||||
<EscalationsSection caseId={caseId} />
|
||||
{isActiveEscalation ? (<EscalationsSection caseId={caseId} />): null}
|
||||
<CollectMoneySection caseId={caseId} />
|
||||
<FeedbackDetailsSection caseId={caseId} />
|
||||
</Animated.View>
|
||||
|
||||
@@ -315,6 +315,18 @@ export interface CaseDetail {
|
||||
employmentDetail?: EmploymentDetails;
|
||||
unpaidDays?: number;
|
||||
addressStringType?: string;
|
||||
escalationData ?: escalationData;
|
||||
}
|
||||
|
||||
export interface recentEscalationDetails {
|
||||
createdAt : string;
|
||||
customerVoice : string;
|
||||
}
|
||||
|
||||
export interface escalationData {
|
||||
activeEscalationCount : number;
|
||||
pastEscalationCount : number;
|
||||
recentEscalationDetails : recentEscalationDetails;
|
||||
}
|
||||
|
||||
export interface AddressesGeolocationPayload {
|
||||
|
||||
@@ -6,188 +6,49 @@ import useIsOnline from '../../hooks/useIsOnline';
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import { goBack } from '../../components/utlis/navigationUtlis';
|
||||
import { toast } from '../../../RN-UI-LIB/src/components/toast';
|
||||
import { GenericStyles, SCREEN_HEIGHT, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
|
||||
import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
|
||||
import NavigationHeader from '../../../RN-UI-LIB/src/components/NavigationHeader';
|
||||
import SuspenseLoader from '../../../RN-UI-LIB/src/components/suspense_loader/SuspenseLoader';
|
||||
import LineLoader from '../../../RN-UI-LIB/src/components/suspense_loader/LineLoader';
|
||||
import Text from '../../../RN-UI-LIB/src/components/Text';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import Pagination from '../../../RN-UI-LIB/src/components/pagination/Pagination';
|
||||
import { GenericObject } from '@common/GenericTypes';
|
||||
import FlagIcon from '@assets/icons/FlagIcon';
|
||||
import Tag, { TagVariant } from '@rn-ui-lib/components/Tag';
|
||||
import { useAppDispatch, useAppSelector } from '@hooks';
|
||||
import { RootState } from '@store';
|
||||
import { getAllEscalations } from './actions';
|
||||
import dayjs from 'dayjs';
|
||||
interface IEscalationsDetails {
|
||||
route: {
|
||||
params: {
|
||||
caseId: string;
|
||||
loanAccountNumber: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const Escalations = () => {
|
||||
const Escalations: React.FC<IEscalationsDetails> = (props) => {
|
||||
|
||||
const [escalationsList, setEscalationsList] = useState<GenericObject[]>([]);
|
||||
const [totalPage, setTotalPage] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const {
|
||||
route: {
|
||||
params: { caseId, loanAccountNumber },
|
||||
},
|
||||
} = props;
|
||||
|
||||
const {escalationData, isLoading, pageData} = useAppSelector((state: RootState) => state.escalationSlice);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
const isOnline = useIsOnline();
|
||||
|
||||
const getAllEscalations = useCallback(() => {
|
||||
setTotalPage(escalationsList.length)
|
||||
}, [currentPage]);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
setEscalationsList([{
|
||||
id: 1,
|
||||
title: 'Physical assault/threatening to physically assault',
|
||||
raisedOn: '9 May, 2024',
|
||||
time: '2:29 PM',
|
||||
status: 'Closed',
|
||||
description: 'Extension of time for paying dues.',
|
||||
isActiveEscalation: false, // Closed = not active
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Miscommunication about loan terms',
|
||||
raisedOn: '15 April, 2024',
|
||||
time: '10:15 AM',
|
||||
status: 'In Progress',
|
||||
description: 'Clarification required regarding interest rates.',
|
||||
isActiveEscalation: true, // In Progress = active
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Unauthorized access to account',
|
||||
raisedOn: '28 March, 2024',
|
||||
time: '4:45 PM',
|
||||
status: 'Open',
|
||||
description: 'Suspicious activity detected in the bank account.',
|
||||
isActiveEscalation: true, // Open = active
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Delayed processing of payment',
|
||||
raisedOn: '5 March, 2024',
|
||||
time: '1:00 PM',
|
||||
status: 'Closed',
|
||||
description: 'Payment was not processed on time.',
|
||||
isActiveEscalation: false, // Closed = not active
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Incorrect charges applied',
|
||||
raisedOn: '20 February, 2024',
|
||||
time: '9:30 AM',
|
||||
status: 'Resolved',
|
||||
description: 'Extra charges applied without notice.',
|
||||
isActiveEscalation: false, // Resolved = not active
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Unauthorized access to account',
|
||||
raisedOn: '28 March, 2024',
|
||||
time: '4:45 PM',
|
||||
status: 'Open',
|
||||
description: 'Suspicious activity detected in the bank account.',
|
||||
isActiveEscalation: true, // Open = active
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Delayed processing of payment',
|
||||
raisedOn: '5 March, 2024',
|
||||
time: '1:00 PM',
|
||||
status: 'Closed',
|
||||
description: 'Payment was not processed on time.',
|
||||
isActiveEscalation: false, // Closed = not active
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Incorrect charges applied',
|
||||
raisedOn: '20 February, 2024',
|
||||
time: '9:30 AM',
|
||||
status: 'Resolved',
|
||||
description: 'Extra charges applied without notice.',
|
||||
isActiveEscalation: false, // Resolved = not active
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Unauthorized access to account',
|
||||
raisedOn: '28 March, 2024',
|
||||
time: '4:45 PM',
|
||||
status: 'Open',
|
||||
description: 'Suspicious activity detected in the bank account.',
|
||||
isActiveEscalation: true, // Open = active
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Delayed processing of payment',
|
||||
raisedOn: '5 March, 2024',
|
||||
time: '1:00 PM',
|
||||
status: 'Closed',
|
||||
description: 'Payment was not processed on time.',
|
||||
isActiveEscalation: false, // Closed = not active
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Incorrect charges applied',
|
||||
raisedOn: '20 February, 2024',
|
||||
time: '9:30 AM',
|
||||
status: 'Resolved',
|
||||
description: 'Extra charges applied without notice.',
|
||||
isActiveEscalation: false, // Resolved = not active
|
||||
}, {
|
||||
id: 3,
|
||||
title: 'Unauthorized access to account',
|
||||
raisedOn: '28 March, 2024',
|
||||
time: '4:45 PM',
|
||||
status: 'Open',
|
||||
description: 'Suspicious activity detected in the bank account.',
|
||||
isActiveEscalation: true, // Open = active
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Delayed processing of payment',
|
||||
raisedOn: '5 March, 2024',
|
||||
time: '1:00 PM',
|
||||
status: 'Closed',
|
||||
description: 'Payment was not processed on time.',
|
||||
isActiveEscalation: false, // Closed = not active
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Incorrect charges applied',
|
||||
raisedOn: '20 February, 2024',
|
||||
time: '9:30 AM',
|
||||
status: 'Resolved',
|
||||
description: 'Extra charges applied without notice.',
|
||||
isActiveEscalation: false, // Resolved = not active
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Unauthorized access to account',
|
||||
raisedOn: '28 March, 2024',
|
||||
time: '4:45 PM',
|
||||
status: 'Open',
|
||||
description: 'Suspicious activity detected in the bank account.',
|
||||
isActiveEscalation: true, // Open = active
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Delayed processing of payment',
|
||||
raisedOn: '5 March, 2024',
|
||||
time: '1:00 PM',
|
||||
status: 'Closed',
|
||||
description: 'Payment was not processed on time.',
|
||||
isActiveEscalation: false, // Closed = not active
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: 'Incorrect charges applied',
|
||||
raisedOn: '20 February, 2024',
|
||||
time: '9:30 AM',
|
||||
status: 'Resolved',
|
||||
description: 'Extra charges applied without notice.',
|
||||
isActiveEscalation: false, // Resolved = not active
|
||||
}
|
||||
]);
|
||||
getAllEscalations();
|
||||
dispatch(getAllEscalations(loanAccountNumber, { page_no: currentPage-1, page_size: 10 }));
|
||||
|
||||
}, [currentPage]);
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOnline) {
|
||||
setCurrentPage(1);
|
||||
@@ -206,22 +67,18 @@ const Escalations = () => {
|
||||
setCurrentPage(page);
|
||||
};
|
||||
|
||||
const isActiveEscalationCase = true;
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<View style={[GenericStyles.fill, GenericStyles.whiteBackground]}>
|
||||
<NavigationHeader title={'All Escalations'} onBack={goBack} />
|
||||
<ScrollView style={[GenericStyles.ph16, GenericStyles.mt16, GenericStyles.fill]}>
|
||||
<SuspenseLoader
|
||||
loading={false}
|
||||
loading={isLoading}
|
||||
fallBack={
|
||||
<>
|
||||
{[...Array(8).keys()].map(() => (
|
||||
<LineLoader
|
||||
width={'100%'}
|
||||
height={75}
|
||||
height={150}
|
||||
style={[GenericStyles.br6, { marginBottom: 20 }]}
|
||||
/>
|
||||
))}
|
||||
@@ -229,25 +86,25 @@ const Escalations = () => {
|
||||
}
|
||||
>
|
||||
<View>
|
||||
{escalationsList?.length ? (
|
||||
{escalationData?.length ? (
|
||||
<>
|
||||
{escalationsList.map((escalation) => (
|
||||
<View key={escalation.id}>
|
||||
{escalationData.map((escalation) => (
|
||||
<View key={escalation.voc} >
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.whiteBackground,
|
||||
GenericStyles.w100,
|
||||
getShadowStyle(2),
|
||||
GenericStyles.mt16,
|
||||
styles.br16,
|
||||
styles.secondSection
|
||||
]}
|
||||
>
|
||||
<View style={[GenericStyles.row, GenericStyles.pt16, GenericStyles.pl16]} >
|
||||
<View style={[GenericStyles.row, GenericStyles.p16,]} >
|
||||
|
||||
<View style={[styles.flagContainer, GenericStyles.mr16, GenericStyles.centerAligned, {
|
||||
backgroundColor: escalation.isActiveEscalation ? COLORS.BACKGROUND.RED : COLORS.BACKGROUND.YELLOW_LIGHT
|
||||
backgroundColor: escalation.status === 'OPEN' ? COLORS.BACKGROUND.RED : COLORS.BACKGROUND.YELLOW_LIGHT
|
||||
}]}>
|
||||
<FlagIcon fillColor={escalation.isActiveEscalation ? '#FF0000' : '#FFB800'} />
|
||||
<FlagIcon fillColor={escalation.status === 'OPEN' ? '#FF0000' : '#FFB800'} />
|
||||
</View>
|
||||
|
||||
<View style={[styles.upperContainer]}>
|
||||
@@ -259,17 +116,17 @@ const Escalations = () => {
|
||||
<View style={[GenericStyles.row, GenericStyles.spaceBetween]}>
|
||||
<View style={[GenericStyles.row]}>
|
||||
<Text style={[GenericStyles.fontSize13, GenericStyles.mr4,styles.textColorGrayscale2]}>
|
||||
{escalation.raisedOn}
|
||||
{dayjs(escalation.createdAt).format('DD MMM YYYY')}
|
||||
</Text>
|
||||
<Text style={[GenericStyles.fontSize13, styles.textColorGrayscale3]}>
|
||||
{'|'}
|
||||
</Text>
|
||||
<Text style={[GenericStyles.fontSize13, GenericStyles.ml4, styles.textColorGrayscale2]}>
|
||||
{escalation.time}
|
||||
{dayjs(escalation.createdAt).format('hh:mm A')}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{escalation.isActiveEscalation ? (
|
||||
{escalation.status === 'OPEN' ? (
|
||||
<Tag variant={TagVariant.error} text="Open" />
|
||||
) : (
|
||||
<Tag variant={TagVariant.yellow} text="Closed" />
|
||||
@@ -281,7 +138,7 @@ const Escalations = () => {
|
||||
<View style={styles.horizontalLine} />
|
||||
<View style={[GenericStyles.p16]}>
|
||||
<Text style={[styles.textColorGrayscale3]}>Description</Text>
|
||||
<Text style={[styles.textColorGrayscale2,GenericStyles.fontSize13]}>{escalation.description}</Text>
|
||||
<Text style={[styles.textColorGrayscale2,GenericStyles.fontSize13]}>{escalation.voc}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -300,11 +157,11 @@ const Escalations = () => {
|
||||
</View>
|
||||
</SuspenseLoader>
|
||||
</ScrollView>
|
||||
{escalationsList?.length && totalPage > 1 ? (
|
||||
{escalationData?.length && pageData?.totalPages > 1 ? (
|
||||
<Pagination
|
||||
onPageChange={handlePageChange}
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPage}
|
||||
totalPages={pageData.totalPages}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
@@ -312,11 +169,13 @@ const Escalations = () => {
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
br16: {
|
||||
secondSection: {
|
||||
alignSelf: 'center',
|
||||
borderRadius: 16,
|
||||
width: '98%',
|
||||
},
|
||||
horizontalLine: {
|
||||
backgroundColor: COLORS.BACKGROUND.BLACK,
|
||||
backgroundColor: COLORS.TEXT.LIGHT,
|
||||
width: '100%',
|
||||
height: 1,
|
||||
marginTop: 16,
|
||||
@@ -330,8 +189,6 @@ const styles = StyleSheet.create({
|
||||
textColorGrayscale3: {
|
||||
color: COLORS.TEXT.LIGHT,
|
||||
},
|
||||
|
||||
|
||||
upperContainer: {
|
||||
width: 248,
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useAppSelector } from '@hooks';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import { CaseDetailStackEnum } from '../caseDetails/CaseDetailStack';
|
||||
import FlagIcon from '@assets/icons/FlagIcon';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
interface IEscalationsSection {
|
||||
caseId: string;
|
||||
@@ -19,24 +20,27 @@ interface IEscalationsSection {
|
||||
const EscalationsSection = ({ caseId }: IEscalationsSection) => {
|
||||
|
||||
const caseDetail = useAppSelector((state: RootState) => state.allCases?.caseDetails?.[caseId]) || {};
|
||||
const { loanAccountNumber } = caseDetail;
|
||||
const { escalationData, loanAccountNumber } = caseDetail;
|
||||
|
||||
|
||||
const handleOnPressClick = () => {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_VIEW_ALL_ESCALATIONS_SCREEN_CLICKED, {
|
||||
lan: loanAccountNumber,
|
||||
});
|
||||
navigateToScreen(CaseDetailStackEnum.ESCALATIONS);
|
||||
navigateToScreen(CaseDetailStackEnum.ESCALATIONS, {
|
||||
loanAccountNumber,
|
||||
caseId,
|
||||
})
|
||||
};
|
||||
|
||||
const isActiveEscalationCase = Number(escalationData?.activeEscalationCount) > 0;
|
||||
const activeEscalationCount = Number(escalationData?.activeEscalationCount);
|
||||
const pastEscalationCount = Number(escalationData?.pastEscalationCount);
|
||||
const totalEscalationsCount = activeEscalationCount + pastEscalationCount;
|
||||
const escalationCreatedAt = escalationData?.recentEscalationDetails.createdAt;
|
||||
const customerVoice = escalationData?.recentEscalationDetails.customerVoice;
|
||||
|
||||
|
||||
|
||||
const activeEscalationsCount = 2;
|
||||
const pastEscalationsCount = 3;
|
||||
const totalEscalationsCount = activeEscalationsCount + pastEscalationsCount; // might break for 2-3 digit number
|
||||
const isActiveEscalationCase = true;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
@@ -54,46 +58,55 @@ const EscalationsSection = ({ caseId }: IEscalationsSection) => {
|
||||
>
|
||||
<View style={GenericStyles.fill}>
|
||||
|
||||
<View style={[styles.escalationContainer, GenericStyles.row, GenericStyles.centerAligned, {
|
||||
backgroundColor: isActiveEscalationCase ? COLORS.BACKGROUND.RED : COLORS.BACKGROUND.YELLOW_LIGHT
|
||||
}]}>
|
||||
<FlagIcon fillColor={isActiveEscalationCase ? '#FF0000' : '#FFB800'} />
|
||||
<Text small style={[isActiveEscalationCase ? styles.activeEscalationText : styles.pastEscalationText]}>
|
||||
{`${totalEscalationsCount} `}{isActiveEscalationCase ? 'Active Escalations' : 'Past Escalations'}
|
||||
</Text>
|
||||
</View>
|
||||
{isActiveEscalationCase ? (
|
||||
<View style={[styles.escalationContainer, GenericStyles.row, GenericStyles.centerAligned, {
|
||||
backgroundColor: COLORS.BACKGROUND.RED
|
||||
}]}>
|
||||
<FlagIcon fillColor='#FF0000' />
|
||||
<Text small style={styles.activeEscalationText}>
|
||||
{`${activeEscalationCount} Active Escalation${activeEscalationCount === 1 ? '' : 's'}`}
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
<View style={[styles.escalationContainer, GenericStyles.row, GenericStyles.centerAligned, {
|
||||
backgroundColor: COLORS.BACKGROUND.YELLOW_LIGHT
|
||||
}]}>
|
||||
<FlagIcon fillColor='#FFB800' />
|
||||
<Text small style={styles.pastEscalationText}>
|
||||
{`${pastEscalationCount} Past Escalation${pastEscalationCount === 1 ? '' : 's'}`}
|
||||
</Text>
|
||||
</View>)
|
||||
}
|
||||
|
||||
|
||||
<View style={[GenericStyles.columnDirection, GenericStyles.fill,,GenericStyles.pl16]}>
|
||||
<View style={[GenericStyles.columnDirection, GenericStyles.fill, , GenericStyles.pl16]}>
|
||||
<Text dark bold style={[GenericStyles.fontSize14, GenericStyles.mb4]}>
|
||||
{'Fake/False commitments/information'}
|
||||
{customerVoice}
|
||||
</Text>
|
||||
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
|
||||
<Text style={[GenericStyles.fontSize13]}>
|
||||
{'Raised on:'}
|
||||
</Text>
|
||||
<Text style={[GenericStyles.fontSize13, GenericStyles.ml4,GenericStyles.mr4]}>
|
||||
{'9 May, 2024'}
|
||||
<Text style={[GenericStyles.fontSize13, GenericStyles.ml4, GenericStyles.mr4,styles.textColorGrayscale2]}>
|
||||
{dayjs(escalationCreatedAt).format('DD MMM YYYY')}
|
||||
</Text>
|
||||
<Text style={[GenericStyles.fontSize13]}>
|
||||
<Text style={[GenericStyles.fontSize13, styles.textColorGrayscale3]}>
|
||||
{'|'}
|
||||
</Text>
|
||||
<Text style={[GenericStyles.fontSize13,GenericStyles.ml4]}>
|
||||
{'2:29 PM'}
|
||||
<Text style={[GenericStyles.fontSize13, GenericStyles.ml4,styles.textColorGrayscale2]}>
|
||||
{dayjs(escalationCreatedAt).format('hh:mm A')}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter, GenericStyles.pt12,GenericStyles.pb16, GenericStyles.pl16]}>
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter, GenericStyles.pt12, GenericStyles.pb16, GenericStyles.pl16]}>
|
||||
<Text style={[styles.viewAllButton]}>View all {`(${totalEscalationsCount})`}</Text>
|
||||
<Chevron fillColor={COLORS.TEXT.BLUE} />
|
||||
</View>
|
||||
|
||||
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
@@ -106,8 +119,7 @@ export const styles = StyleSheet.create({
|
||||
paddingRight: 8,
|
||||
borderBottomRightRadius: 6,
|
||||
borderTopLeftRadius: 16,
|
||||
width : 148,
|
||||
|
||||
width: 148,
|
||||
},
|
||||
activeEscalationText: {
|
||||
color: COLORS.TEXT.RED,
|
||||
@@ -117,6 +129,12 @@ export const styles = StyleSheet.create({
|
||||
color: COLORS.TEXT.YELLOW,
|
||||
marginLeft: 4,
|
||||
},
|
||||
textColorGrayscale2: {
|
||||
color: COLORS.TEXT.BLACK,
|
||||
},
|
||||
textColorGrayscale3: {
|
||||
color: COLORS.TEXT.LIGHT,
|
||||
},
|
||||
viewAllButton: {
|
||||
color: COLORS.TEXT.BLUE,
|
||||
fontSize: 12,
|
||||
|
||||
46
src/screens/escalations/actions.ts
Normal file
46
src/screens/escalations/actions.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import axiosInstance, { getApiUrl, ApiKeys } from "@components/utlis/apiHelper";
|
||||
import { logError } from "@components/utlis/errorUtils";
|
||||
import { setEscalationData, setIsLoading, setPageData } from "@reducers/escalationSlice";
|
||||
import { AppDispatch } from "@store";
|
||||
|
||||
export interface IEscalationPayload {
|
||||
page_no: number;
|
||||
page_size: number;
|
||||
}
|
||||
|
||||
export interface IAllEscalationsPayload {
|
||||
lan: string;
|
||||
escalationPayload: IEscalationPayload;
|
||||
}
|
||||
|
||||
export const getAllEscalations = (
|
||||
lan: string,
|
||||
escalationPayload: IEscalationPayload = {
|
||||
page_no: 10,
|
||||
page_size: 0
|
||||
}
|
||||
) => (dispatch: AppDispatch) => {
|
||||
dispatch(setIsLoading(true));
|
||||
console.log('called getAllEscalations API');
|
||||
const payload = {
|
||||
loan_account_number: lan,
|
||||
...escalationPayload,
|
||||
}
|
||||
const url = getApiUrl(ApiKeys.ALL_ESCALATIONS, null, payload );
|
||||
console.log('getAllEscalationsUrl', url);
|
||||
return axiosInstance
|
||||
.get(url)
|
||||
.then((response) => {
|
||||
console.log('getAllEscalationsResponse', response);
|
||||
if (response?.data) {
|
||||
dispatch(setEscalationData(response.data.data));
|
||||
dispatch(setPageData(response.data.pages));
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
logError(err);
|
||||
})
|
||||
.finally(() => {
|
||||
dispatch(setIsLoading(false));
|
||||
});
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import commitmentTrackerSlice from '@reducers/commitmentTrackerSlice';
|
||||
import nearbyCasesSlice from '@reducers/nearbyCasesSlice';
|
||||
import activeCallSlice from '@reducers/activeCallSlice';
|
||||
import documentsSlice from '@reducers/documentsSlice';
|
||||
import escalationSlice from '@reducers/escalationSlice';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
case: caseReducer,
|
||||
@@ -66,6 +67,7 @@ const rootReducer = combineReducers({
|
||||
nearbyCasesSlice: nearbyCasesSlice,
|
||||
activeCall: activeCallSlice,
|
||||
documentsSlice: documentsSlice,
|
||||
escalationSlice: escalationSlice,
|
||||
});
|
||||
|
||||
const persistConfig = {
|
||||
|
||||
Reference in New Issue
Block a user