TP-31976 | Merge branch 'master' of github.cmd.navi-tech.in:medici/Address-Verification-App into bug/TP-31976
This commit is contained in:
@@ -81,7 +81,11 @@ export const syncCaseDetail =
|
||||
const offlineImageIdList = getOfflineImageId(payload);
|
||||
const url = getApiUrl(ApiKeys.FEEDBACK);
|
||||
axiosInstance
|
||||
.post(url, payload)
|
||||
.post(url, payload, {
|
||||
params: {
|
||||
apiVersion: 2,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
const caseType = payload.caseType;
|
||||
dispatch(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { AxiosResponse } from 'axios';
|
||||
import axiosInstance, { ApiKeys, getApiUrl } from '../components/utlis/apiHelper';
|
||||
import { logError } from '../components/utlis/errorUtils';
|
||||
import { VisitPlanStatus } from '../reducer/userSlice';
|
||||
import { FilterResponse } from '../screens/allCases/interface';
|
||||
import { CaseDetail } from '../screens/caseDetails/interface';
|
||||
|
||||
@@ -30,11 +32,18 @@ export interface ISyncCaseIdPayload {
|
||||
cases: ICases[];
|
||||
}
|
||||
|
||||
interface ICasesSyncStatus {
|
||||
syncStatus: SyncStatus;
|
||||
visitPlanStatus: VisitPlanStatus;
|
||||
}
|
||||
|
||||
export const getCasesSyncStatus = async (userReferenceId: string) => {
|
||||
try {
|
||||
const url = getApiUrl(ApiKeys.CASES_SYNC_STATUS, {}, { userReferenceId });
|
||||
const response = await axiosInstance.get(url, { headers: { donotHandleError: true } });
|
||||
return response?.data?.syncStatus;
|
||||
const response: AxiosResponse<ICasesSyncStatus> = await axiosInstance.get(url, {
|
||||
headers: { donotHandleError: true },
|
||||
});
|
||||
return response?.data;
|
||||
} catch (err) {
|
||||
logError(err as Error, 'Error getting sync status');
|
||||
}
|
||||
|
||||
114
src/assets/icons/GeneratingVisitPlanIcon.tsx
Normal file
114
src/assets/icons/GeneratingVisitPlanIcon.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import * as React from 'react';
|
||||
import Svg, { Path, Mask, G, SvgProps } from 'react-native-svg';
|
||||
|
||||
const GeneratingVisitPlan = (props: SvgProps) => {
|
||||
return (
|
||||
<Svg
|
||||
width={160}
|
||||
height={160}
|
||||
viewBox="0 0 160 160"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<Path
|
||||
d="M123.716 25.406H31.192c-.695 0-1.258.564-1.258 1.259v125.273c0 .695.563 1.259 1.258 1.259h92.524c.695 0 1.258-.564 1.258-1.259V26.665c0-.695-.563-1.259-1.258-1.259z"
|
||||
fill="#0085FF"
|
||||
stroke="#12183E"
|
||||
strokeWidth={0.245714}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<Path
|
||||
d="M120.286 31.584v111.785c0 .509-.446.922-.997.922H36.702c-.551 0-.997-.413-.997-.922V31.584c0-.51.446-.922.997-.922h82.587c.551 0 .997.413.997.922z"
|
||||
fill="#fff"
|
||||
stroke="#12183E"
|
||||
strokeWidth={0.5}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<Path
|
||||
d="M47.034 30.688s-1.7 2.256-1.486 5.26a1.38 1.38 0 001.38 1.284l59.786-.206c1.002-1.191.606-3.826.333-6.342H47.034v.004z"
|
||||
fill="#545454"
|
||||
/>
|
||||
<Path
|
||||
d="M79.133 22.562a1.678 1.678 0 11-3.333-.29 1.674 1.674 0 001.654 1.388c.83 0 1.515-.597 1.654-1.389.017.093.025.19.025.29z"
|
||||
fill="#12183E"
|
||||
/>
|
||||
<Path
|
||||
d="M101.649 26.665H89.52c0-6.662-5.4-12.061-12.061-12.061-6.663 0-12.062 5.399-12.062 12.061H53.268a5.63 5.63 0 00-5.63 5.63v1.882c0 .37.298.67.669.67h58.312c.37 0 .669-.3.669-.67v-1.881a5.63 5.63 0 00-5.631-5.631h-.008zm-25.844-4.394a1.679 1.679 0 113.306.58 1.679 1.679 0 01-3.306-.58z"
|
||||
fill="#F7F7F7"
|
||||
stroke="#12183E"
|
||||
strokeWidth={0.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<Path
|
||||
d="M101.649 26.665H89.52c0-6.662-5.4-12.061-12.061-12.061-6.663 0-12.062 5.399-12.062 12.061H53.268a5.63 5.63 0 00-5.63 5.63v1.882c0 .37.298.67.669.67h58.312c.37 0 .669-.3.669-.67v-1.881a5.63 5.63 0 00-5.631-5.631h-.008zm-25.844-4.394a1.679 1.679 0 113.306.58 1.679 1.679 0 01-3.306-.58v0z"
|
||||
stroke="#12183E"
|
||||
strokeWidth={0.5}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<Path
|
||||
d="M53.615 55.512h-9.06a1.17 1.17 0 00-1.17 1.17v8.206c0 .646.523 1.17 1.17 1.17h9.06a1.17 1.17 0 001.17-1.17v-8.206a1.17 1.17 0 00-1.17-1.17zM53.615 95.09h-9.061a1.17 1.17 0 00-1.17 1.17v8.206c0 .646.524 1.17 1.17 1.17h9.06a1.17 1.17 0 001.17-1.17V96.26a1.17 1.17 0 00-1.17-1.17zM53.615 75.303h-9.061a1.17 1.17 0 00-1.17 1.17v8.206c0 .646.524 1.17 1.17 1.17h9.06a1.17 1.17 0 001.17-1.17v-8.206a1.17 1.17 0 00-1.17-1.17zM53.615 114.881h-9.061a1.17 1.17 0 00-1.17 1.17v8.206c0 .646.524 1.17 1.17 1.17h9.06a1.17 1.17 0 001.17-1.17v-8.206a1.17 1.17 0 00-1.17-1.17z"
|
||||
fill="#fff"
|
||||
stroke="#12183E"
|
||||
strokeWidth={0.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<Path
|
||||
d="M61.652 117.285h25.744v2.341H61.652v-2.341zM61.653 123.135h45.637v2.34H61.653v-2.34zM61.652 57.488h25.744v2.34H61.652v-2.34zM61.653 63.34h45.637v2.34H61.653v-2.34zM61.652 77.031h25.744v2.34H61.652v-2.34zM61.653 82.88h45.637v2.341H61.653v-2.34zM61.652 96.572h25.744v2.34H61.652v-2.34zM61.653 102.424h45.637v2.34H61.653v-2.34z"
|
||||
fill="#E1E1E1"
|
||||
/>
|
||||
<Path
|
||||
d="M52.717 58.613l-4.574 4.574-2.287-2.287M52.717 78.367l-4.574 4.574-2.287-2.287M52.717 98.12l-4.574 4.573-2.287-2.287M52.717 117.869l-4.574 4.574-2.287-2.287"
|
||||
stroke="#C5C5C5"
|
||||
strokeWidth={1.17657}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<Path
|
||||
d="M38.458 27.68c-10.449-1.942-11.67-3.306-12.956-14.213-1.83 10.828-3.14 12.087-13.656 13.364 10.449 1.941 11.67 3.305 12.956 14.212 1.83-10.828 3.141-12.087 13.656-13.364zM13.694 15.376c-4.366-.814-4.876-1.38-5.414-5.939-.764 4.523-1.313 5.05-5.706 5.584 4.366.813 4.876 1.38 5.414 5.938.765-4.523 1.313-5.05 5.706-5.583z"
|
||||
fill="#fff"
|
||||
stroke="#12183E"
|
||||
strokeWidth={0.35}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<Path
|
||||
d="M135.589 103.557c-12.835-6.789-28.743-1.892-35.532 10.942-6.789 12.834-1.893 28.742 10.942 35.531 12.834 6.789 28.742 1.893 35.531-10.941 6.789-12.835 1.893-28.742-10.941-35.532z"
|
||||
fill="#fff"
|
||||
stroke="#12183E"
|
||||
strokeWidth={0.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<Mask
|
||||
id="a"
|
||||
style={{
|
||||
maskType: 'alpha',
|
||||
}}
|
||||
maskUnits="userSpaceOnUse"
|
||||
x={106}
|
||||
y={110}
|
||||
width={33}
|
||||
height={33}
|
||||
>
|
||||
<Path fill="#D9D9D9" d="M107 110.5H139V142.5H107z" />
|
||||
</Mask>
|
||||
<G mask="url(#a)">
|
||||
<Path
|
||||
d="M125.835 114.132c2.822.658 5.139 2.119 6.951 4.383 1.812 2.265 2.718 4.926 2.718 7.985 0 1.938-.437 3.766-1.311 5.485-.873 1.718-2.112 3.23-3.715 4.536h3.171a.813.813 0 01.837.838.816.816 0 01-.237.6.816.816 0 01-.6.237h-5.111c-.301 0-.55-.098-.747-.295a1.012 1.012 0 01-.295-.747v-5.111c0-.241.079-.441.237-.6a.816.816 0 01.6-.237.813.813 0 01.838.837v3.352c1.484-1.127 2.631-2.46 3.442-3.999a10.359 10.359 0 001.216-4.896c0-2.636-.775-4.931-2.324-6.885-1.549-1.954-3.558-3.223-6.027-3.807a.844.844 0 01-.464-.312.83.83 0 01-.185-.518c0-.294.101-.524.302-.69a.781.781 0 01.704-.156zm-5.67 24.735c-2.822-.657-5.139-2.116-6.951-4.376-1.812-2.26-2.718-4.924-2.718-7.991 0-1.938.437-3.764 1.31-5.478.874-1.715 2.113-3.225 3.716-4.531h-3.171a.816.816 0 01-.6-.237.816.816 0 01-.237-.6.813.813 0 01.837-.838h5.11a1.01 1.01 0 011.043 1.043v5.11a.813.813 0 01-.837.838.81.81 0 01-.838-.838v-3.351c-1.487 1.104-2.635 2.429-3.444 3.976a10.437 10.437 0 00-1.214 4.906c0 2.627.774 4.92 2.322 6.879 1.547 1.958 3.557 3.229 6.029 3.813a.848.848 0 01.464.312.83.83 0 01.185.518c0 .294-.101.524-.302.689a.775.775 0 01-.704.156z"
|
||||
fill="#0276FE"
|
||||
/>
|
||||
</G>
|
||||
<Path
|
||||
d="M132.335 57.488h27.664M4.316 80.06h15.513M0 153.254h158.025"
|
||||
stroke="#12183E"
|
||||
strokeWidth={0.5}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</Svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default GeneratingVisitPlan;
|
||||
32
src/assets/icons/NotificationVisitPlanIcon.tsx
Normal file
32
src/assets/icons/NotificationVisitPlanIcon.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import * as React from 'react';
|
||||
import Svg, { Rect, Path, SvgProps } from 'react-native-svg';
|
||||
|
||||
function NotificationVisitPlan(props: SvgProps) {
|
||||
return (
|
||||
<Svg
|
||||
width={36}
|
||||
height={36}
|
||||
viewBox="0 0 36 36"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<Rect width={36} height={36} rx={18} fill="#E6F1FF" />
|
||||
<Path
|
||||
d="M16.22 10.59H10.8a.2.2 0 00-.2.2v5.42c0 .11.09.2.2.2h5.42a.2.2 0 00.2-.2v-5.42a.2.2 0 00-.2-.2zM16.22 19.58H10.8a.2.2 0 00-.2.2v5.42c0 .11.09.2.2.2h5.42a.2.2 0 00.2-.2v-5.42a.2.2 0 00-.2-.2z"
|
||||
fill="#0276FE"
|
||||
stroke="#0276FE"
|
||||
strokeWidth={1.2}
|
||||
strokeMiterlimit={10}
|
||||
/>
|
||||
<Path
|
||||
d="M19.59 22.66l1.739 1.738a.2.2 0 00.282 0l3.95-3.949M19.59 13.66l1.739 1.74a.2.2 0 00.282 0l3.95-3.94"
|
||||
stroke="#0276FE"
|
||||
strokeWidth={1.2}
|
||||
strokeMiterlimit={10}
|
||||
/>
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default NotificationVisitPlan;
|
||||
@@ -494,8 +494,12 @@ export const PrefixJpegBase64Image = getPrefixBase64Image(MimeType['image/jpeg']
|
||||
|
||||
export const HEADER_HEIGHT_MAX = 132;
|
||||
export const HEADER_HEIGHT_MIN = 80;
|
||||
export const VISIT_PLAN_HEADER_HEIGHT_MAX = 183;
|
||||
export const VISIT_PLAN_HEADER_HEIGHT_MIN = 130;
|
||||
export const HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS = HEADER_HEIGHT_MAX + 50;
|
||||
export const HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS = HEADER_HEIGHT_MIN + 50;
|
||||
export const VISIT_PLAN_HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS = VISIT_PLAN_HEADER_HEIGHT_MAX + 50;
|
||||
export const VISIT_PLAN_HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS = VISIT_PLAN_HEADER_HEIGHT_MIN + 50;
|
||||
export const HEADER_SCROLL_DISTANCE = (HEADER_HEIGHT_MAX - HEADER_HEIGHT_MIN) * 2;
|
||||
export const HEADER_SCROLL_DISTANCE_WITH_QUICK_FILTERS =
|
||||
(HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS - HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS) * 2;
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
import { getSyncCaseIds } from '../components/utlis/firebaseFallbackUtils';
|
||||
import { syncCasesByFallback } from '../reducer/allCasesSlice';
|
||||
import { MILLISECONDS_IN_A_MINUTE } from '../../RN-UI-LIB/src/utlis/common';
|
||||
import { VisitPlanStatus, setLockData } from '../reducer/userSlice';
|
||||
|
||||
export enum FOREGROUND_TASKS {
|
||||
GEOLOCATION = 'GEOLOCATION',
|
||||
@@ -81,8 +82,10 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
|
||||
const handleGetCaseSyncStatus = async () => {
|
||||
try {
|
||||
const syncStatus = await getCasesSyncStatus(referenceId);
|
||||
LAST_SYNC_STATUS = syncStatus;
|
||||
const { syncStatus, visitPlanStatus } = (await getCasesSyncStatus(referenceId)) ?? {};
|
||||
if (syncStatus) {
|
||||
LAST_SYNC_STATUS = syncStatus;
|
||||
}
|
||||
if (syncStatus === SyncStatus.SEND_CASES) {
|
||||
const cases = getSyncCaseIds([...pendingList, ...pinnedList]);
|
||||
const payload: ISyncCaseIdPayload = {
|
||||
@@ -96,6 +99,13 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
dispatch(syncCasesByFallback(updatedDetails));
|
||||
}
|
||||
}
|
||||
if (visitPlanStatus) {
|
||||
dispatch(
|
||||
setLockData({
|
||||
visitPlanStatus,
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logError(e as Error, 'Error during fetching case sync status');
|
||||
}
|
||||
@@ -133,7 +143,8 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
|
||||
const handleAppStateChange = async (nextAppState: AppStateStatus) => {
|
||||
// App comes to foreground from background
|
||||
if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
|
||||
if (nextAppState === 'active') {
|
||||
handleGetCaseSyncStatus();
|
||||
UnstoppableService.start(tasks);
|
||||
if (bgTrackingTimeoutId.current) {
|
||||
clearTimeout(bgTrackingTimeoutId.current);
|
||||
@@ -169,7 +180,6 @@ const TrackingComponent: React.FC<ITrackingComponent> = ({ children }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (isOnline) {
|
||||
UnstoppableService.start(tasks);
|
||||
AppState.addEventListener('change', handleAppStateChange);
|
||||
} else {
|
||||
if (UnstoppableService.isRunning()) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import firestore, { FirebaseFirestoreTypes } from '@react-native-firebase/firestore';
|
||||
import { RootState } from '../store/store';
|
||||
import { useAppDispatch, useAppSelector } from '.';
|
||||
@@ -8,15 +8,13 @@ import { FirestoreUpdateTypes } from '../common/Constants';
|
||||
import { toast } from '../../RN-UI-LIB/src/components/toast';
|
||||
import auth from '@react-native-firebase/auth';
|
||||
import { updateAvTemplateData, updateCollectionTemplateData } from '../reducer/caseReducer';
|
||||
import { GLOBAL, setGlobalUserData } from '../constants/Global';
|
||||
import { setAuthData } from '../reducer/userSlice';
|
||||
import { ILockData, setLockData, VisitPlanStatus } from '../reducer/userSlice';
|
||||
import { setFilters } from '../reducer/filtersSlice';
|
||||
import { FormTemplateV1 } from '../types/template.types';
|
||||
import { ToastMessages } from '../screens/allCases/constants';
|
||||
import { setForceUninstallData } from '../reducer/metadataSlice';
|
||||
import { logError } from '../components/utlis/errorUtils';
|
||||
import { GenericFunctionArgs } from '../common/GenericTypes';
|
||||
import axiosInstance, { ApiKeys, getApiUrl } from '../components/utlis/apiHelper';
|
||||
|
||||
export interface CaseUpdates {
|
||||
updateType: string;
|
||||
@@ -34,11 +32,18 @@ const isUserSignedIn = () => {
|
||||
};
|
||||
|
||||
const useFirestoreUpdates = () => {
|
||||
const reduxStoreData = useAppSelector((state: RootState) => state);
|
||||
const {
|
||||
user: { user, isLoggedIn, sessionDetails },
|
||||
user: { user, isLoggedIn, sessionDetails, lock },
|
||||
allCases: { caseDetails, casesList, loading },
|
||||
} = reduxStoreData;
|
||||
} = useAppSelector((state: RootState) => ({
|
||||
user: state.user,
|
||||
allCases: state.allCases,
|
||||
}));
|
||||
const lockRef = useRef<ILockData | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
lockRef.current = lock;
|
||||
}, [lock]);
|
||||
|
||||
let casesUnsubscribe: GenericFunctionArgs;
|
||||
let avTemplateUnSubscriber: GenericFunctionArgs;
|
||||
@@ -46,6 +51,7 @@ const useFirestoreUpdates = () => {
|
||||
let configUnsubscribe: GenericFunctionArgs;
|
||||
let filterUnsubscribe: GenericFunctionArgs;
|
||||
let forceUninstallUnsubscribe: GenericFunctionArgs;
|
||||
let lockUnsubscribe: GenericFunctionArgs;
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
@@ -94,7 +100,13 @@ const useFirestoreUpdates = () => {
|
||||
}
|
||||
});
|
||||
const isInitialLoad = casesList.length === 0;
|
||||
dispatch(updateCaseDetailsFirestore({ caseUpdates, isInitialLoad }));
|
||||
dispatch(
|
||||
updateCaseDetailsFirestore({
|
||||
caseUpdates,
|
||||
isInitialLoad,
|
||||
isVisitPlanLocked: lockRef?.current?.visitPlanStatus === VisitPlanStatus.LOCKED,
|
||||
})
|
||||
);
|
||||
!isInitialLoad && showCaseUpdationToast(newlyAddedCases, deletedCases);
|
||||
};
|
||||
|
||||
@@ -133,6 +145,12 @@ const useFirestoreUpdates = () => {
|
||||
const filterResponse = snapshot.data();
|
||||
filterResponse?.filterComponentList && dispatch(setFilters(filterResponse.filterComponentList));
|
||||
};
|
||||
const handleLockUpdate = (
|
||||
snapshot: FirebaseFirestoreTypes.DocumentSnapshot<FirebaseFirestoreTypes.DocumentData>
|
||||
) => {
|
||||
const lockData = snapshot.data();
|
||||
lockData && dispatch(setLockData(lockData));
|
||||
};
|
||||
|
||||
const handleError = (err: any, collectionPath?: string) => {
|
||||
const errMsg = `Error while fetching fireStore snapshot: referenceId: ${user?.referenceId} collectionPath: ${collectionPath}`;
|
||||
@@ -203,6 +221,11 @@ const useFirestoreUpdates = () => {
|
||||
return subscribeToDoc(handleConfigUpdate, collectionPath);
|
||||
};
|
||||
|
||||
const subscribeToLocks = () => {
|
||||
const lockPath = `locks/${user?.referenceId}`;
|
||||
return subscribeToDoc(handleLockUpdate, lockPath);
|
||||
};
|
||||
|
||||
const subscribeToFirestore = () => {
|
||||
addFirestoreListeners();
|
||||
configUnsubscribe = subscribeToUserConfig();
|
||||
@@ -213,6 +236,7 @@ const useFirestoreUpdates = () => {
|
||||
filterUnsubscribe = subscribeToFilters();
|
||||
avTemplateUnSubscriber = subscribeToAvTemplate();
|
||||
collectionTemplateUnsubscribe = subscribeToCollectionTemplate();
|
||||
lockUnsubscribe = subscribeToLocks();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -235,6 +259,7 @@ const useFirestoreUpdates = () => {
|
||||
configUnsubscribe && configUnsubscribe();
|
||||
avTemplateUnSubscriber && avTemplateUnSubscriber();
|
||||
collectionTemplateUnsubscribe && collectionTemplateUnsubscribe();
|
||||
lockUnsubscribe && lockUnsubscribe();
|
||||
};
|
||||
}, [isLoggedIn, user?.referenceId]);
|
||||
|
||||
|
||||
@@ -235,9 +235,10 @@ const allCasesSlice = createSlice({
|
||||
state.loading = action.payload;
|
||||
},
|
||||
updateCaseDetailsFirestore: (state, action) => {
|
||||
const { caseUpdates, isInitialLoad } = action.payload as {
|
||||
const { caseUpdates, isInitialLoad, isVisitPlanLocked } = action.payload as {
|
||||
caseUpdates: CaseUpdates[];
|
||||
isInitialLoad: boolean;
|
||||
isVisitPlanLocked: boolean;
|
||||
};
|
||||
if (state.loading) {
|
||||
state.loading = false;
|
||||
@@ -354,12 +355,14 @@ const allCasesSlice = createSlice({
|
||||
getLoanAccountNumber(state.caseDetails[item?.caseReferenceId])
|
||||
),
|
||||
});
|
||||
toast({
|
||||
type: 'info',
|
||||
text1: `${newVisitCaseLoanIds.length} case${
|
||||
newVisitCaseLoanIds.length > 1 ? 's' : ''
|
||||
} added to the visit plan`,
|
||||
});
|
||||
if (!isVisitPlanLocked) {
|
||||
toast({
|
||||
type: 'info',
|
||||
text1: `${newVisitCaseLoanIds.length} case${
|
||||
newVisitCaseLoanIds.length > 1 ? 's' : ''
|
||||
} added to the visit plan`,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (removedVisitedCasesLoanIds.length > 0) {
|
||||
@@ -369,12 +372,14 @@ const allCasesSlice = createSlice({
|
||||
getLoanAccountNumber(state.caseDetails[item?.caseReferenceId])
|
||||
),
|
||||
});
|
||||
toast({
|
||||
type: 'info',
|
||||
text1: `${removedVisitedCasesLoanIds.length} case${
|
||||
removedVisitedCasesLoanIds.length > 1 ? 's' : ''
|
||||
} removed from the visit plan`,
|
||||
});
|
||||
if (!isVisitPlanLocked) {
|
||||
toast({
|
||||
type: 'info',
|
||||
text1: `${removedVisitedCasesLoanIds.length} case${
|
||||
removedVisitedCasesLoanIds.length > 1 ? 's' : ''
|
||||
} removed from the visit plan`,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
updateCaseDetail: (state, action) => {
|
||||
|
||||
@@ -33,11 +33,20 @@ interface IClickstreamEvents {
|
||||
attributes?: Record<string, any>;
|
||||
}
|
||||
|
||||
export enum VisitPlanStatus {
|
||||
LOCKED = 'LOCKED',
|
||||
UNLOCKED = 'UNLOCKED',
|
||||
}
|
||||
export interface ILockData {
|
||||
visitPlanStatus: VisitPlanStatus;
|
||||
}
|
||||
|
||||
export interface IUserSlice extends IUser {
|
||||
isLoggedIn: boolean;
|
||||
deviceId: string;
|
||||
clickstreamEvents: IClickstreamEvents[];
|
||||
isImpersonated: boolean;
|
||||
lock: ILockData;
|
||||
}
|
||||
|
||||
const initialState: IUserSlice = {
|
||||
@@ -47,6 +56,9 @@ const initialState: IUserSlice = {
|
||||
user: null,
|
||||
clickstreamEvents: [],
|
||||
isImpersonated: false,
|
||||
lock: {
|
||||
visitPlanStatus: VisitPlanStatus.UNLOCKED,
|
||||
},
|
||||
};
|
||||
|
||||
export const userSlice = createSlice({
|
||||
@@ -69,9 +81,12 @@ export const userSlice = createSlice({
|
||||
state.deviceId = action.payload;
|
||||
setGlobalUserData({ token: '', deviceId: action.payload });
|
||||
},
|
||||
setLockData: (state, action) => {
|
||||
state.lock = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setAuthData, setDeviceId } = userSlice.actions;
|
||||
export const { setAuthData, setDeviceId, setLockData } = userSlice.actions;
|
||||
|
||||
export default userSlice.reducer;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { View, ViewProps } from 'react-native';
|
||||
import { Text, View, ViewProps, StyleSheet } from 'react-native';
|
||||
import { GenericStyles } 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';
|
||||
import { FeedbackStatus } from '../caseDetails/interface';
|
||||
|
||||
interface ICaseItemProps extends ViewProps {
|
||||
caseDetailObj: ICaseItemCaseDetailObj;
|
||||
@@ -22,7 +24,15 @@ const CaseItem: React.FC<ICaseItemProps> = ({
|
||||
shouldBatchAvatar = false,
|
||||
...restProps
|
||||
}) => {
|
||||
const { ADD_VISIT_PLAN } = CaseTypes;
|
||||
const { ADD_VISIT_PLAN, ATTEMPTED_CASES } = CaseTypes;
|
||||
const { attemptedCount, totalPinnedCount } = useAppSelector((state) => ({
|
||||
totalPinnedCount: state.allCases.pinnedList.length,
|
||||
attemptedCount: state.allCases.pinnedList.filter(
|
||||
(item) =>
|
||||
state.allCases.caseDetails[item.caseReferenceId]?.feedbackStatus ===
|
||||
FeedbackStatus.ATTEMPTED
|
||||
).length,
|
||||
}));
|
||||
|
||||
const handleAddVisitPlan = () => {
|
||||
navigateToScreen('Cases');
|
||||
@@ -53,6 +63,18 @@ const CaseItem: React.FC<ICaseItemProps> = ({
|
||||
/>
|
||||
);
|
||||
}
|
||||
case ATTEMPTED_CASES: {
|
||||
return (
|
||||
<Text
|
||||
style={[
|
||||
styles.attemptedText,
|
||||
attemptedCount === totalPinnedCount ? GenericStyles.pt16 : {},
|
||||
]}
|
||||
>
|
||||
Attempted Cases
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
default:
|
||||
return (
|
||||
<View {...restProps}>
|
||||
@@ -67,4 +89,11 @@ const CaseItem: React.FC<ICaseItemProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
attemptedText: {
|
||||
paddingTop: 18,
|
||||
paddingBottom: 6,
|
||||
paddingHorizontal: 2,
|
||||
},
|
||||
});
|
||||
export default CaseItem;
|
||||
|
||||
@@ -19,18 +19,25 @@ import { ToastMessages } from './constants';
|
||||
import useIsOnline from '../../hooks/useIsOnline';
|
||||
import { getLoanAccountNumber } from '../../components/utlis/commonFunctions';
|
||||
import useCurrentRoute from '../../hooks/useCurrentRoute';
|
||||
import { VisitPlanStatus } from '../../reducer/userSlice';
|
||||
|
||||
export const CasesActionButtons: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
newlyPinnedCases,
|
||||
selectedTodoListCount,
|
||||
selectedTodoListMap,
|
||||
casesList,
|
||||
caseDetails,
|
||||
visitPlansUpdating,
|
||||
pinnedList,
|
||||
} = useAppSelector((state: RootState) => state.allCases);
|
||||
allCases: {
|
||||
newlyPinnedCases,
|
||||
selectedTodoListCount,
|
||||
selectedTodoListMap,
|
||||
casesList,
|
||||
caseDetails,
|
||||
visitPlansUpdating,
|
||||
pinnedList,
|
||||
},
|
||||
isLockedVisitPlanStatus,
|
||||
} = useAppSelector((state: RootState) => ({
|
||||
allCases: state.allCases,
|
||||
isLockedVisitPlanStatus: state.user.lock.visitPlanStatus === VisitPlanStatus.LOCKED,
|
||||
}));
|
||||
const isOnline = useIsOnline();
|
||||
|
||||
const currentRoute = useCurrentRoute();
|
||||
@@ -49,6 +56,12 @@ export const CasesActionButtons: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleRemovePinnedList = () => {
|
||||
if (isLockedVisitPlanStatus) {
|
||||
toast({
|
||||
type: 'info',
|
||||
text1: ToastMessages.CASES_DELETION_DISABLED,
|
||||
});
|
||||
}
|
||||
if (!isOnline) {
|
||||
toast({ type: 'error', text1: ToastMessages.VISIT_PLAN_OFFLINE });
|
||||
return;
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
NativeScrollEvent,
|
||||
NativeSyntheticEvent,
|
||||
StyleSheet,
|
||||
Text,
|
||||
View,
|
||||
VirtualizedList,
|
||||
} from 'react-native';
|
||||
@@ -13,7 +14,7 @@ import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import { RootState } from '../../store/store';
|
||||
import { CaseTypes, ICaseItem, ICaseItemCaseDetailObj } from './interface';
|
||||
import { EmptyListMessages, ListHeaderItems, LIST_HEADER_ITEMS } from './constants';
|
||||
import { EmptyListMessages, LIST_HEADER_ITEMS, ListHeaderItems } from './constants';
|
||||
import CaseItem from './CaseItem';
|
||||
import { Search } from '../../../RN-UI-LIB/src/utlis/search';
|
||||
import FiltersContainer from '../../components/screens/allCases/allCasesFilters/FiltersContainer';
|
||||
@@ -28,13 +29,20 @@ import {
|
||||
HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS,
|
||||
HEADER_SCROLL_DISTANCE,
|
||||
HEADER_SCROLL_DISTANCE_WITH_QUICK_FILTERS,
|
||||
VISIT_PLAN_HEADER_HEIGHT_MAX,
|
||||
VISIT_PLAN_HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS,
|
||||
VISIT_PLAN_HEADER_HEIGHT_MIN,
|
||||
VISIT_PLAN_HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS,
|
||||
} from '../../common/Constants';
|
||||
import { CaseDetail } from '../caseDetails/interface';
|
||||
import { CaseDetail, FeedbackStatus } from '../caseDetails/interface';
|
||||
import CaseListHeader from './CaseListHeader';
|
||||
import { evaluateFilterForCases } from '../../components/screens/allCases/allCasesFilters/FilterUtils';
|
||||
import { debounce } from '../../components/utlis/commonFunctions';
|
||||
import { getCurrentScreen } from '../../components/utlis/navigationUtlis';
|
||||
import { useFocusEffect } from '@react-navigation/native';
|
||||
import { VisitPlanStatus } from '../../reducer/userSlice';
|
||||
import { dateFormat, DAY_MONTH_DATE_FORMAT } from '../../../RN-UI-LIB/src/utlis/dates';
|
||||
import { getAttemptedList, getNonAttemptedList } from './utils';
|
||||
|
||||
export const getItem = (item: Array<ICaseItem>, index: number) => item[index];
|
||||
|
||||
@@ -52,16 +60,16 @@ const CasesList: React.FC<ICasesList> = ({ casesList = [], isVisitPlan }) => {
|
||||
selectedTodoListMap,
|
||||
} = useAppSelector((state: RootState) => state.allCases);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const { filters, filterCount, selectedFilters, quickFiltersPresent } = useAppSelector(
|
||||
(state: RootState) => ({
|
||||
const { filters, filterCount, selectedFilters, quickFiltersPresent, isLockedVisitPlanStatus } =
|
||||
useAppSelector((state: RootState) => ({
|
||||
filters: state.filters.filters,
|
||||
filterCount: isVisitPlan ? state.filters.filterCountVisitPlan : state.filters.filterCount,
|
||||
selectedFilters: isVisitPlan
|
||||
? state.filters.selectedFiltersVisitPlan
|
||||
: state.filters.selectedFilters,
|
||||
quickFiltersPresent: state?.filters?.quickFilters?.length > 0,
|
||||
})
|
||||
);
|
||||
isLockedVisitPlanStatus: state.user.lock.visitPlanStatus === VisitPlanStatus.LOCKED,
|
||||
}));
|
||||
|
||||
const [showFilterModal, setShowFilterModal] = React.useState<boolean>(false);
|
||||
|
||||
@@ -83,13 +91,21 @@ const CasesList: React.FC<ICasesList> = ({ casesList = [], isVisitPlan }) => {
|
||||
|
||||
const headerHeight = scrollAnimation.interpolate({
|
||||
inputRange: [0, HEADER_SCROLL_DISTANCE],
|
||||
outputRange: [HEADER_HEIGHT_MAX, HEADER_HEIGHT_MIN],
|
||||
outputRange: isVisitPlan
|
||||
? [VISIT_PLAN_HEADER_HEIGHT_MAX, VISIT_PLAN_HEADER_HEIGHT_MIN]
|
||||
: [HEADER_HEIGHT_MAX, HEADER_HEIGHT_MIN],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
|
||||
//TODO: clean these different heights
|
||||
const headerHeightQuickFilters = scrollAnimation.interpolate({
|
||||
inputRange: [0, HEADER_SCROLL_DISTANCE_WITH_QUICK_FILTERS],
|
||||
outputRange: [HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS, HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS],
|
||||
outputRange: isVisitPlan
|
||||
? [
|
||||
VISIT_PLAN_HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS,
|
||||
VISIT_PLAN_HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS,
|
||||
]
|
||||
: [HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS, HEADER_HEIGHT_MIN_WITH_QUICK_FILTERS],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
|
||||
@@ -124,40 +140,73 @@ const CasesList: React.FC<ICasesList> = ({ casesList = [], isVisitPlan }) => {
|
||||
: filteredList;
|
||||
|
||||
const isFilterApplied = filterCount > 0 || searchQuery.length > 0;
|
||||
const getEmptyListMessage = () => {
|
||||
if (isLockedVisitPlanStatus && isVisitPlan) {
|
||||
const currentDate = new Date();
|
||||
return (
|
||||
EmptyListMessages.GENERATING_VISIT_PLAN +
|
||||
' ' +
|
||||
dateFormat(currentDate, DAY_MONTH_DATE_FORMAT) +
|
||||
'...'
|
||||
);
|
||||
}
|
||||
if (isFilterApplied) {
|
||||
return EmptyListMessages.NO_CASES_FOUND;
|
||||
}
|
||||
if (isVisitPlan) {
|
||||
return EmptyListMessages.NO_VISIT_PLANS;
|
||||
}
|
||||
return EmptyListMessages.NO_PENDING_CASES;
|
||||
};
|
||||
|
||||
const getSubMessage = () => {
|
||||
if (isVisitPlan && isLockedVisitPlanStatus) {
|
||||
return EmptyListMessages.GENERATING_VISIT_PLAN_SUB;
|
||||
}
|
||||
if (isFilterApplied) {
|
||||
return EmptyListMessages.NO_CASES_FOUND_SUB;
|
||||
}
|
||||
};
|
||||
|
||||
const listEmptyComponent = (
|
||||
<EmptyList
|
||||
message={
|
||||
isFilterApplied
|
||||
? EmptyListMessages.NO_CASES_FOUND
|
||||
: isVisitPlan
|
||||
? EmptyListMessages.NO_VISIT_PLANS
|
||||
: EmptyListMessages.NO_PENDING_CASES
|
||||
}
|
||||
message={getEmptyListMessage()}
|
||||
isVisitPlan={isVisitPlan}
|
||||
isFilterApplied={isFilterApplied}
|
||||
subMessage={isFilterApplied ? EmptyListMessages.NO_CASES_FOUND_SUB : ''}
|
||||
subMessage={getSubMessage()}
|
||||
isLockedVisitPlanStatus={isLockedVisitPlanStatus}
|
||||
/>
|
||||
);
|
||||
|
||||
return { filteredCasesList: filteredListAfterQuery, listEmptyComponent };
|
||||
}, [casesList, caseDetails, filters, selectedFilters, searchQuery]);
|
||||
}, [casesList, caseDetails, filters, selectedFilters, searchQuery, isLockedVisitPlanStatus]);
|
||||
|
||||
const filteredCasesListWithCTA = useMemo(() => {
|
||||
if (!isVisitPlan) {
|
||||
return filteredCasesList;
|
||||
}
|
||||
if (isLockedVisitPlanStatus) {
|
||||
return [];
|
||||
}
|
||||
const visitPlanCount = filteredCasesList.length;
|
||||
let visitPlanList: ICaseItem[] = [];
|
||||
if (visitPlanCount) {
|
||||
const attemptedFilteredList = getAttemptedList(filteredCasesList, caseDetails);
|
||||
const nonAttemptedFilteredList = getNonAttemptedList(filteredCasesList, caseDetails);
|
||||
visitPlanList = nonAttemptedFilteredList;
|
||||
if (attemptedFilteredList?.length > 0) {
|
||||
visitPlanList = [
|
||||
...visitPlanList,
|
||||
ListHeaderItems.ATTEMPTED_CASES_TEXT as ICaseItem,
|
||||
...attemptedFilteredList,
|
||||
];
|
||||
}
|
||||
if (!selectedTodoListCount) {
|
||||
visitPlanList = [...filteredCasesList, ListHeaderItems.ADD_VISIT_PLAN as ICaseItem];
|
||||
} else {
|
||||
visitPlanList = [...filteredCasesList];
|
||||
visitPlanList.push(ListHeaderItems.ADD_VISIT_PLAN as ICaseItem);
|
||||
}
|
||||
}
|
||||
return visitPlanList;
|
||||
}, [filteredCasesList, isVisitPlan, selectedTodoListCount]);
|
||||
}, [filteredCasesList, isVisitPlan, selectedTodoListCount, isLockedVisitPlanStatus]);
|
||||
|
||||
const handleSearchChange = useCallback(
|
||||
debounce((query: string) => {
|
||||
@@ -199,6 +248,17 @@ const CasesList: React.FC<ICasesList> = ({ casesList = [], isVisitPlan }) => {
|
||||
}
|
||||
setShowFilterModal(!showFilterModal);
|
||||
};
|
||||
const getHeaderLabel = () => {
|
||||
if (isVisitPlan) {
|
||||
if (isLockedVisitPlanStatus) {
|
||||
return 'Generating visit plan';
|
||||
}
|
||||
const currentDate = new Date();
|
||||
const formattedDate = dateFormat(currentDate, DAY_MONTH_DATE_FORMAT);
|
||||
return formattedDate;
|
||||
}
|
||||
return `My Cases (${filteredCasesList.length})`;
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={[GenericStyles.fill, styles.container]}>
|
||||
@@ -208,12 +268,8 @@ const CasesList: React.FC<ICasesList> = ({ casesList = [], isVisitPlan }) => {
|
||||
handleSearchChange={handleSearchChange}
|
||||
toggleFilterModal={toggleFilterModal}
|
||||
headerHeight={quickFiltersPresent ? headerHeightQuickFilters : headerHeight}
|
||||
headerLabel={
|
||||
isVisitPlan
|
||||
? `Today's visit plan (${filteredCasesList.length})`
|
||||
: `My Cases (${filteredCasesList.length})`
|
||||
}
|
||||
showFilters={!!casesList.length}
|
||||
headerLabel={getHeaderLabel()}
|
||||
showFilters={isVisitPlan && isLockedVisitPlanStatus ? false : !!casesList.length}
|
||||
isVisitPlan={isVisitPlan}
|
||||
/>
|
||||
{visitPlansUpdating ? (
|
||||
@@ -230,7 +286,13 @@ const CasesList: React.FC<ICasesList> = ({ casesList = [], isVisitPlan }) => {
|
||||
onScroll={handleListScroll}
|
||||
contentContainerStyle={[
|
||||
filteredCasesListWithCTA.length ? null : GenericStyles.fill,
|
||||
quickFiltersPresent ? styles.listWithQuickFilters : styles.list,
|
||||
!isVisitPlan
|
||||
? quickFiltersPresent
|
||||
? styles.listWithQuickFilters
|
||||
: styles.list
|
||||
: quickFiltersPresent
|
||||
? styles.visitPlanListWithQuickFilters
|
||||
: styles.visitPlanList,
|
||||
]}
|
||||
initialNumToRender={4}
|
||||
renderItem={renderListItem}
|
||||
@@ -283,11 +345,21 @@ const styles = StyleSheet.create({
|
||||
marginTop: HEADER_HEIGHT_MAX,
|
||||
paddingBottom: HEADER_HEIGHT_MAX + 10,
|
||||
},
|
||||
visitPlanList: {
|
||||
marginHorizontal: 12,
|
||||
marginTop: VISIT_PLAN_HEADER_HEIGHT_MAX,
|
||||
paddingBottom: VISIT_PLAN_HEADER_HEIGHT_MAX + 10,
|
||||
},
|
||||
listWithQuickFilters: {
|
||||
marginHorizontal: 12,
|
||||
marginTop: HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS,
|
||||
paddingBottom: HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS + 10,
|
||||
},
|
||||
visitPlanListWithQuickFilters: {
|
||||
marginHorizontal: 12,
|
||||
marginTop: VISIT_PLAN_HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS,
|
||||
paddingBottom: VISIT_PLAN_HEADER_HEIGHT_MAX_WITH_QUICK_FILTERS + 10,
|
||||
},
|
||||
});
|
||||
|
||||
export default CasesList;
|
||||
|
||||
@@ -7,23 +7,41 @@ import Button from '../../../RN-UI-LIB/src/components/Button';
|
||||
import { navigateToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import NoCasesFoundIcon from '../../assets/icons/NoCasesFoundIcon';
|
||||
import Text from '../../../RN-UI-LIB/src/components/Text';
|
||||
import GeneratingVisitPlan from '../../assets/icons/GeneratingVisitPlanIcon';
|
||||
|
||||
interface IEmptyList {
|
||||
message: string;
|
||||
subMessage?: string;
|
||||
isVisitPlan?: boolean;
|
||||
isFilterApplied?: boolean;
|
||||
isLockedVisitPlanStatus?: boolean;
|
||||
}
|
||||
|
||||
const EmptyList: React.FC<IEmptyList> = ({ message, isVisitPlan, isFilterApplied, subMessage }) => {
|
||||
const EmptyList: React.FC<IEmptyList> = ({
|
||||
message,
|
||||
isVisitPlan,
|
||||
isFilterApplied,
|
||||
subMessage,
|
||||
isLockedVisitPlanStatus,
|
||||
}) => {
|
||||
const handleCreateVisitPlan = () => {
|
||||
navigateToScreen('Cases');
|
||||
};
|
||||
|
||||
const renderIcon = () => {
|
||||
if (isLockedVisitPlanStatus && isVisitPlan) {
|
||||
return <GeneratingVisitPlan />;
|
||||
}
|
||||
if (isFilterApplied || !isVisitPlan) {
|
||||
return <NoCasesFoundIcon />;
|
||||
}
|
||||
return <EmptyPageCheckIcon />;
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.centerAbsolute}>
|
||||
<View>
|
||||
<View style={[GenericStyles.w100, styles.centerAbsolute]}>
|
||||
{isFilterApplied || !isVisitPlan ? <NoCasesFoundIcon /> : <EmptyPageCheckIcon />}
|
||||
{renderIcon()}
|
||||
<View style={[GenericStyles.mt12, styles.text]}>
|
||||
<Heading
|
||||
dark
|
||||
@@ -34,11 +52,11 @@ const EmptyList: React.FC<IEmptyList> = ({ message, isVisitPlan, isFilterApplied
|
||||
{message}
|
||||
</Heading>
|
||||
{subMessage ? (
|
||||
<Text light style={GenericStyles.centerAlignedText}>
|
||||
<Text small light style={[GenericStyles.centerAlignedText, styles.subTextStyle]}>
|
||||
{subMessage}
|
||||
</Text>
|
||||
) : null}
|
||||
{isVisitPlan && !isFilterApplied ? (
|
||||
{isVisitPlan && !isFilterApplied && !isLockedVisitPlanStatus ? (
|
||||
<Button
|
||||
title={'Create a visit plan'}
|
||||
style={[GenericStyles.w100, GenericStyles.mt24]}
|
||||
@@ -56,12 +74,14 @@ const styles = StyleSheet.create({
|
||||
height: '100%',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 4,
|
||||
paddingTop: 48,
|
||||
},
|
||||
text: {
|
||||
paddingHorizontal: 30,
|
||||
width: '100%',
|
||||
},
|
||||
subTextStyle: {
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
});
|
||||
|
||||
export default EmptyList;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ScrollView, StyleSheet, View } from 'react-native';
|
||||
import React from 'react';
|
||||
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
|
||||
import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
|
||||
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
|
||||
import TextInput from '../../../RN-UI-LIB/src/components/TextInput';
|
||||
import SearchIcon from '../../../RN-UI-LIB/src/Icons/SearchIcon';
|
||||
@@ -8,6 +8,9 @@ import IconButton from '../../../RN-UI-LIB/src/components/IconButton';
|
||||
import FilterIcon from '../../assets/icons/FilterIcon';
|
||||
import Text from '../../../RN-UI-LIB/src/components/Text';
|
||||
import QuickFilters from '../../components/screens/allCases/allCasesFilters/QuickFilters';
|
||||
import { useAppSelector } from '../../hooks';
|
||||
import { VisitPlanStatus } from '../../reducer/userSlice';
|
||||
import { FeedbackStatus } from '../caseDetails/interface';
|
||||
|
||||
interface IFilters {
|
||||
filterCount: number;
|
||||
@@ -24,6 +27,22 @@ const Filters: React.FC<IFilters> = ({
|
||||
toggleFilterModal,
|
||||
isVisitPlan,
|
||||
}) => {
|
||||
const { attemptedCount, totalPinnedCount, isVisitPlanStatusLocked } = useAppSelector((state) => ({
|
||||
totalPinnedCount: state.allCases.pinnedList.length,
|
||||
attemptedCount: state.allCases.pinnedList.filter(
|
||||
(item) =>
|
||||
state.allCases.caseDetails[item.caseReferenceId]?.feedbackStatus ===
|
||||
FeedbackStatus.ATTEMPTED
|
||||
)?.length,
|
||||
isVisitPlanStatusLocked: state.user.lock.visitPlanStatus === VisitPlanStatus.LOCKED,
|
||||
}));
|
||||
const getBarWidth = () => {
|
||||
if (!totalPinnedCount) {
|
||||
return 0;
|
||||
}
|
||||
return attemptedCount / totalPinnedCount;
|
||||
};
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View
|
||||
@@ -60,6 +79,44 @@ const Filters: React.FC<IFilters> = ({
|
||||
onPress={toggleFilterModal}
|
||||
/>
|
||||
</View>
|
||||
{isVisitPlan && !isVisitPlanStatusLocked ? (
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.w100,
|
||||
GenericStyles.pb4,
|
||||
{ backgroundColor: COLORS.BACKGROUND.GREY_LIGHT },
|
||||
]}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
GenericStyles.row,
|
||||
GenericStyles.alignCenter,
|
||||
GenericStyles.spaceBetween,
|
||||
styles.progressBarSection,
|
||||
GenericStyles.ph16,
|
||||
GenericStyles.pv12,
|
||||
getShadowStyle(8),
|
||||
]}
|
||||
>
|
||||
<Text dark bold style={GenericStyles.fw500}>
|
||||
{attemptedCount} attempted
|
||||
</Text>
|
||||
|
||||
<View style={styles.progressBarContainer}>
|
||||
<View
|
||||
style={[
|
||||
styles.progressBar,
|
||||
{
|
||||
width: `${getBarWidth() * 100}%`,
|
||||
},
|
||||
]}
|
||||
></View>
|
||||
</View>
|
||||
|
||||
<Text light>{totalPinnedCount} cases today</Text>
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false} style={[styles.chips]}>
|
||||
<QuickFilters isVisitPlan={isVisitPlan} />
|
||||
</ScrollView>
|
||||
@@ -111,6 +168,25 @@ const styles = StyleSheet.create({
|
||||
paddingHorizontal: 18,
|
||||
backgroundColor: COLORS.BACKGROUND.GREY_LIGHT,
|
||||
},
|
||||
progressBarContainer: {
|
||||
height: 8,
|
||||
flex: 0.9,
|
||||
position: 'relative',
|
||||
borderRadius: 10,
|
||||
backgroundColor: COLORS.BORDER.PRIMARY,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
progressBar: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
backgroundColor: COLORS.TEXT.GREEN,
|
||||
height: '100%',
|
||||
},
|
||||
progressBarSection: {
|
||||
backgroundColor: COLORS.BACKGROUND.PRIMARY,
|
||||
marginHorizontal: 0,
|
||||
marginVertical: 0,
|
||||
},
|
||||
});
|
||||
|
||||
export default Filters;
|
||||
|
||||
@@ -4,7 +4,7 @@ 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 } from '../../hooks';
|
||||
import { useAppDispatch, useAppSelector } from '../../hooks';
|
||||
import {
|
||||
setPinnedRank,
|
||||
setSelectedTodoListMap,
|
||||
@@ -30,6 +30,9 @@ import OnboardingIcon from '../../../RN-UI-LIB/src/Icons/OnboardingIcon';
|
||||
import CollectionsIcon from '../../../RN-UI-LIB/src/Icons/CollectionsIcon';
|
||||
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 { ToastMessages } from './constants';
|
||||
import { VisitPlanStatus } from '../../reducer/userSlice';
|
||||
|
||||
interface IListItem {
|
||||
caseListItemDetailObj: ICaseItemCaseDetailObj;
|
||||
@@ -56,6 +59,9 @@ const ListItem: React.FC<IListItem> = (props) => {
|
||||
} = caseListItemDetailObj;
|
||||
|
||||
const isCollectionCaseType = caseType === CaseAllocationType.COLLECTION_CASE;
|
||||
const isVisitPlanStatusLocked = useAppSelector(
|
||||
(state) => state.user.lock.visitPlanStatus === VisitPlanStatus.LOCKED
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
@@ -73,6 +79,13 @@ const ListItem: React.FC<IListItem> = (props) => {
|
||||
if (isTodoItem || caseStatus === CaseStatuses.CLOSED) {
|
||||
return;
|
||||
}
|
||||
if (isVisitPlanStatusLocked) {
|
||||
toast({
|
||||
type: 'info',
|
||||
text1: ToastMessages.CASES_SELECTION_DISABLED,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (pinRank) {
|
||||
dispatch(
|
||||
setSelectedTodoListMap({
|
||||
|
||||
@@ -23,6 +23,10 @@ export const ListHeaderItems = {
|
||||
type: CaseTypes.ADD_VISIT_PLAN,
|
||||
caseReferenceId: '-4',
|
||||
},
|
||||
ATTEMPTED_CASES_TEXT: {
|
||||
type: CaseTypes.ATTEMPTED_CASES,
|
||||
caseReferenceId: '-5',
|
||||
},
|
||||
};
|
||||
|
||||
export const LIST_HEADER_ITEMS = [
|
||||
@@ -39,6 +43,8 @@ export const EmptyListMessages = {
|
||||
NO_VISIT_PLANS: 'Plan your day by creating a visit plan',
|
||||
NO_CASES_FOUND: 'No results found',
|
||||
NO_CASES_FOUND_SUB: 'Try removing or adding different filters, or search something else',
|
||||
GENERATING_VISIT_PLAN: 'Generating a visit plan for',
|
||||
GENERATING_VISIT_PLAN_SUB: '*Any pending cases from previous visit plan will be added',
|
||||
};
|
||||
|
||||
export const ToastMessages = {
|
||||
@@ -61,4 +67,12 @@ export const ToastMessages = {
|
||||
LOGIN_SUCCESSFUL: 'Login Successful!',
|
||||
SSO_SERVER_SIGN_IN_ERROR: 'Error while signing in to cosmos',
|
||||
NOT_ALLOWED_ERROR: 'Not allowed',
|
||||
CASES_SELECTION_DISABLED: 'Case addition is disabled during the generation of visit plan',
|
||||
CASES_DELETION_DISABLED: 'Case deletion is disabled during the generation of visit plan',
|
||||
};
|
||||
|
||||
export enum BOTTOM_TAB_ROUTES {
|
||||
Cases = 'Cases',
|
||||
VisitPlan = 'Visit plan',
|
||||
Profile = 'Profile',
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
} from '../../reducer/allCasesSlice';
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
|
||||
import { BOTTOM_TAB_ROUTES } from './constants';
|
||||
|
||||
const AllCasesMain = () => {
|
||||
const { pendingList, pinnedList, loading } = useAppSelector((state) => state.allCases);
|
||||
@@ -29,17 +30,17 @@ const AllCasesMain = () => {
|
||||
const HOME_SCREENS: ITabScreen[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
name: 'Cases',
|
||||
name: BOTTOM_TAB_ROUTES.Cases,
|
||||
component: () => <CasesList casesList={pendingList} />,
|
||||
icon: CasesIcon,
|
||||
},
|
||||
{
|
||||
name: 'Visit plan',
|
||||
name: BOTTOM_TAB_ROUTES.VisitPlan,
|
||||
component: () => <CasesList casesList={pinnedList} isVisitPlan />,
|
||||
icon: VisitPlanIcon,
|
||||
},
|
||||
{
|
||||
name: 'Profile',
|
||||
name: BOTTOM_TAB_ROUTES.Profile,
|
||||
component: () => <Profile />,
|
||||
icon: ProfileIcon,
|
||||
},
|
||||
|
||||
@@ -22,6 +22,7 @@ export enum CaseTypes {
|
||||
CASE,
|
||||
BANNER,
|
||||
ADD_VISIT_PLAN,
|
||||
ATTEMPTED_CASES,
|
||||
}
|
||||
|
||||
export enum caseVerdict {
|
||||
|
||||
29
src/screens/allCases/utils.ts
Normal file
29
src/screens/allCases/utils.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { CaseDetail, FeedbackStatus } from '../caseDetails/interface';
|
||||
import { ICaseItem } from './interface';
|
||||
|
||||
export const getAttemptedList = (
|
||||
filteredCasesList: ICaseItem[],
|
||||
caseDetails: Record<string, CaseDetail>
|
||||
) => {
|
||||
return (
|
||||
filteredCasesList.filter(
|
||||
(item) => caseDetails[item.caseReferenceId]?.feedbackStatus === FeedbackStatus.ATTEMPTED
|
||||
) as ICaseItem[]
|
||||
).sort((a, b) => {
|
||||
return (
|
||||
(caseDetails[b.caseReferenceId]?.attemptedAt ?? 0) -
|
||||
(caseDetails[a.caseReferenceId]?.attemptedAt ?? 0)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const getNonAttemptedList = (
|
||||
filteredCasesList: ICaseItem[],
|
||||
caseDetails: Record<string, CaseDetail>
|
||||
) => {
|
||||
return filteredCasesList.filter((item) =>
|
||||
caseDetails[item.caseReferenceId]?.feedbackStatus
|
||||
? caseDetails[item.caseReferenceId].feedbackStatus === FeedbackStatus.NOT_ATTEMPTED
|
||||
: true
|
||||
);
|
||||
};
|
||||
@@ -151,6 +151,11 @@ export interface IDocument {
|
||||
type: DOCUMENT_TYPE;
|
||||
}
|
||||
|
||||
export enum FeedbackStatus {
|
||||
ATTEMPTED = 'ATTEMPTED',
|
||||
NOT_ATTEMPTED = 'NOT_ATTEMPTED',
|
||||
}
|
||||
|
||||
export interface CaseDetail {
|
||||
id: string;
|
||||
allocationReferenceId?: string;
|
||||
@@ -195,6 +200,8 @@ export interface CaseDetail {
|
||||
imageReferenceId?: string;
|
||||
collectionTag?: 'Fresh' | 'Stab';
|
||||
disbursementAmount?: number;
|
||||
feedbackStatus?: FeedbackStatus;
|
||||
attemptedAt?: number;
|
||||
}
|
||||
|
||||
export interface AddressesGeolocationPayload {
|
||||
|
||||
@@ -10,12 +10,13 @@ import { useAppDispatch, useAppSelector } from '../../hooks';
|
||||
import { CaseDetail } from '../caseDetails/interface';
|
||||
import NotificationTemplate from './NotificationTemplate';
|
||||
import { CaseAllocationType } from '../allCases/interface';
|
||||
import { pushToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import { navigateToScreen, pushToScreen } from '../../components/utlis/navigationUtlis';
|
||||
import { hasNotCompletedAction, notificationAction } from '../../action/notificationActions';
|
||||
import useIsOnline from '../../hooks/useIsOnline';
|
||||
import { addNotificationToQueue } from '../../reducer/notificationsSlice';
|
||||
import { addClickstreamEvent } from '../../services/clickstreamEventService';
|
||||
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
|
||||
import { BOTTOM_TAB_ROUTES } from '../allCases/constants';
|
||||
|
||||
export interface INotification {
|
||||
id: string;
|
||||
@@ -36,10 +37,11 @@ export interface INotification {
|
||||
paymentTime: string;
|
||||
date: string;
|
||||
time: string;
|
||||
caseCount: number;
|
||||
};
|
||||
template: {
|
||||
id: number;
|
||||
templateName: keyof typeof NotificationTypes;
|
||||
templateName: NotificationTypes;
|
||||
};
|
||||
actions: Record<string, string>[];
|
||||
createdAt: number;
|
||||
@@ -102,6 +104,10 @@ const NotificationItem: React.FC<INotificationProps> = ({ data }) => {
|
||||
if (isActionNotCompleted) {
|
||||
handleNotificationAction();
|
||||
}
|
||||
if (templateName === NotificationTypes.VISIT_PLAN_UPDATE_NOTIFICATION_TEMPLATE) {
|
||||
navigateToScreen(BOTTOM_TAB_ROUTES.VisitPlan);
|
||||
return;
|
||||
}
|
||||
if (!collectionCaseId || !clickable) {
|
||||
return;
|
||||
}
|
||||
@@ -139,7 +145,9 @@ const NotificationItem: React.FC<INotificationProps> = ({ data }) => {
|
||||
{notificationIcon}
|
||||
<View style={[GenericStyles.pl16, GenericStyles.flex80]}>
|
||||
<Heading type="h5" dark>
|
||||
{customerName}
|
||||
{templateName === NotificationTypes.VISIT_PLAN_UPDATE_NOTIFICATION_TEMPLATE
|
||||
? 'New visit plan created!'
|
||||
: customerName}
|
||||
</Heading>
|
||||
<NotificationTemplate data={data} />
|
||||
<Text small>{getTimeDifference(scheduledAt)}</Text>
|
||||
|
||||
@@ -24,6 +24,8 @@ const NotificationTemplate: React.FC<INotificationTemplateProps> = ({ data }) =>
|
||||
paymentTime,
|
||||
paymentMode,
|
||||
paymentTimestamp,
|
||||
date,
|
||||
caseCount,
|
||||
} = params || {};
|
||||
|
||||
switch (templateName) {
|
||||
@@ -163,6 +165,12 @@ const NotificationTemplate: React.FC<INotificationTemplateProps> = ({ data }) =>
|
||||
{paymentTime}
|
||||
</Text>
|
||||
);
|
||||
case NotificationTypes.VISIT_PLAN_UPDATE_NOTIFICATION_TEMPLATE:
|
||||
return (
|
||||
<Text light>
|
||||
for <Text dark>{date}</Text> with <Text dark>{caseCount}</Text> cases
|
||||
</Text>
|
||||
);
|
||||
default:
|
||||
return <Text>New notification</Text>;
|
||||
}
|
||||
|
||||
@@ -4,27 +4,7 @@ import NotificationIcon from '../../../RN-UI-LIB/src/Icons/NotificationIcon';
|
||||
import PaymentFailedIcon from '../../../RN-UI-LIB/src/Icons/PaymentFailedIcon';
|
||||
import PaymentSuccessIcon from '../../../RN-UI-LIB/src/Icons/PaymentSuccessIcon';
|
||||
import PromiseToPayIcon from '../../../RN-UI-LIB/src/Icons/PromiseToPayIcon';
|
||||
|
||||
export const NotificationIconsMap = {
|
||||
NEW_ADDRESS_ADDED_NOTIFICATION_TEMPLATE: <NewAddressIcon />,
|
||||
ENACH_PAYMENT_MADE_TEMPLATE: <PaymentSuccessIcon />,
|
||||
ENACH_PAYMENT_FAILED_TEMPLATE: <PaymentFailedIcon />,
|
||||
PAYMENT_FAILED_TEMPLATE_V2: <PaymentFailedIcon />,
|
||||
PAYMENT_FAILED_TEMPLATE: <PaymentFailedIcon />,
|
||||
PAYMENT_MADE_TEMPLATE_V2: <PaymentSuccessIcon />,
|
||||
PAYMENT_MADE_TEMPLATE: <PaymentSuccessIcon />,
|
||||
PROMISED_TO_PAY_SCHEDULED_NOTIFICATION_TEMPLATE: <PromiseToPayIcon />,
|
||||
NEW_TELEPHONE_ADDED_NOTIFICATION_TEMPLATE: <NewTelephoneIcon />,
|
||||
LONGHORN_ALERTS_CHANGE_IN_DELIQUENCY: <NotificationIcon />,
|
||||
LONGHORN_ALERTS_MULTIPLE_TRIGGER_ALERTS: <NotificationIcon />,
|
||||
LONGHORN_ALERTS_CHANGE_IN_SCORE: <NotificationIcon />,
|
||||
LONGHORN_ALERTS_NEW_PHONE_NUMBER_ADDED: <NotificationIcon />,
|
||||
LONGHORN_ALERTS_CHANGE_IN_DELIQUENCY_ACCOUNTS: <NotificationIcon />,
|
||||
LONGHORN_ALERTS_CHANGE_IN_UTILIZATION: <NotificationIcon />,
|
||||
LONGHORN_ALERTS_NEW_TRADE_ACCOUNT: <NotificationIcon />,
|
||||
LONGHORN_ALERTS_NEW_ENQUIRY: <NotificationIcon />,
|
||||
REVISIT_SCHEDULED_NOTIFICATION_TEMPLATE: <NotificationIcon />,
|
||||
};
|
||||
import NotificationVisitPlanIcon from '../../assets/icons/NotificationVisitPlanIcon';
|
||||
|
||||
export enum NotificationTypes {
|
||||
PAYMENT_MADE_TEMPLATE = 'PAYMENT_MADE_TEMPLATE',
|
||||
@@ -45,8 +25,31 @@ export enum NotificationTypes {
|
||||
REVISIT_SCHEDULED_NOTIFICATION_TEMPLATE = 'REVISIT_SCHEDULED_NOTIFICATION_TEMPLATE',
|
||||
ENACH_PAYMENT_MADE_TEMPLATE = 'ENACH_PAYMENT_MADE_TEMPLATE',
|
||||
ENACH_PAYMENT_FAILED_TEMPLATE = 'ENACH_PAYMENT_FAILED_TEMPLATE',
|
||||
VISIT_PLAN_UPDATE_NOTIFICATION_TEMPLATE = 'VISIT_PLAN_UPDATE_NOTIFICATION_TEMPLATE',
|
||||
}
|
||||
|
||||
export const NotificationIconsMap = {
|
||||
[NotificationTypes.NEW_ADDRESS_ADDED_NOTIFICATION_TEMPLATE]: <NewAddressIcon />,
|
||||
[NotificationTypes.ENACH_PAYMENT_MADE_TEMPLATE]: <PaymentSuccessIcon />,
|
||||
[NotificationTypes.ENACH_PAYMENT_FAILED_TEMPLATE]: <PaymentFailedIcon />,
|
||||
[NotificationTypes.PAYMENT_FAILED_TEMPLATE_V2]: <PaymentFailedIcon />,
|
||||
[NotificationTypes.PAYMENT_FAILED_TEMPLATE]: <PaymentFailedIcon />,
|
||||
[NotificationTypes.PAYMENT_MADE_TEMPLATE_V2]: <PaymentSuccessIcon />,
|
||||
[NotificationTypes.PAYMENT_MADE_TEMPLATE]: <PaymentSuccessIcon />,
|
||||
[NotificationTypes.PROMISED_TO_PAY_SCHEDULED_NOTIFICATION_TEMPLATE]: <PromiseToPayIcon />,
|
||||
[NotificationTypes.NEW_TELEPHONE_ADDED_NOTIFICATION_TEMPLATE]: <NewTelephoneIcon />,
|
||||
[NotificationTypes.LONGHORN_ALERTS_CHANGE_IN_DELIQUENCY]: <NotificationIcon />,
|
||||
[NotificationTypes.LONGHORN_ALERTS_MULTIPLE_TRIGGER_ALERTS]: <NotificationIcon />,
|
||||
[NotificationTypes.LONGHORN_ALERTS_CHANGE_IN_SCORE]: <NotificationIcon />,
|
||||
[NotificationTypes.LONGHORN_ALERTS_NEW_PHONE_NUMBER_ADDED]: <NotificationIcon />,
|
||||
[NotificationTypes.LONGHORN_ALERTS_CHANGE_IN_DELIQUENCY_ACCOUNTS]: <NotificationIcon />,
|
||||
[NotificationTypes.LONGHORN_ALERTS_CHANGE_IN_UTILIZATION]: <NotificationIcon />,
|
||||
[NotificationTypes.LONGHORN_ALERTS_NEW_TRADE_ACCOUNT]: <NotificationIcon />,
|
||||
[NotificationTypes.LONGHORN_ALERTS_NEW_ENQUIRY]: <NotificationIcon />,
|
||||
[NotificationTypes.REVISIT_SCHEDULED_NOTIFICATION_TEMPLATE]: <NotificationIcon />,
|
||||
[NotificationTypes.VISIT_PLAN_UPDATE_NOTIFICATION_TEMPLATE]: <NotificationVisitPlanIcon />,
|
||||
};
|
||||
|
||||
export enum WidgetStatus {
|
||||
CLICKED = 'CLICKED',
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import useIsOnline from '../../hooks/useIsOnline';
|
||||
import { getLoanAccountNumber } from '../../components/utlis/commonFunctions';
|
||||
import CaseItem from '../allCases/CaseItem';
|
||||
import { CaseDetail } from '../caseDetails/interface';
|
||||
import { VisitPlanStatus } from '../../reducer/userSlice';
|
||||
|
||||
const TodoList = () => {
|
||||
const {
|
||||
@@ -35,7 +36,16 @@ const TodoList = () => {
|
||||
intermediateTodoListMap,
|
||||
caseDetails,
|
||||
visitPlansUpdating,
|
||||
} = useSelector((state: RootState) => state.allCases);
|
||||
isVisitPlanStatusLocked,
|
||||
} = useSelector((state: RootState) => ({
|
||||
pinnedList: state.allCases.pinnedList,
|
||||
intermediateTodoList: state.allCases.intermediateTodoList,
|
||||
casesList: state.allCases.casesList,
|
||||
intermediateTodoListMap: state.allCases.intermediateTodoListMap,
|
||||
caseDetails: state.allCases.caseDetails,
|
||||
visitPlansUpdating: state.allCases.visitPlansUpdating,
|
||||
isVisitPlanStatusLocked: state.user.lock.visitPlanStatus === VisitPlanStatus.LOCKED,
|
||||
}));
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const isOnline = useIsOnline();
|
||||
@@ -53,6 +63,13 @@ const TodoList = () => {
|
||||
dispatch(resetTodoList());
|
||||
return;
|
||||
}
|
||||
if (isVisitPlanStatusLocked) {
|
||||
toast({
|
||||
type: 'info',
|
||||
text1: ToastMessages.CASES_SELECTION_DISABLED,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const updatedPinnedList: ICaseItem[] = [];
|
||||
const pinnedCasesPayload: IPinnedCasesPayload[] = [];
|
||||
const newPinedCasesLoanAccountNumbers: string[] = [];
|
||||
|
||||
Reference in New Issue
Block a user