diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts index 465e462f..bb22535d 100644 --- a/src/components/utlis/apiHelper.ts +++ b/src/components/utlis/apiHelper.ts @@ -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 = {} as Record; @@ -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 = { diff --git a/src/reducer/escalationSlice.ts b/src/reducer/escalationSlice.ts new file mode 100644 index 00000000..2ebbd498 --- /dev/null +++ b/src/reducer/escalationSlice.ts @@ -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; diff --git a/src/screens/allCases/ListItem.tsx b/src/screens/allCases/ListItem.tsx index db0b4c94..52637745 100644 --- a/src/screens/allCases/ListItem.tsx +++ b/src/screens/allCases/ListItem.tsx @@ -84,6 +84,7 @@ const ListItem: React.FC = (props) => { interactionStatus, totalOverdueAmount, distanceInKm, + escalationData, } = caseListItemDetailObj; const isVisitPlanStatusLocked = useAppSelector( @@ -206,20 +207,17 @@ const ListItem: React.FC = (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 ( - + + = (props) => { ? COLORS.BACKGROUND.SILVER : COLORS.BACKGROUND.PRIMARY, }, + ]} > {isEscalationCase ? ( - - - - {`${totalEscalationCaseCount} `}{isActiveEscalationCase ? 'Active Escalations' : 'Past Escalations'} - - - ) : null} - - - - {showVisitPlanBtn ? ( - - - - ) : null} - {showInVisitPlanTag && ( - - In visit plan - - )} - {nearbyCaseView && distanceInKm && ( - - - {relativeDistanceFormatter(distanceInKm)} km away - - - )} - - - {paymentStatus ? ( - - - - ) : null} - {collectionTag ? ( - - - - ) : null} - - {!isVisitPlan && distanceOfCaseItem ? ( - - - } - text={Number(distanceOfCaseItem?.toFixed(1)) + ' KM'} - variant={isNearestCaseView ? TagVariant.darkBlue : TagVariant.darkGray} - /> - - ) : null} - - - {customerName} - - {taskTitle ? ( - - - {/* @ts-ignore */} - {TaskTitleUIMapping[taskTitle]} - {displayAddress ? `: ${displayAddress}` : null} + isActiveEscalationCase ? ( + + + + {`${activeEscalationCount} Active Escalation${activeEscalationCount === 1 ? '' : 's'}`} - + ) : ( - - {displayAddress} - + + + + {`${pastEscalationCount} Past Escalation${pastEscalationCount === 1 ? '' : 's'}`} + + + ) + ) : null} + + + + {showVisitPlanBtn ? ( + + + + ) : null} + {showInVisitPlanTag && ( + + In visit plan + )} - - {is1To30FieldAgent ? ( - - Total due {formatAmount(totalOverdueAmount, false)} - {' '}DPD Cycle {dpdCycle} + {nearbyCaseView && distanceInKm && ( + + + {relativeDistanceFormatter(distanceInKm)} km away + + + )} + + + {paymentStatus ? ( + + + + ) : null} + {collectionTag ? ( + + + + ) : null} + + {!isVisitPlan && distanceOfCaseItem ? ( + + + } + text={Number(distanceOfCaseItem?.toFixed(1)) + ' KM'} + variant={isNearestCaseView ? TagVariant.darkBlue : TagVariant.darkGray} + /> + + ) : null} + + + {customerName} + + {taskTitle ? ( + + + {/* @ts-ignore */} + {TaskTitleUIMapping[taskTitle]} + {displayAddress ? `: ${displayAddress}` : null} + ) : ( - - Total due {formatAmount(totalOverdueAmount, false)} - {' '}DPD bucket {dpdBucket} + + {displayAddress} )} - {caseInteractionStatus ? ( - - {caseInteractionStatus} - - ) : null} + + {is1To30FieldAgent ? ( + + Total due {formatAmount(totalOverdueAmount, false)} + {' '}DPD Cycle {dpdCycle} + + ) : ( + + Total due {formatAmount(totalOverdueAmount, false)} + {' '}DPD bucket {dpdBucket} + + )} + {caseInteractionStatus ? ( + + {caseInteractionStatus} + + ) : null} + @@ -339,17 +350,15 @@ const ListItem: React.FC = (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: { diff --git a/src/screens/allCases/interface.ts b/src/screens/allCases/interface.ts index 5f3f7bba..848aebbf 100644 --- a/src/screens/allCases/interface.ts +++ b/src/screens/allCases/interface.ts @@ -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; +} diff --git a/src/screens/caseDetails/CollectionCaseDetail.tsx b/src/screens/caseDetails/CollectionCaseDetail.tsx index b9fddf67..192bef01 100644 --- a/src/screens/caseDetails/CollectionCaseDetail.tsx +++ b/src/screens/caseDetails/CollectionCaseDetail.tsx @@ -40,6 +40,7 @@ const CollectionCaseDetails: React.FC = (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 = (props) => { > - + {isActiveEscalation ? (): null} diff --git a/src/screens/caseDetails/interface.ts b/src/screens/caseDetails/interface.ts index a6031e07..b88ddff5 100644 --- a/src/screens/caseDetails/interface.ts +++ b/src/screens/caseDetails/interface.ts @@ -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 { diff --git a/src/screens/escalations/Escalations.tsx b/src/screens/escalations/Escalations.tsx index 0a2d5932..b522f568 100644 --- a/src/screens/escalations/Escalations.tsx +++ b/src/screens/escalations/Escalations.tsx @@ -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 = (props) => { - const [escalationsList, setEscalationsList] = useState([]); - 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 ( {[...Array(8).keys()].map(() => ( ))} @@ -229,25 +86,25 @@ const Escalations = () => { } > - {escalationsList?.length ? ( + {escalationData?.length ? ( <> - {escalationsList.map((escalation) => ( - + {escalationData.map((escalation) => ( + - + - + @@ -259,17 +116,17 @@ const Escalations = () => { - {escalation.raisedOn} + {dayjs(escalation.createdAt).format('DD MMM YYYY')} {'|'} - {escalation.time} + {dayjs(escalation.createdAt).format('hh:mm A')} - {escalation.isActiveEscalation ? ( + {escalation.status === 'OPEN' ? ( ) : ( @@ -281,7 +138,7 @@ const Escalations = () => { Description - {escalation.description} + {escalation.voc} @@ -300,11 +157,11 @@ const Escalations = () => { - {escalationsList?.length && totalPage > 1 ? ( + {escalationData?.length && pageData?.totalPages > 1 ? ( ) : null} @@ -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, }, diff --git a/src/screens/escalations/EscalationsSection.tsx b/src/screens/escalations/EscalationsSection.tsx index 840b5057..7b8f6612 100644 --- a/src/screens/escalations/EscalationsSection.tsx +++ b/src/screens/escalations/EscalationsSection.tsx @@ -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 ( { > - - - - {`${totalEscalationsCount} `}{isActiveEscalationCase ? 'Active Escalations' : 'Past Escalations'} - - + {isActiveEscalationCase ? ( + + + + {`${activeEscalationCount} Active Escalation${activeEscalationCount === 1 ? '' : 's'}`} + + + ) : ( + + + + {`${pastEscalationCount} Past Escalation${pastEscalationCount === 1 ? '' : 's'}`} + + ) + } - - + - {'Fake/False commitments/information'} + {customerVoice} {'Raised on:'} - - {'9 May, 2024'} + + {dayjs(escalationCreatedAt).format('DD MMM YYYY')} - + {'|'} - - {'2:29 PM'} + + {dayjs(escalationCreatedAt).format('hh:mm A')} - - + View all {`(${totalEscalationsCount})`} - + ); @@ -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, diff --git a/src/screens/escalations/actions.ts b/src/screens/escalations/actions.ts new file mode 100644 index 00000000..6c83802a --- /dev/null +++ b/src/screens/escalations/actions.ts @@ -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)); + }); +} diff --git a/src/store/store.ts b/src/store/store.ts index 0c728e9a..436296d2 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -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 = {