NTP-8094 | Case Card Revamp (#983)
Co-authored-by: aishwarya.srivastava <aishwarya.srivastava@navi.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import { logError } from '../components/utlis/errorUtils';
|
||||
import { type IDocument, removeDocumentByQuestionKey } from '../reducer/feedbackImagesSlice';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
import { PAST_FEEDBACK_PAGE_SIZE } from '@screens/caseDetails/feedback/pastFeedbackCommon';
|
||||
|
||||
export const getRepaymentsData = (loanAccountNumber: string) => (dispatch: AppDispatch) => {
|
||||
dispatch(setRepaymentsLoading({ loanAccountNumber, isLoading: true }));
|
||||
@@ -81,7 +82,7 @@ export const getFeedbackHistory = (loanAccountNumber: string) => (dispatch: AppD
|
||||
{
|
||||
loan_account_number: loanAccountNumber,
|
||||
page_no: 0,
|
||||
page_size: 5,
|
||||
page_size: PAST_FEEDBACK_PAGE_SIZE,
|
||||
}
|
||||
);
|
||||
axiosInstance
|
||||
|
||||
@@ -77,27 +77,23 @@ export const getPastFeedbacksOnAddresses = (pastFeedbackPayload: IPastFeedbacksP
|
||||
});
|
||||
};
|
||||
|
||||
export const getTopFeedbacks = (loanAccountNumber: string) => (dispatch: AppDispatch) => {
|
||||
// TODO: Change API Endpoint
|
||||
const url = getApiUrl(ApiKeys.PAST_FEEDBACK_ON_ADDRESSES);
|
||||
dispatch(setTopFeedbacksLoading({ loanAccountNumber, isLoading: true }));
|
||||
export const getTopFeedbacks = (caseId: string) => (dispatch: AppDispatch) => {
|
||||
const url = getApiUrl(ApiKeys.GET_PRIORTIY_FEEDBACK);
|
||||
dispatch(setTopFeedbacksLoading({ caseId, isLoading: true }));
|
||||
return axiosInstance
|
||||
.get(url, {
|
||||
params: { loanAccountNumber },
|
||||
params: { caseReferenceId: caseId },
|
||||
})
|
||||
.then((response) => {
|
||||
dispatch(
|
||||
setTopFeedbacks({
|
||||
loanAccountNumber,
|
||||
feedbacks: [
|
||||
response?.data?.data?.currentMonthFeedbackStatus,
|
||||
response?.data?.data?.lastMonthFeedbackStatus,
|
||||
],
|
||||
caseId,
|
||||
feedbacks: response?.data || [],
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
dispatch(setTopFeedbacksLoading({ loanAccountNumber, isLoading: false }));
|
||||
logError(err);
|
||||
});
|
||||
})
|
||||
.finally(() => dispatch(setTopFeedbacksLoading({ caseId, isLoading: false })));
|
||||
};
|
||||
|
||||
@@ -12,10 +12,10 @@ const LocationDistanceIcon = (props: ILocationDistanceIcon) => {
|
||||
const backgroundColor = props?.backgroundColor || COLORS.BACKGROUND.BLUE;
|
||||
|
||||
return (
|
||||
<Svg width="16" height="20" viewBox="0 0 16 20" fill="none">
|
||||
<Svg width="16" height="16" viewBox="0 0 16 20" fill="none">
|
||||
<Rect width="16" height="40" fill={backgroundColor} />
|
||||
<Mask id="mask0_15800_100880" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="20">
|
||||
<Rect width="16" height="20" fill="#D9D9D9" />
|
||||
<Mask id="mask0_15800_100880" maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16">
|
||||
<Rect width="16" height="16" fill="#D9D9D9" />
|
||||
</Mask>
|
||||
<G transform={`translate(0, -2)`}>
|
||||
<G mask="url(#mask0_15800_100880)" transform="translate(0,4)">
|
||||
|
||||
16
src/assets/icons/TagPlaceholderIcon.tsx
Normal file
16
src/assets/icons/TagPlaceholderIcon.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { Path, Svg } from 'react-native-svg';
|
||||
|
||||
const TagPlaceholderIcon = () => {
|
||||
return (
|
||||
<Svg width="70" height="27" 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"
|
||||
/>
|
||||
<Path d="M66 20L66 26.5L70 20L66 20Z" fill="#A0CBFD" />
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default TagPlaceholderIcon;
|
||||
@@ -44,6 +44,7 @@ import { NUDGE_BOTTOM_SHEET_DEFAULT_STATE } from './constants';
|
||||
import { useBackHandler } from '@hooks/useBackHandler';
|
||||
import { CALLING_NUDGE } from '@screens/caseDetails/CallingFlow/constants';
|
||||
import { isFunction } from '@components/utlis/commonFunctions';
|
||||
import { getTopFeedbacks } from '@actions/feedbackActions';
|
||||
import { setPostOperationalHourRestrictions } from '@reducers/postOperationalHourRestrictionsSlice';
|
||||
import { getSyncTime } from '@hooks/capturingApi';
|
||||
|
||||
@@ -228,6 +229,7 @@ const Widget: React.FC<IWidget> = (props) => {
|
||||
caseId,
|
||||
});
|
||||
}
|
||||
dispatch(getTopFeedbacks(caseId));
|
||||
dispatch(
|
||||
deleteJourney({
|
||||
caseId,
|
||||
|
||||
@@ -111,6 +111,7 @@ export enum ApiKeys {
|
||||
GET_EMI_SCHEDULE = 'GET_EMI_SCHEDULE',
|
||||
GET_REPAYMENTS = 'GET_REPAYMENTS',
|
||||
GET_FEEDBACK_HISTORY = 'GET_FEEDBACK_HISTORY',
|
||||
GET_PRIORTIY_FEEDBACK = 'GET_PRIORTIY_FEEDBACK',
|
||||
}
|
||||
|
||||
export const API_URLS: Record<ApiKeys, string> = {} as Record<ApiKeys, string>;
|
||||
@@ -215,6 +216,7 @@ API_URLS[ApiKeys.GET_GROUPED_ADDRESSES_AND_GEOLOCATIONS] =
|
||||
API_URLS[ApiKeys.GET_EMI_SCHEDULE] = '/collection-cases/{loanAccountNumber}/emiSchedule';
|
||||
API_URLS[ApiKeys.GET_REPAYMENTS] = '/collection-cases/{loanAccountNumber}/repayments';
|
||||
API_URLS[ApiKeys.GET_FEEDBACK_HISTORY] = '/feedback/filters';
|
||||
API_URLS[ApiKeys.GET_PRIORTIY_FEEDBACK] = 'feedback/case-status';
|
||||
|
||||
export const API_STATUS_CODE = {
|
||||
OK: 200,
|
||||
|
||||
@@ -4,7 +4,7 @@ interface ITopFeedback {
|
||||
status: string;
|
||||
color: string;
|
||||
referenceId: string;
|
||||
offset: number;
|
||||
orderOffset: number;
|
||||
createdAt: string;
|
||||
type: string;
|
||||
}
|
||||
@@ -23,16 +23,16 @@ const TopFeedbacksSlice = createSlice({
|
||||
initialState,
|
||||
reducers: {
|
||||
setTopFeedbacks: (state, action) => {
|
||||
const { loanAccountNumber, feedbacks } = action.payload || {};
|
||||
state[loanAccountNumber] = {
|
||||
...(state[loanAccountNumber] || {}),
|
||||
const { caseId, feedbacks } = action.payload || {};
|
||||
state[caseId] = {
|
||||
...(state[caseId] || {}),
|
||||
feedbacks,
|
||||
};
|
||||
},
|
||||
setTopFeedbacksLoading: (state, action) => {
|
||||
const { loanAccountNumber, isLoading } = action.payload || {};
|
||||
state[loanAccountNumber] = {
|
||||
...(state?.[loanAccountNumber] || {}),
|
||||
const { caseId, isLoading } = action.payload || {};
|
||||
state[caseId] = {
|
||||
...(state?.[caseId] || {}),
|
||||
isLoading: isLoading,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useMemo } from 'react';
|
||||
import { Text, View, ViewProps, StyleSheet, Pressable } from 'react-native';
|
||||
import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
|
||||
import { CaseTypes, ICaseItemCaseDetailObj } from './interface';
|
||||
import ListItem from './ListItem';
|
||||
import Button from '../../../RN-UI-LIB/src/components/Button';
|
||||
import { navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import { useAppSelector } from '../../hooks';
|
||||
@@ -13,6 +12,7 @@ import LocationIcon from '@assets/icons/LocationIcon';
|
||||
import ArrowRightOutlineIcon from '@rn-ui-lib/icons/ArrowRightOutlineIcon';
|
||||
import { addClickstreamEvent } from '@services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
|
||||
import CaseListItem from './CaseItem/CaseListItem';
|
||||
|
||||
interface ICaseItemProps extends ViewProps {
|
||||
caseDetailObj: ICaseItemCaseDetailObj;
|
||||
@@ -125,7 +125,7 @@ const CaseItem: React.FC<ICaseItemProps> = ({
|
||||
default:
|
||||
return (
|
||||
<View {...restProps}>
|
||||
<ListItem
|
||||
<CaseListItem
|
||||
caseListItemDetailObj={getCaseItemCaseDetailObj}
|
||||
shouldBatchAvatar={shouldBatchAvatar}
|
||||
isCompleted={isCompleted}
|
||||
|
||||
41
src/screens/allCases/CaseItem/CaseDetailKeyValue.tsx
Normal file
41
src/screens/allCases/CaseItem/CaseDetailKeyValue.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
import Text from '@rn-ui-lib/components/Text';
|
||||
import { GenericStyles } from '@rn-ui-lib/styles';
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { ICaseDetailKeyValue } from '../interface';
|
||||
|
||||
const CaseDetailKeyValue = (props: ICaseDetailKeyValue) => {
|
||||
const { title, value, showDot = false } = props;
|
||||
return (
|
||||
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
|
||||
<Text small style={[styles.caseStatusText]} bold>
|
||||
<Text small bold style={styles.caseStatusTextTitle}>
|
||||
{title}
|
||||
</Text>{' '}
|
||||
{value ?? '--'}
|
||||
</Text>
|
||||
{showDot ? <View style={styles.dot} /> : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
caseStatusText: {
|
||||
color: COLORS.TEXT.GREY_32465B,
|
||||
fontWeight: '500',
|
||||
},
|
||||
caseStatusTextTitle: {
|
||||
color: COLORS.TEXT.LIGHT,
|
||||
fontWeight: '500',
|
||||
},
|
||||
dot: {
|
||||
height: 4,
|
||||
width: 4,
|
||||
borderRadius: 2,
|
||||
marginHorizontal: 8,
|
||||
backgroundColor: COLORS.TEXT.GREY_1,
|
||||
},
|
||||
});
|
||||
|
||||
export default CaseDetailKeyValue;
|
||||
254
src/screens/allCases/CaseItem/CaseListItem.tsx
Normal file
254
src/screens/allCases/CaseItem/CaseListItem.tsx
Normal file
@@ -0,0 +1,254 @@
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { Pressable, StyleSheet, View } from 'react-native';
|
||||
import Heading from '../../../../RN-UI-LIB/src/components/Heading';
|
||||
import Text from '../../../../RN-UI-LIB/src/components/Text';
|
||||
import { GenericStyles, getShadowStyle } from '../../../../RN-UI-LIB/src/styles';
|
||||
import { getCurrentScreen, navigateToScreen } from '../../../components/utlis/navigationUtlis';
|
||||
import { useAppDispatch, useAppSelector } from '../../../hooks';
|
||||
import { setPinnedRank, setSelectedTodoListMap } from '../../../reducer/allCasesSlice';
|
||||
import CaseItemAvatar from '../CaseItemAvatar';
|
||||
import { CaseStatuses, 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';
|
||||
import { formatAmount } from '../../../../RN-UI-LIB/src/utlis/amount';
|
||||
import RoundCheckIcon from '../../../../RN-UI-LIB/src/Icons/RoundCheckIcon';
|
||||
import { getDocumentList, pluralise } from '../../../components/utlis/commonFunctions';
|
||||
import { toast } from '../../../../RN-UI-LIB/src/components/toast';
|
||||
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';
|
||||
|
||||
const CaseListItem: React.FC<ICaseListItem> = (props) => {
|
||||
const {
|
||||
caseListItemDetailObj,
|
||||
isCompleted,
|
||||
isTodoItem,
|
||||
shouldBatchAvatar,
|
||||
allCasesView,
|
||||
nearbyCaseView,
|
||||
isVisitPlan,
|
||||
} = props;
|
||||
const {
|
||||
caseReferenceId: caseId,
|
||||
isIntermediateOrSelectedTodoCaseItem,
|
||||
caseStatus,
|
||||
caseType,
|
||||
dpdBucket,
|
||||
dpdCycle,
|
||||
daysTillDeallocation,
|
||||
pinRank,
|
||||
isSynced,
|
||||
totalOverdueAmount,
|
||||
escalationData,
|
||||
} = caseListItemDetailObj;
|
||||
|
||||
const isVisitPlanStatusLocked = useAppSelector(
|
||||
(state) => state.user?.lock?.visitPlanStatus === VisitPlanStatus.LOCKED
|
||||
);
|
||||
const is1To30FieldAgent = useAppSelector((state) => state.user?.is1To30FieldAgent);
|
||||
const isTeamLead = useAppSelector((state) => state.user.isTeamLead);
|
||||
const activeEscalationCount = Number(escalationData?.activeEscalationCount ?? 0);
|
||||
const pastEscalationCount = Number(escalationData?.pastEscalationCount ?? 0);
|
||||
const totalEscalationsCount = activeEscalationCount + pastEscalationCount;
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleAvatarClick = () => {
|
||||
if (isTodoItem || caseStatus === CaseStatuses.CLOSED) {
|
||||
return;
|
||||
}
|
||||
if (isVisitPlanStatusLocked) {
|
||||
toast({
|
||||
type: 'info',
|
||||
text1: ToastMessages.CASES_SELECTION_DISABLED,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (pinRank) {
|
||||
dispatch(
|
||||
setSelectedTodoListMap({
|
||||
pinRank,
|
||||
caseReferenceId: caseId,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
dispatch(
|
||||
setPinnedRank({
|
||||
caseReferenceId: caseId,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCaseClick = async () => {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_CASE_LIST_CASE_CLICKED, {
|
||||
caseId,
|
||||
screen: getCurrentScreen().name === 'Profile' ? 'Completed Cases' : getCurrentScreen().name, // todo: need to update use router
|
||||
caseType,
|
||||
});
|
||||
if (nearbyCaseView) {
|
||||
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_NEARBY_CASE_CLICKED, {
|
||||
caseId,
|
||||
});
|
||||
}
|
||||
navigateToScreen(PageRouteEnum.CASE_DETAIL_STACK, {
|
||||
screen: CaseDetailStackEnum.COLLECTION_CASE_DETAIL,
|
||||
params: { caseId },
|
||||
});
|
||||
};
|
||||
|
||||
const isCaseSelected = !isTodoItem && !!isIntermediateOrSelectedTodoCaseItem;
|
||||
const address =
|
||||
caseListItemDetailObj?.currentTask?.metadata?.addressLine ||
|
||||
caseListItemDetailObj?.addressString;
|
||||
const customerName =
|
||||
caseListItemDetailObj.customerInfo?.name ||
|
||||
caseListItemDetailObj.customerInfo?.customerName ||
|
||||
caseListItemDetailObj.customerName;
|
||||
|
||||
const getCaseItemAvatarCaseDetailObj = useMemo(
|
||||
(): ICaseItemAvatarCaseDetailObj => ({
|
||||
isPinned: pinRank ? true : false,
|
||||
isCaseSynced: isSynced as boolean,
|
||||
customerName: customerName,
|
||||
caseId,
|
||||
documentList: getDocumentList(caseListItemDetailObj) || [],
|
||||
caseType: caseType,
|
||||
imageUri: caseListItemDetailObj?.imageUri || '',
|
||||
}),
|
||||
[
|
||||
caseType,
|
||||
isSynced,
|
||||
pinRank,
|
||||
caseListItemDetailObj?.customerInfo?.documents,
|
||||
caseListItemDetailObj?.documents,
|
||||
caseListItemDetailObj?.imageUri,
|
||||
]
|
||||
);
|
||||
|
||||
const isCaseItemPinnedMainView = getCaseItemAvatarCaseDetailObj.isPinned && allCasesView;
|
||||
const caseCompleted = COMPLETED_STATUSES.includes(caseStatus);
|
||||
|
||||
const showVisitPlanBtn =
|
||||
!(caseCompleted || isCaseItemPinnedMainView) &&
|
||||
!isTodoItem &&
|
||||
!isCompleted &&
|
||||
!isTeamLead &&
|
||||
!nearbyCaseView;
|
||||
|
||||
const showInVisitPlanTag = isCaseItemPinnedMainView && !caseCompleted;
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
style={[
|
||||
styles.listItem,
|
||||
getShadowStyle(2),
|
||||
isCaseSelected ? styles.backgroundBlueLight : GenericStyles.whiteBackground,
|
||||
]}
|
||||
onPress={handleCaseClick}
|
||||
>
|
||||
<Escalation escalationData={escalationData} />
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.centerAlignedRow,
|
||||
GenericStyles.justifyStart,
|
||||
isCaseSelected ? styles.backgroundBlue : styles.backgroundSilverLight,
|
||||
GenericStyles.w100,
|
||||
GenericStyles.p12,
|
||||
!totalEscalationsCount ? GenericStyles.brt8 : {},
|
||||
]}
|
||||
>
|
||||
<CaseItemAvatar
|
||||
caseDetailObj={getCaseItemAvatarCaseDetailObj}
|
||||
shouldBatchAvatar={shouldBatchAvatar}
|
||||
/>
|
||||
<View style={[GenericStyles.pl8, GenericStyles.alignStart, GenericStyles.flex90]}>
|
||||
<View style={[GenericStyles.w80, GenericStyles.pr16]}>
|
||||
<Heading ellipsizeMode="tail" numberOfLines={1} type="h5" bold dark>
|
||||
{customerName}
|
||||
</Heading>
|
||||
</View>
|
||||
<CaseStatus caseListItemDetailObj={caseListItemDetailObj} isVisitPlan={isVisitPlan} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{showVisitPlanBtn ? (
|
||||
<Pressable onPress={handleAvatarClick} style={styles.selectBtn}>
|
||||
<RoundCheckIcon focused={isCaseSelected} />
|
||||
</Pressable>
|
||||
) : null}
|
||||
|
||||
{showInVisitPlanTag ? <VisitPlanTag totalEscalationsCount={totalEscalationsCount} /> : null}
|
||||
|
||||
<View style={GenericStyles.p12}>
|
||||
<Text light ellipsizeMode="tail" style={[GenericStyles.fontSize13, GenericStyles.mb16]}>
|
||||
{address ? address : 'Address not available'}
|
||||
</Text>
|
||||
{is1To30FieldAgent ? (
|
||||
<View>
|
||||
<CaseDetailKeyValue title="Total due" value={formatAmount(totalOverdueAmount, false)} />
|
||||
<View style={[GenericStyles.row, GenericStyles.wrap]}>
|
||||
<CaseDetailKeyValue showDot title="Cycle" value={dpdCycle} />
|
||||
<CaseDetailKeyValue
|
||||
title="Deallocation in"
|
||||
value={
|
||||
daysTillDeallocation
|
||||
? `${daysTillDeallocation} day${pluralise(daysTillDeallocation, '', 's')}`
|
||||
: '--'
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<View style={[GenericStyles.row, GenericStyles.wrap]}>
|
||||
<CaseDetailKeyValue
|
||||
showDot
|
||||
title="Total due"
|
||||
value={formatAmount(totalOverdueAmount, false)}
|
||||
/>
|
||||
<CaseDetailKeyValue title="Bucket" value={dpdBucket} />
|
||||
</View>
|
||||
)}
|
||||
<FeedbackStatus caseListItemDetailObj={caseListItemDetailObj} />
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
listItem: {
|
||||
backgroundColor: COLORS.BACKGROUND.PRIMARY,
|
||||
borderRadius: 8,
|
||||
marginVertical: 10,
|
||||
position: 'relative',
|
||||
},
|
||||
selectBtn: {
|
||||
position: 'absolute',
|
||||
paddingTop: 12,
|
||||
right: 0,
|
||||
paddingRight: 12,
|
||||
width: 80,
|
||||
height: 80,
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
backgroundBlue: {
|
||||
backgroundColor: COLORS.BACKGROUND.BLUE,
|
||||
},
|
||||
backgroundSilverLight: {
|
||||
backgroundColor: COLORS.BACKGROUND.SILVER_LIGHT_3,
|
||||
},
|
||||
backgroundBlueLight: {
|
||||
backgroundColor: COLORS.BACKGROUND.BLUE_LIGHT_3,
|
||||
},
|
||||
});
|
||||
|
||||
export default memo(CaseListItem);
|
||||
57
src/screens/allCases/CaseItem/CaseStatus.tsx
Normal file
57
src/screens/allCases/CaseItem/CaseStatus.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { GenericStyles } from '@rn-ui-lib/styles';
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { paymentStatusMapping } from '../utils';
|
||||
import Tag, { TagVariant } from '@rn-ui-lib/components/Tag';
|
||||
import LocationDistanceIcon from '@assets/icons/LocationDistanceIcon';
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
import { useAppSelector } from '@hooks';
|
||||
import { TABS_KEYS } from '../constants';
|
||||
import { ICaseStatus } from '../interface';
|
||||
|
||||
const CaseStatus = (props: ICaseStatus) => {
|
||||
const { caseListItemDetailObj, isVisitPlan } = props;
|
||||
const { collectionTag, paymentStatus, caseReferenceId } = caseListItemDetailObj || {};
|
||||
const distanceMapOfNearbyCases =
|
||||
useAppSelector((state) => state.nearbyCasesSlice.caseReferenceIdToDistanceMap) || {};
|
||||
const selectedTab = useAppSelector((state) => state?.nearbyCasesSlice?.sortTabSelected);
|
||||
|
||||
const distanceOfCaseItem = distanceMapOfNearbyCases.get(caseReferenceId);
|
||||
const isNearestCaseView = selectedTab === TABS_KEYS.NEAREST_CASE;
|
||||
|
||||
return (
|
||||
<View style={[GenericStyles.centerAlignedRow, GenericStyles.mt2]}>
|
||||
{paymentStatus ? (
|
||||
<View style={[GenericStyles.mr8]}>
|
||||
<Tag
|
||||
variant={paymentStatusMapping[paymentStatus]?.variant || TagVariant.alert}
|
||||
text={(paymentStatusMapping[paymentStatus]?.label || paymentStatus) as string}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
{collectionTag ? (
|
||||
<View style={[GenericStyles.mr8]}>
|
||||
<Tag variant={TagVariant.white} text={collectionTag} />
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{!isVisitPlan && distanceOfCaseItem ? (
|
||||
<Tag
|
||||
tagIcon={
|
||||
<LocationDistanceIcon
|
||||
iconColor={isNearestCaseView ? COLORS.TEXT.BLUE_DARK_2 : COLORS.TEXT.GREY_3}
|
||||
backgroundColor={
|
||||
isNearestCaseView ? COLORS.BACKGROUND.BLUE_LIGHT_3 : COLORS.BACKGROUND.PRIMARY
|
||||
}
|
||||
/>
|
||||
}
|
||||
style={GenericStyles.pl2}
|
||||
text={Number(distanceOfCaseItem?.toFixed(1)) + ' KM'}
|
||||
variant={isNearestCaseView ? TagVariant.darkGray1 : TagVariant.white}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default CaseStatus;
|
||||
77
src/screens/allCases/CaseItem/FeedbackStatus.tsx
Normal file
77
src/screens/allCases/CaseItem/FeedbackStatus.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
import Text from '@rn-ui-lib/components/Text';
|
||||
import { GenericStyles } from '@rn-ui-lib/styles';
|
||||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { View } from 'react-native';
|
||||
import { IFeedbackStatus } from '../interface';
|
||||
import { feedbackStatusColorMapping } from '../utils';
|
||||
|
||||
const FeedbackStatus = (props: IFeedbackStatus) => {
|
||||
const { caseListItemDetailObj } = props;
|
||||
const { currentMonthFeedbackStatus, lastMonthFeedbackStatus } = caseListItemDetailObj || {};
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={[styles.dashedBorder, GenericStyles.mv16]} />
|
||||
<View style={[GenericStyles.row, GenericStyles.justifyContentSpaceBetween]}>
|
||||
<View>
|
||||
<Text small style={GenericStyles.lh18}>
|
||||
Current month
|
||||
</Text>
|
||||
<Text
|
||||
small
|
||||
style={[
|
||||
styles.feedbackStatus,
|
||||
{
|
||||
color:
|
||||
feedbackStatusColorMapping[
|
||||
currentMonthFeedbackStatus?.color as keyof typeof feedbackStatusColorMapping
|
||||
] ?? COLORS.TEXT.BLACK,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{currentMonthFeedbackStatus?.status
|
||||
? currentMonthFeedbackStatus?.status
|
||||
: 'Not attempted'}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={GenericStyles.alignItemsFlexEnd}>
|
||||
<Text small style={GenericStyles.lh18}>
|
||||
Last month
|
||||
</Text>
|
||||
<Text
|
||||
small
|
||||
style={[
|
||||
styles.feedbackStatus,
|
||||
{
|
||||
color:
|
||||
feedbackStatusColorMapping[
|
||||
lastMonthFeedbackStatus?.color as keyof typeof feedbackStatusColorMapping
|
||||
] ?? COLORS.TEXT.BLACK,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{lastMonthFeedbackStatus?.status ? lastMonthFeedbackStatus?.status : 'Not attempted'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
feedbackStatus: {
|
||||
lineHeight: 18,
|
||||
marginTop: 4,
|
||||
color: COLORS.TEXT.BLACK,
|
||||
fontWeight: '500',
|
||||
},
|
||||
dashedBorder: {
|
||||
borderTopWidth: 1,
|
||||
borderColor: COLORS.BORDER.PRIMARY,
|
||||
borderStyle: 'dashed',
|
||||
},
|
||||
});
|
||||
|
||||
export default FeedbackStatus;
|
||||
46
src/screens/allCases/CaseItem/VisitPlanTag.tsx
Normal file
46
src/screens/allCases/CaseItem/VisitPlanTag.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import TagPlaceholderIcon from '@assets/icons/TagPlaceholderIcon';
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
import Text from '@rn-ui-lib/components/Text';
|
||||
import { GenericStyles } from '@rn-ui-lib/styles';
|
||||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { View } from 'react-native';
|
||||
|
||||
interface IVisitPlanTag {
|
||||
totalEscalationsCount: number;
|
||||
}
|
||||
|
||||
const VisitPlanTag = (props: IVisitPlanTag) => {
|
||||
const { totalEscalationsCount } = props;
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.absolute,
|
||||
styles.visitPlanContainer,
|
||||
totalEscalationsCount ? styles.top42 : {},
|
||||
]}
|
||||
>
|
||||
<TagPlaceholderIcon />
|
||||
<Text style={[GenericStyles.fontSize10, styles.visitPlanText]}>In visit plan</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
visitPlanContainer: {
|
||||
right: -4,
|
||||
top: 12,
|
||||
},
|
||||
top42: {
|
||||
top: 42,
|
||||
},
|
||||
visitPlanText: {
|
||||
position: 'absolute',
|
||||
left: 9,
|
||||
color: COLORS.TEXT.DARK,
|
||||
fontWeight: '500',
|
||||
lineHeight: 18
|
||||
},
|
||||
});
|
||||
|
||||
export default VisitPlanTag;
|
||||
@@ -410,7 +410,7 @@ const CasesList: React.FC<ICasesList> = ({
|
||||
{visitPlansUpdating ? (
|
||||
<Animated.View style={[styles.fillOverlay, { top: headerHeightValue }]} />
|
||||
) : null}
|
||||
<View style={[GenericStyles.fill, isPullToRefreshBannerVisible ? styles.mt64 : styles.mt16]}>
|
||||
<View style={[GenericStyles.fill, isPullToRefreshBannerVisible ? styles.mt64 : GenericStyles.mt12]}>
|
||||
{filteredCasesListWithCTA.length > 0 ? (
|
||||
<FlashList
|
||||
key={selectedTab}
|
||||
@@ -516,22 +516,22 @@ const styles = StyleSheet.create({
|
||||
opacity: 0.6,
|
||||
},
|
||||
list: {
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: 16,
|
||||
paddingTop: HEADER_HEIGHT_MAX,
|
||||
paddingBottom: 5,
|
||||
paddingBottom: 10,
|
||||
},
|
||||
visitPlanList: {
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: 16,
|
||||
paddingTop: VISIT_PLAN_HEADER_HEIGHT_MAX,
|
||||
paddingBottom: 10,
|
||||
},
|
||||
listWithQuickFilters: {
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: 16,
|
||||
paddingTop: HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS,
|
||||
paddingBottom: 5,
|
||||
paddingBottom: 10,
|
||||
},
|
||||
visitPlanListWithQuickFilters: {
|
||||
paddingHorizontal: 12,
|
||||
paddingHorizontal: 16,
|
||||
paddingTop: VISIT_PLAN_HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS,
|
||||
paddingBottom: 10,
|
||||
},
|
||||
|
||||
41
src/screens/allCases/Escalation/Escalation.tsx
Normal file
41
src/screens/allCases/Escalation/Escalation.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import EscalationItem from './EscalationItem';
|
||||
import { EscalationData } from '@screens/caseDetails/interface';
|
||||
|
||||
interface IEscalation {
|
||||
escalationData?: EscalationData;
|
||||
}
|
||||
|
||||
const Escalation = (props: IEscalation) => {
|
||||
const { escalationData } = props;
|
||||
const isActiveEscalationCase = Number(escalationData?.activeEscalationCount) > 0;
|
||||
const activeEscalationCount = Number(escalationData?.activeEscalationCount);
|
||||
const pastEscalationCount = Number(escalationData?.pastEscalationCount);
|
||||
const totalEscalationsCount = activeEscalationCount + pastEscalationCount;
|
||||
|
||||
if (!escalationData || totalEscalationsCount === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
{isActiveEscalationCase ? (
|
||||
<EscalationItem
|
||||
escalationText={`${activeEscalationCount} Active Escalation${
|
||||
activeEscalationCount === 1 ? '' : 's'
|
||||
}`}
|
||||
isActiveEscalationCase
|
||||
/>
|
||||
) : (
|
||||
<EscalationItem
|
||||
escalationText={`${pastEscalationCount} Past Escalation${
|
||||
pastEscalationCount === 1 ? '' : 's'
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Escalation;
|
||||
62
src/screens/allCases/Escalation/EscalationItem.tsx
Normal file
62
src/screens/allCases/Escalation/EscalationItem.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import FlagIcon from '@assets/icons/FlagIcon';
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
import Text from '@rn-ui-lib/components/Text';
|
||||
import { GenericStyles } from '@rn-ui-lib/styles';
|
||||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { View } from 'react-native';
|
||||
|
||||
interface IEscalationItem {
|
||||
escalationText: string;
|
||||
isActiveEscalationCase?: boolean;
|
||||
}
|
||||
|
||||
const EscalationItem = (props: IEscalationItem) => {
|
||||
const { escalationText, isActiveEscalationCase = false } = props;
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.escalationContainer,
|
||||
GenericStyles.row,
|
||||
GenericStyles.alignCenter,
|
||||
GenericStyles.w100,
|
||||
isActiveEscalationCase ? styles.redContainer : styles.yellowContainer,
|
||||
]}
|
||||
>
|
||||
<FlagIcon fillColor={isActiveEscalationCase ? COLORS.TEXT.RED : COLORS.TEXT.YELLOW_LIGHT} />
|
||||
<Text
|
||||
small
|
||||
style={[
|
||||
styles.escalationText,
|
||||
{ color: isActiveEscalationCase ? COLORS.TEXT.RED : COLORS.TEXT.YELLOW_LIGHT },
|
||||
]}
|
||||
>
|
||||
{escalationText}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
escalationContainer: {
|
||||
height: 30,
|
||||
paddingLeft: 12,
|
||||
paddingRight: 8,
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
borderWidth: 1,
|
||||
},
|
||||
escalationText: {
|
||||
marginLeft: 4,
|
||||
},
|
||||
redContainer: {
|
||||
borderColor: COLORS.BORDER.RED,
|
||||
backgroundColor: COLORS.BACKGROUND.RED,
|
||||
},
|
||||
yellowContainer: {
|
||||
borderColor: COLORS.BORDER.YELLOW,
|
||||
backgroundColor: COLORS.BACKGROUND.YELLOW_LIGHT,
|
||||
},
|
||||
});
|
||||
|
||||
export default EscalationItem;
|
||||
@@ -355,3 +355,29 @@ export interface IEscalationSummary {
|
||||
activeEscalationCount: number;
|
||||
recentEscalationDetails: IRecentEscalationDetails;
|
||||
}
|
||||
|
||||
export interface ICaseListItem {
|
||||
caseListItemDetailObj: ICaseItemCaseDetailObj;
|
||||
isTodoItem?: boolean;
|
||||
isCompleted?: boolean;
|
||||
shouldBatchAvatar?: boolean;
|
||||
allCasesView?: boolean;
|
||||
isAgentDashboard?: boolean;
|
||||
nearbyCaseView?: boolean;
|
||||
isVisitPlan?: boolean;
|
||||
}
|
||||
|
||||
export interface ICaseDetailKeyValue {
|
||||
title: string;
|
||||
value: string;
|
||||
showDot?: boolean;
|
||||
}
|
||||
|
||||
export interface ICaseStatus {
|
||||
caseListItemDetailObj: ICaseItemCaseDetailObj;
|
||||
isVisitPlan?: boolean;
|
||||
}
|
||||
|
||||
export interface IFeedbackStatus{
|
||||
caseListItemDetailObj: ICaseItemCaseDetailObj;
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
FeedbackStatus,
|
||||
IDocumentItem,
|
||||
INearbyCaseItemObj,
|
||||
PaymentStatus,
|
||||
} from '../caseDetails/interface';
|
||||
import {
|
||||
BOTTOM_TAB_ROUTES,
|
||||
@@ -28,6 +29,8 @@ import { logError } from '@components/utlis/errorUtils';
|
||||
import { setAgentsDocumentsData, setDocumentsData } from '@reducers/documentsSlice';
|
||||
import { useWindowDimensions } from 'react-native';
|
||||
import { toast } from '@rn-ui-lib/components/toast';
|
||||
import { TagVariant } from '@rn-ui-lib/components/Tag';
|
||||
import { COLORS } from '@rn-ui-lib/colors';
|
||||
|
||||
export const getAttemptedList = (
|
||||
filteredCasesList: ICaseItem[],
|
||||
@@ -322,3 +325,22 @@ export const calculateBottomSheetHeight = (rowLength = 0) => {
|
||||
const dynamicHeight = ((rowLength * rowHeight + headerOffset) / SCREEN_HEIGHT) * 100;
|
||||
return Math.min(dynamicHeight, maxHeightPercentage);
|
||||
};
|
||||
|
||||
export const paymentStatusMapping: Record<
|
||||
PaymentStatus,
|
||||
{ label: PaymentStatus | string; variant: TagVariant }
|
||||
> = {
|
||||
[PaymentStatus.Paid]: { label: PaymentStatus.Paid, variant: TagVariant.success },
|
||||
[PaymentStatus['Partially Paid']]: {
|
||||
label: PaymentStatus['Partially Paid'],
|
||||
variant: TagVariant.yellow,
|
||||
},
|
||||
[PaymentStatus.Unpaid]: { label: PaymentStatus.Unpaid, variant: TagVariant.alert },
|
||||
[PaymentStatus.Closed]: { label: PaymentStatus.Closed, variant: TagVariant.error },
|
||||
};
|
||||
|
||||
export const feedbackStatusColorMapping = {
|
||||
green: COLORS.TEXT.GREEN,
|
||||
red: COLORS.TEXT.RED,
|
||||
gray: COLORS.TEXT.BLACK,
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ import { COLORS } from '@rn-ui-lib/colors';
|
||||
import { syncActiveCallDetails } from '@actions/callRecordingActions';
|
||||
import { getUngroupedAddress } from '@actions/addressGeolocationAction';
|
||||
import EscalationsSection from '@screens/escalations/EscalationsSection';
|
||||
import TopFeedbacks from './feedback/TopFeedbacks';
|
||||
|
||||
interface ICaseDetails {
|
||||
route: {
|
||||
@@ -59,7 +60,6 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (caseId) dispatch(setSelectedCaseId(caseId));
|
||||
dispatch(getFeedbackHistory(loanAccountNumber));
|
||||
return () => {
|
||||
dispatch(setSelectedCaseId(''));
|
||||
};
|
||||
@@ -130,6 +130,7 @@ const CollectionCaseDetails: React.FC<ICaseDetails> = (props) => {
|
||||
) : null}
|
||||
<EmiDetailsSection caseId={caseId} />
|
||||
<CollectMoneySection caseId={caseId} />
|
||||
<TopFeedbacks caseId={caseId} />
|
||||
<FeedbackDetailsSection caseId={caseId} />
|
||||
</Animated.View>
|
||||
</ScrollView>
|
||||
|
||||
@@ -35,12 +35,12 @@ import Filters, { TFilterOptions } from '../../../components/filters/Filters';
|
||||
import { _map } from '../../../../RN-UI-LIB/src/utlis/common';
|
||||
import ChevronDown from '../../../assets/icons/ChevronDown';
|
||||
import ChevronUp from '../../../assets/icons/ChevronUp';
|
||||
import { PAST_FEEDBACK_PAGE_SIZE } from '@screens/caseDetails/feedback/pastFeedbackCommon';
|
||||
|
||||
const FEEDBACK_PAGE_TITLE = 'All feedbacks';
|
||||
const ADDRESS_FEEDBACK_PAGE_TITLE = 'Address feedback';
|
||||
|
||||
const SCROLL_LAYOUT_OFFSET = 10;
|
||||
const FEEDBACK_PER_PAGE = 5;
|
||||
|
||||
interface IFeedbackDetailContainer {
|
||||
route: {
|
||||
@@ -123,7 +123,7 @@ const FeedbackDetailContainer: React.FC<IFeedbackDetailContainer> = ({ route: ro
|
||||
{
|
||||
loan_account_number: loanAccountNumber,
|
||||
page_no: currentPage - 1,
|
||||
page_size: FEEDBACK_PER_PAGE,
|
||||
page_size: PAST_FEEDBACK_PAGE_SIZE,
|
||||
customerRecahble: false,
|
||||
addressReferenceIds,
|
||||
},
|
||||
|
||||
@@ -16,6 +16,8 @@ import { shareToWhatsapp } from '../../../services/FeedbackWhatsApp';
|
||||
import { CaseDetailStackEnum } from '../CaseDetailStack';
|
||||
import { feedbackTypeIcon } from '@screens/caseDetails/feedback/FeedbackDetailItem';
|
||||
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';
|
||||
|
||||
interface IFeedbackListItem {
|
||||
feedbackItem: IFeedback | IUnSyncedFeedbackItem;
|
||||
@@ -41,7 +43,9 @@ const FeedbackListItem: React.FC<IFeedbackListItem> = ({
|
||||
};
|
||||
navigateToScreen(CaseDetailStackEnum.PAST_FEEDBACK_DETAIL, {
|
||||
...commonParams,
|
||||
pageNo: isTopFeedbackItem ? (feedbackItem as IFeedback)?.offset : 1,
|
||||
pageNo: isTopFeedbackItem
|
||||
? Math.ceil(((feedbackItem as IFeedback)?.orderOffset ?? 0) / PAST_FEEDBACK_PAGE_SIZE)
|
||||
: 1,
|
||||
});
|
||||
};
|
||||
const [isWhastappSendLoading, setIsWhatsappSendLoading] = useState(false);
|
||||
@@ -98,6 +102,12 @@ const FeedbackListItem: React.FC<IFeedbackListItem> = ({
|
||||
style={[
|
||||
styles.textHeading,
|
||||
feedbackItem.type === FEEDBACK_TYPE.GEN_AI_BOT_FIELD ? styles.capitalized : null,
|
||||
{
|
||||
color:
|
||||
feedbackStatusColorMapping[
|
||||
feedbackItem?.color as keyof typeof feedbackStatusColorMapping
|
||||
] ?? COLORS.TEXT.BLACK,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{sanitizeString(feedbackItem.interactionStatus)}
|
||||
@@ -116,7 +126,7 @@ const FeedbackListItem: React.FC<IFeedbackListItem> = ({
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
{feedbackItem?.type === FEEDBACK_TYPE.FIELD_VISIT ? (
|
||||
{feedbackItem?.type === FEEDBACK_TYPE.FIELD_VISIT && !isTopFeedbackItem ? (
|
||||
<TouchableOpacity
|
||||
style={styles.ShareButton}
|
||||
hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }}
|
||||
@@ -186,7 +196,7 @@ const styles = StyleSheet.create({
|
||||
paddingHorizontal: 0,
|
||||
},
|
||||
topFeedbackItem: {
|
||||
paddingTop: 40,
|
||||
paddingTop: 36,
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
currentMonthFeedback: {
|
||||
|
||||
@@ -20,14 +20,14 @@ const TopFeedbacks = (props: ITopFeedbacks) => {
|
||||
const caseDetail = useAppSelector((state: RootState) => state.allCases.caseDetails[caseId]) || {};
|
||||
const { loanAccountNumber } = caseDetail || {};
|
||||
const feedbackList = useAppSelector(
|
||||
(state: RootState) => state.topFeedbacks?.[loanAccountNumber as string]?.feedbacks || []
|
||||
(state: RootState) => state.topFeedbacks?.[caseId as string]?.feedbacks || []
|
||||
);
|
||||
const isLoading = useAppSelector(
|
||||
(state: RootState) => state.topFeedbacks?.[loanAccountNumber as string]?.isLoading || false
|
||||
(state: RootState) => state.topFeedbacks?.[caseId as string]?.isLoading || false
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getTopFeedbacks(loanAccountNumber));
|
||||
dispatch(getTopFeedbacks(caseId));
|
||||
}, []);
|
||||
|
||||
if (!feedbackList?.length) return null;
|
||||
@@ -60,7 +60,7 @@ const TopFeedbacks = (props: ITopFeedbacks) => {
|
||||
</View>
|
||||
<View style={[GenericStyles.whiteBackground, GenericStyles.br8, getShadowStyle(2)]}>
|
||||
<SuspenseLoader
|
||||
loading={isLoading && !feedbackList?.length}
|
||||
loading={isLoading}
|
||||
fallBack={<FeedbackLoading arrayLength={2} isTopFeedbackItem />}
|
||||
>
|
||||
{feedbackList?.map((feedbackItem: any, idx: number) => (
|
||||
|
||||
1
src/screens/caseDetails/feedback/pastFeedbackCommon.ts
Normal file
1
src/screens/caseDetails/feedback/pastFeedbackCommon.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const PAST_FEEDBACK_PAGE_SIZE = 5;
|
||||
@@ -262,6 +262,11 @@ export interface EmploymentDetails {
|
||||
employerName: string;
|
||||
}
|
||||
|
||||
export interface FeedbackStatusObj {
|
||||
status: string;
|
||||
color: string
|
||||
}
|
||||
|
||||
export interface CaseDetail {
|
||||
id: string;
|
||||
allocationReferenceId?: string;
|
||||
@@ -316,7 +321,9 @@ export interface CaseDetail {
|
||||
employmentDetail?: EmploymentDetails;
|
||||
unpaidDays?: number;
|
||||
addressStringType?: string;
|
||||
escalationData ?: escalationData;
|
||||
currentMonthFeedbackStatus?: FeedbackStatusObj;
|
||||
lastMonthFeedbackStatus?:FeedbackStatusObj;
|
||||
escalationData ?: EscalationData;
|
||||
daysTillDeallocation: number;
|
||||
}
|
||||
|
||||
@@ -325,7 +332,7 @@ export interface recentEscalationDetails {
|
||||
customerVoice : string;
|
||||
}
|
||||
|
||||
export interface escalationData {
|
||||
export interface EscalationData {
|
||||
activeEscalationCount : number;
|
||||
pastEscalationCount : number;
|
||||
recentEscalationDetails : recentEscalationDetails;
|
||||
|
||||
@@ -78,6 +78,7 @@ export interface IFeedback {
|
||||
interactionStatus: InteractionStatuses;
|
||||
tagTitle?: string;
|
||||
offset?: number;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export interface IUnSyncedFeedbackItem {
|
||||
@@ -85,6 +86,7 @@ export interface IUnSyncedFeedbackItem {
|
||||
createdAt: string;
|
||||
isSynced: boolean;
|
||||
type?: FEEDBACK_TYPE;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export const CALLING_FEEDBACKS = [FEEDBACK_TYPE.CALL_BRIDGE, FEEDBACK_TYPE.SELF_CALL];
|
||||
|
||||
Reference in New Issue
Block a user