375 lines
11 KiB
TypeScript
375 lines
11 KiB
TypeScript
import React, { memo, useEffect, 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,
|
|
toggleNewlyAddedCase,
|
|
} from '../../reducer/allCasesSlice';
|
|
import CaseItemAvatar from './CaseItemAvatar';
|
|
import {
|
|
CaseAllocationType,
|
|
CaseStatuses,
|
|
CaseTypeMap,
|
|
displayStatuses,
|
|
InteractionStatuses,
|
|
TaskTitleUIMapping,
|
|
ICaseItemAvatarCaseDetailObj,
|
|
ICaseItemCaseDetailObj,
|
|
} from './interface';
|
|
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
|
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
|
|
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
|
import Tag, { TagVariant } from '../../../RN-UI-LIB/src/components/Tag';
|
|
import { formatAmount } from '../../../RN-UI-LIB/src/utlis/amount';
|
|
import OnboardingIcon from '../../../RN-UI-LIB/src/Icons/OnboardingIcon';
|
|
import RoundCheckIcon from '../../../RN-UI-LIB/src/Icons/RoundCheckIcon';
|
|
import { getDocumentList } 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 { PaymentStatus } from '../caseDetails/interface';
|
|
import relativeDistanceFormatter from '@screens/addressGeolocation/utils/relativeDistanceFormatter';
|
|
import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack';
|
|
import { PageRouteEnum } from '@screens/auth/ProtectedRouter';
|
|
|
|
interface IListItem {
|
|
caseListItemDetailObj: ICaseItemCaseDetailObj;
|
|
isTodoItem?: boolean;
|
|
isCompleted?: boolean;
|
|
shouldBatchAvatar?: boolean;
|
|
allCasesView?: boolean;
|
|
isAgentDashboard?: boolean;
|
|
nearbyCaseView?: boolean;
|
|
}
|
|
|
|
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 },
|
|
};
|
|
|
|
const ListItem: React.FC<IListItem> = (props) => {
|
|
const {
|
|
caseListItemDetailObj,
|
|
isCompleted,
|
|
isTodoItem,
|
|
shouldBatchAvatar,
|
|
allCasesView,
|
|
nearbyCaseView,
|
|
} = props;
|
|
const {
|
|
id: caseId,
|
|
isIntermediateOrSelectedTodoCaseItem,
|
|
caseStatus,
|
|
caseType,
|
|
collectionTag,
|
|
paymentStatus,
|
|
dpdBucket,
|
|
pinRank,
|
|
isSynced,
|
|
isNewlyAdded,
|
|
interactionStatus,
|
|
caseVerdict,
|
|
totalOverdueAmount,
|
|
distanceInKm,
|
|
} = caseListItemDetailObj;
|
|
|
|
const isCollectionCaseType = caseType === CaseAllocationType.COLLECTION_CASE;
|
|
const isVisitPlanStatusLocked = useAppSelector(
|
|
(state) => state.user?.lock?.visitPlanStatus === VisitPlanStatus.LOCKED
|
|
);
|
|
const isTeamLead = useAppSelector((state) => state.user.isTeamLead);
|
|
|
|
const dispatch = useAppDispatch();
|
|
|
|
useEffect(() => {
|
|
if (isNewlyAdded) {
|
|
setTimeout(() => dispatch(toggleNewlyAddedCase(caseId)), 1000);
|
|
}
|
|
}, []);
|
|
|
|
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 () => {
|
|
await 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,
|
|
});
|
|
}
|
|
if (isCollectionCaseType) {
|
|
navigateToScreen(PageRouteEnum.CASE_DETAIL_STACK, {
|
|
screen: CaseDetailStackEnum.COLLECTION_CASE_DETAIL,
|
|
params: { caseId },
|
|
});
|
|
} else {
|
|
navigateToScreen('caseDetail', { caseId });
|
|
}
|
|
};
|
|
|
|
const isCaseSelected = !isTodoItem && !!isIntermediateOrSelectedTodoCaseItem;
|
|
|
|
const address =
|
|
caseListItemDetailObj?.currentTask?.metadata?.addressLine ||
|
|
caseListItemDetailObj?.addressString;
|
|
const phoneNumber = caseListItemDetailObj?.currentTask?.metadata?.primaryPhoneNumber;
|
|
const taskTitle =
|
|
caseListItemDetailObj?.currentTask?.taskType || caseListItemDetailObj?.currentTask?.title;
|
|
|
|
const displayAddress = address || phoneNumber;
|
|
|
|
const displayCaseStatus = displayStatuses[caseStatus] || displayStatuses[caseVerdict];
|
|
|
|
const customerName =
|
|
caseListItemDetailObj.customerInfo?.name ||
|
|
caseListItemDetailObj.customerInfo?.customerName ||
|
|
caseListItemDetailObj.customerName;
|
|
|
|
const caseInteractionStatus = InteractionStatuses[interactionStatus] ?? interactionStatus;
|
|
|
|
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;
|
|
|
|
return (
|
|
<Pressable onPress={handleCaseClick}>
|
|
<View
|
|
style={[
|
|
GenericStyles.row,
|
|
styles.listItem,
|
|
getShadowStyle(2),
|
|
{
|
|
backgroundColor: isNewlyAdded
|
|
? COLORS.BACKGROUND.ORANGE
|
|
: isCaseSelected
|
|
? COLORS.BACKGROUND.SILVER
|
|
: COLORS.BACKGROUND.PRIMARY,
|
|
},
|
|
]}
|
|
>
|
|
<CaseItemAvatar
|
|
caseDetailObj={getCaseItemAvatarCaseDetailObj}
|
|
shouldBatchAvatar={shouldBatchAvatar}
|
|
/>
|
|
{showVisitPlanBtn ? (
|
|
<Pressable onPress={handleAvatarClick} style={styles.selectBtn}>
|
|
<RoundCheckIcon focused={isCaseSelected} />
|
|
</Pressable>
|
|
) : null}
|
|
{isCaseItemPinnedMainView && !caseCompleted && (
|
|
<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.tag}>
|
|
{isCollectionCaseType ? (
|
|
<View style={[GenericStyles.row, GenericStyles.alignCenter]}>
|
|
{paymentStatus ? (
|
|
<View>
|
|
<Tag
|
|
variant={paymentStatusMapping[paymentStatus]?.variant || TagVariant.alert}
|
|
text={(paymentStatusMapping[paymentStatus]?.label || paymentStatus) as string}
|
|
/>
|
|
</View>
|
|
) : null}
|
|
{collectionTag ? (
|
|
<View style={paymentStatus && GenericStyles.ml8}>
|
|
<Tag variant={TagVariant.gray} text={collectionTag} />
|
|
</View>
|
|
) : null}
|
|
</View>
|
|
) : (
|
|
<Tag
|
|
variant={TagVariant.teal}
|
|
text={CaseTypeMap[CaseAllocationType.ADDRESS_VERIFICATION_CASE]}
|
|
tagIcon={<OnboardingIcon />}
|
|
/>
|
|
)}
|
|
</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>
|
|
) : isCollectionCaseType ? (
|
|
<Text dark bold ellipsizeMode="tail" style={styles.address}>
|
|
<Text light>{displayAddress}</Text>
|
|
</Text>
|
|
) : null}
|
|
{isCollectionCaseType ? (
|
|
<View>
|
|
<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>
|
|
) : displayCaseStatus ? (
|
|
<View style={styles.borderTop}>
|
|
{displayCaseStatus ? (
|
|
<Text small style={styles.caseStatusText} bold>
|
|
{displayCaseStatus.label}
|
|
</Text>
|
|
) : null}
|
|
</View>
|
|
) : null}
|
|
</View>
|
|
</View>
|
|
</Pressable>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
listItem: {
|
|
padding: 12,
|
|
borderRadius: 8,
|
|
marginVertical: 6,
|
|
position: 'relative',
|
|
},
|
|
avatarContainer: {
|
|
height: 40,
|
|
width: 48,
|
|
},
|
|
caseItemInfo: {
|
|
flexBasis: '82%',
|
|
marginLeft: 12,
|
|
},
|
|
address: {
|
|
fontSize: 13,
|
|
},
|
|
tag: {
|
|
marginBottom: 8,
|
|
},
|
|
caseStatusText: {
|
|
color: COLORS.BACKGROUND.SECONDARY,
|
|
},
|
|
borderTop: {
|
|
borderTopWidth: 1,
|
|
borderTopColor: COLORS.BORDER.PRIMARY,
|
|
marginTop: 13,
|
|
paddingTop: 8,
|
|
},
|
|
selectBtn: {
|
|
position: 'absolute',
|
|
paddingTop: 12,
|
|
right: 0,
|
|
paddingRight: 12,
|
|
width: 80,
|
|
height: 80,
|
|
display: 'flex',
|
|
justifyContent: 'flex-start',
|
|
alignItems: 'flex-end',
|
|
fill: 1,
|
|
zIndex: 100,
|
|
},
|
|
visitPlanContainer: {
|
|
right: 0,
|
|
top: 0,
|
|
padding: 8,
|
|
backgroundColor: COLORS.BACKGROUND.BLUE,
|
|
borderBottomLeftRadius: 4,
|
|
borderTopRightRadius: 4,
|
|
},
|
|
visitPlanText: {
|
|
color: COLORS.TEXT.BLUE,
|
|
},
|
|
distanceContainer: {
|
|
right: 0,
|
|
top: 0,
|
|
padding: 8,
|
|
backgroundColor: COLORS.BACKGROUND.SILVER,
|
|
borderBottomLeftRadius: 4,
|
|
borderTopRightRadius: 4,
|
|
},
|
|
distanceText: {
|
|
color: COLORS.TEXT.BLACK,
|
|
},
|
|
});
|
|
|
|
export default memo(ListItem);
|