Files
address-verification-app/src/screens/allCases/ListItem.tsx
2024-02-07 21:30:13 +05:30

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);