diff --git a/src/action/agentPerformanceAction.ts b/src/action/agentPerformanceAction.ts
new file mode 100644
index 00000000..752749f3
--- /dev/null
+++ b/src/action/agentPerformanceAction.ts
@@ -0,0 +1,52 @@
+import axiosInstance, { ApiKeys, API_STATUS_CODE, getApiUrl } from '../components/utlis/apiHelper';
+import { logError } from '../components/utlis/errorUtils';
+
+export const getAgentDetail = () => {
+ const url = getApiUrl(ApiKeys.GET_AGENT_DETAIL);
+ return axiosInstance
+ .get(url)
+ .then((response) => {
+ if (response.status === API_STATUS_CODE.OK) {
+ return response.data;
+ }
+ throw response;
+ })
+ .catch((err) => {
+ logError(err);
+ throw new Error(err);
+ });
+};
+
+export const getPerformanceMetrics = () => {
+ const url = getApiUrl(ApiKeys.GET_PERFORMANCE_METRICS);
+ return axiosInstance
+ .get(url)
+ .then((response) => {
+ if (response.status === API_STATUS_CODE.OK) {
+ return response.data;
+ }
+ throw response;
+ })
+ .catch((err) => {
+ logError(err);
+ throw new Error(err);
+ });
+};
+
+export const getCashCollectedSplit = (payload: any) => {
+ const url = getApiUrl(ApiKeys.GET_CASH_COLLECTED);
+ return axiosInstance
+ .get(url, {
+ params: payload,
+ })
+ .then((response) => {
+ if (response.status === API_STATUS_CODE.OK) {
+ return response.data;
+ }
+ throw response;
+ })
+ .catch((err) => {
+ logError(err);
+ throw new Error(err);
+ });
+};
diff --git a/src/assets/icons/CashCollectedIcon.tsx b/src/assets/icons/CashCollectedIcon.tsx
new file mode 100644
index 00000000..c53dc945
--- /dev/null
+++ b/src/assets/icons/CashCollectedIcon.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import Svg, { Rect, G, Path } from 'react-native-svg';
+
+const CashCollectedIcon = () => {
+ return (
+
+ );
+};
+
+export default CashCollectedIcon;
diff --git a/src/assets/icons/DashboardIcon.tsx b/src/assets/icons/DashboardIcon.tsx
new file mode 100644
index 00000000..0a3e5f70
--- /dev/null
+++ b/src/assets/icons/DashboardIcon.tsx
@@ -0,0 +1,24 @@
+import * as React from 'react';
+import Svg, { Path } from 'react-native-svg';
+import { ITabIconProps } from '../../../RN-UI-LIB/src/components/bottomNavigator';
+import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
+
+const DashboardIcon: React.FC = ({
+ size = 20,
+ color = COLORS.TEXT.LIGHT,
+ focused,
+}) => {
+ return (
+
+ );
+};
+export default DashboardIcon;
diff --git a/src/assets/icons/EmiCollectedIcon.tsx b/src/assets/icons/EmiCollectedIcon.tsx
new file mode 100644
index 00000000..c73f6736
--- /dev/null
+++ b/src/assets/icons/EmiCollectedIcon.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import Svg, { Rect, G, Path, Mask } from 'react-native-svg';
+
+const EmiCollectedIcon = () => {
+ return (
+
+ );
+};
+
+export default EmiCollectedIcon;
diff --git a/src/assets/icons/FilledStarIcon.tsx b/src/assets/icons/FilledStarIcon.tsx
new file mode 100644
index 00000000..f9a8c1a8
--- /dev/null
+++ b/src/assets/icons/FilledStarIcon.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import Svg, { Rect, G, Path, Mask } from 'react-native-svg';
+
+const FilledStarIcon = () => {
+ return (
+
+ );
+};
+
+export default FilledStarIcon;
diff --git a/src/common/Constants.ts b/src/common/Constants.ts
index 8b02119b..909cda4f 100644
--- a/src/common/Constants.ts
+++ b/src/common/Constants.ts
@@ -521,6 +521,12 @@ export const CLICKSTREAM_EVENT_NAMES = {
name: 'FA_SUBMIT_ANYWAYS_FAILED',
description: 'Feedback submit anyway failed',
},
+
+ // Agent Dashboard
+ AD_FIREBASE_SYNC_ISSUE: {
+ name: 'AD_FIREBASE_SYNC_ISSUE',
+ description: 'Agent dashboard firebase sync issues',
+ },
} as const;
export enum MimeType {
diff --git a/src/components/screens/allCases/allCasesFilters/FilterUtils.ts b/src/components/screens/allCases/allCasesFilters/FilterUtils.ts
index c354c72d..212ec55e 100644
--- a/src/components/screens/allCases/allCasesFilters/FilterUtils.ts
+++ b/src/components/screens/allCases/allCasesFilters/FilterUtils.ts
@@ -26,12 +26,12 @@ export const evaluateFilterForCases = (
});
Object.keys(selectedFilters).forEach((key) => {
const fieldToCompareIdx =
- filters[key].fieldsToCompare.length > 1
+ filters[key]?.fieldsToCompare.length > 1
? filters[key].fieldsToCompare.findIndex((field) => {
return field.caseType === caseRecord.caseType;
})
: 0;
- switch (filters[key].filterType) {
+ switch (filters[key]?.filterType) {
case FILTER_TYPES.DATE:
switch (filters[key].operator) {
case CONDITIONAL_OPERATORS.EQUALS:
@@ -63,7 +63,7 @@ export const evaluateFilterForCases = (
break;
}
case FILTER_TYPES.STRING:
- switch (filters[key].operator) {
+ switch (filters[key]?.operator) {
case CONDITIONAL_OPERATORS.EQUALS:
if (selectedFilters[key]) {
if (typeof selectedFilters[key] === 'string') {
diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts
index 19422a6c..64c6b240 100644
--- a/src/components/utlis/apiHelper.ts
+++ b/src/components/utlis/apiHelper.ts
@@ -54,6 +54,9 @@ export enum ApiKeys {
GLOBAL_CONFIG = 'GLOBAL_CONFIG',
UPLOAD_IMAGE_ID = 'UPLOAD_IMAGE_ID',
GET_DOCUMENTS = 'GET_DOCUMENTS',
+ GET_AGENT_DETAIL = 'GET_AGENT_DETAIL',
+ GET_PERFORMANCE_METRICS = 'GET_PERFORMANCE_METRICS',
+ GET_CASH_COLLECTED = 'GET_CASH_COLLECTED',
}
export const API_URLS: Record = {} as Record;
@@ -93,6 +96,9 @@ API_URLS[ApiKeys.UPLOAD_FEEDBACK_IMAGES] = '/feedback/persist-original-images';
API_URLS[ApiKeys.GLOBAL_CONFIG] = '/global-config';
API_URLS[ApiKeys.UPLOAD_IMAGE_ID] = '/user/documents/selfie';
API_URLS[ApiKeys.GET_DOCUMENTS] = '/user/documents';
+API_URLS[ApiKeys.GET_AGENT_DETAIL] = '/agent-info';
+API_URLS[ApiKeys.GET_PERFORMANCE_METRICS] = '/agent-performance';
+API_URLS[ApiKeys.GET_CASH_COLLECTED] = '/cash-collected-split';
export const API_STATUS_CODE = {
OK: 200,
diff --git a/src/reducer/agentPerformanceSlice.ts b/src/reducer/agentPerformanceSlice.ts
new file mode 100644
index 00000000..c01a1ac1
--- /dev/null
+++ b/src/reducer/agentPerformanceSlice.ts
@@ -0,0 +1,52 @@
+import { createSlice } from '@reduxjs/toolkit';
+import { CashCollectedDataType, PerformanceDataType } from '../screens/Dashboard/interface';
+
+interface AgentPerformanceInterface {
+ performanceData: PerformanceDataType;
+ cashCollectedData: Array;
+}
+const initialState: AgentPerformanceInterface = {
+ performanceData: {
+ cases: {
+ visitedCases: 0,
+ unvisitedCaseIds: [],
+ unvisitedCases: 0,
+ contactableCases: 0,
+ nonContactableCaseIds: [],
+ nonContactableCases: 0,
+ totalPtp: 0,
+ nonPtpCaseIds: [],
+ nonPtp: 0,
+ convertedPtp: 0,
+ brokenPtpCaseIds: [],
+ brokenPtp: 0,
+ totalEmi: 0,
+ atleastOneEmiCollected: 0,
+ },
+ totalCashCollected: 0,
+ totalCashCollectedCaseIds: [],
+ performanceLevel: {
+ currentLevel: 0,
+ totalLevel: 0,
+ },
+ lastUpdatedAt: '',
+ },
+ cashCollectedData: [],
+};
+
+const agentPerformanceSlice = createSlice({
+ name: 'agentPerformance',
+ initialState,
+ reducers: {
+ setPerformanceData: (state, action) => {
+ state.performanceData = action.payload;
+ },
+ setCashCollectedData: (state, action) => {
+ state.cashCollectedData = action.payload;
+ },
+ },
+});
+
+export const { setPerformanceData, setCashCollectedData } = agentPerformanceSlice.actions;
+
+export default agentPerformanceSlice.reducer;
diff --git a/src/reducer/allCasesSlice.ts b/src/reducer/allCasesSlice.ts
index 867694a7..7c681a35 100644
--- a/src/reducer/allCasesSlice.ts
+++ b/src/reducer/allCasesSlice.ts
@@ -21,6 +21,12 @@ import { CollectionCaseWidgetId, CommonCaseWidgetId } from '../types/template.ty
import { IAvatarUri } from '../action/caseListAction';
export type ICasesMap = { [key: string]: ICaseItem };
+
+interface FilteredListToast {
+ showToast: boolean;
+ caseType: string;
+}
+
interface IAllCasesSlice {
casesList: ICaseItem[];
casesListMap: ICasesMap;
@@ -42,6 +48,7 @@ interface IAllCasesSlice {
completedList: ICaseItem[];
pinnedList: ICaseItem[];
newVisitedCases: string[];
+ filteredListToast: FilteredListToast;
}
const initialState: IAllCasesSlice = {
@@ -65,6 +72,10 @@ const initialState: IAllCasesSlice = {
completedList: [],
pinnedList: [],
newVisitedCases: [],
+ filteredListToast: {
+ showToast: false,
+ caseType: '',
+ },
};
const getCaseListComponents = (casesList: ICaseItem[], caseDetails: Record) => {
@@ -573,6 +584,9 @@ const allCasesSlice = createSlice({
}
});
},
+ setFilteredListToast: (state, action) => {
+ state.filteredListToast = action.payload;
+ },
},
});
@@ -594,6 +608,7 @@ export const {
resetNewVisitedCases,
syncCasesByFallback,
setCasesImageUri,
+ setFilteredListToast,
} = allCasesSlice.actions;
export default allCasesSlice.reducer;
diff --git a/src/reducer/userSlice.ts b/src/reducer/userSlice.ts
index b7fe08af..9a64fb09 100644
--- a/src/reducer/userSlice.ts
+++ b/src/reducer/userSlice.ts
@@ -47,6 +47,7 @@ export interface IUserSlice extends IUser {
clickstreamEvents: IClickstreamEvents[];
isImpersonated: boolean;
lock: ILockData;
+ isExternalAgent: boolean;
}
const initialState: IUserSlice = {
@@ -59,6 +60,7 @@ const initialState: IUserSlice = {
lock: {
visitPlanStatus: VisitPlanStatus.UNLOCKED,
},
+ isExternalAgent: false,
};
export const userSlice = createSlice({
@@ -86,9 +88,12 @@ export const userSlice = createSlice({
state.lock = action.payload;
}
},
+ setIsExternalAgent: (state, action) => {
+ state.isExternalAgent = action.payload;
+ },
},
});
-export const { setAuthData, setDeviceId, setLockData } = userSlice.actions;
+export const { setAuthData, setDeviceId, setLockData, setIsExternalAgent } = userSlice.actions;
export default userSlice.reducer;
diff --git a/src/screens/Dashboard/DashboardHeader.tsx b/src/screens/Dashboard/DashboardHeader.tsx
new file mode 100644
index 00000000..8fed0bfc
--- /dev/null
+++ b/src/screens/Dashboard/DashboardHeader.tsx
@@ -0,0 +1,60 @@
+import { Animated, StyleSheet, View } from 'react-native';
+import React from 'react';
+import Heading from '../../../RN-UI-LIB/src/components/Heading';
+import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
+import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
+import NotificationMenu from '../../components/notificationMenu';
+import Text from '../../../RN-UI-LIB/src/components/Text';
+import { PerfomanceHeaderTitle } from './constants';
+import { useAppSelector } from '../../hooks';
+import { sanitizeString } from '../../components/utlis/commonFunctions';
+import { BUSINESS_TIME_FORMAT, dateFormat } from '../../../RN-UI-LIB/src/utlis/dates';
+
+const DashboardHeader = () => {
+ const performanceData = useAppSelector((state) => state.agentPerformance.performanceData);
+ const { lastUpdatedAt } = performanceData;
+ return (
+
+
+
+
+ {PerfomanceHeaderTitle}
+
+
+ Last updated at{' '}
+ {sanitizeString(`${dateFormat(new Date(lastUpdatedAt), BUSINESS_TIME_FORMAT)}`)}
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ dashboardHeaderContainer: {
+ justifyContent: 'flex-end',
+ position: 'absolute',
+ top: 0,
+ zIndex: 10,
+ width: '100%',
+ height: 'auto',
+ backgroundColor: COLORS.BACKGROUND.INDIGO,
+ },
+ headerLabel: {
+ color: COLORS.BACKGROUND.PRIMARY,
+ },
+ subtitle: {
+ color: COLORS.TEXT.GREY_1,
+ },
+});
+
+export default DashboardHeader;
diff --git a/src/screens/Dashboard/PerformanceCard.tsx b/src/screens/Dashboard/PerformanceCard.tsx
new file mode 100644
index 00000000..a67a9588
--- /dev/null
+++ b/src/screens/Dashboard/PerformanceCard.tsx
@@ -0,0 +1,115 @@
+import React from 'react';
+import { StyleSheet, TouchableOpacity, View } from 'react-native';
+import Heading from '../../../RN-UI-LIB/src/components/Heading';
+import Text from '../../../RN-UI-LIB/src/components/Text';
+import Chevron from '../../../RN-UI-LIB/src/Icons/Chevron';
+import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
+import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
+import { navigateToScreen } from '../../components/utlis/navigationUtlis';
+import { useAppDispatch, useAppSelector } from '../../hooks';
+import { setFilteredListToast } from '../../reducer/allCasesSlice';
+import { setSelectedFilters } from '../../reducer/filtersSlice';
+import { BOTTOM_TAB_ROUTES } from '../allCases/constants';
+import { CurrentAllocationStatsFilterMap, CurrentAllocationStatsMap } from './interface';
+import { getPerformanceDetailFilter, getPerformanceDetails } from './utils';
+
+const PerformanceCard = () => {
+ const { performanceData, filters } = useAppSelector((state) => ({
+ performanceData: state.agentPerformance.performanceData,
+ filters: state.filters.filters,
+ }));
+ const { cases } = performanceData || {};
+ const performanceDetails = getPerformanceDetails(cases);
+
+ const dispatch = useAppDispatch();
+
+ return (
+
+ {performanceDetails.map((item) => (
+ {
+ dispatch(
+ setSelectedFilters(
+ getPerformanceDetailFilter(
+ item.redirectionType,
+ Object.keys(filters['COMMON']?.filters)?.includes(
+ CurrentAllocationStatsFilterMap[item.redirectionType]
+ )
+ )
+ )
+ );
+ dispatch(
+ setFilteredListToast({
+ showToast: true,
+ caseType: CurrentAllocationStatsMap[item.redirectionType],
+ })
+ );
+ navigateToScreen(BOTTOM_TAB_ROUTES.Cases);
+ }}
+ style={[
+ getShadowStyle(2),
+ GenericStyles.br6,
+ GenericStyles.mt16,
+ GenericStyles.p12,
+ styles.pressableCard,
+ ]}
+ >
+ {item.totalConverted}
+ {item.convertedText}
+
+
+
+ {item.totalNotConverted}
+ {item.notConvertedText}
+
+
+
+
+
+
+ ))}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexWrap: 'wrap',
+ justifyContent: 'space-between',
+ paddingTop: 10,
+ },
+ title: {
+ fontSize: 13,
+ marginTop: 8,
+ marginBottom: 8,
+ },
+ totalCount: {
+ color: COLORS.TEXT.DARK,
+ fontSize: 36,
+ fontWeight: '600',
+ paddingTop: 16,
+ },
+ rightIcon: {
+ marginTop: 2,
+ },
+ subTitle: {
+ color: COLORS.BASE.BLUE,
+ },
+ fw700: {
+ fontWeight: '700',
+ },
+ pressableCard: {
+ backgroundColor: COLORS.BACKGROUND.PRIMARY,
+ width: '47%',
+ },
+});
+
+export default PerformanceCard;
diff --git a/src/screens/Dashboard/PerformanceItem.tsx b/src/screens/Dashboard/PerformanceItem.tsx
new file mode 100644
index 00000000..0a20dabc
--- /dev/null
+++ b/src/screens/Dashboard/PerformanceItem.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+import { Pressable, StyleSheet, View } from 'react-native';
+import Text from '../../../RN-UI-LIB/src/components/Text';
+import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
+import { navigateToScreen } from '../../components/utlis/navigationUtlis';
+import { PerformanceItemProps } from './interface';
+
+const PerformanceItem = (props: PerformanceItemProps) => {
+ const { title, icon, navigateTo, rightSideView } = props;
+ return (
+ navigateTo && navigateToScreen(navigateTo)}
+ >
+
+ {icon}
+ {title}
+
+ {rightSideView}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ performanceContainer: {
+ marginTop: 14,
+ marginBottom: 14,
+ },
+});
+
+export default PerformanceItem;
diff --git a/src/screens/Dashboard/PerformanceOverview.tsx b/src/screens/Dashboard/PerformanceOverview.tsx
new file mode 100644
index 00000000..5df2997a
--- /dev/null
+++ b/src/screens/Dashboard/PerformanceOverview.tsx
@@ -0,0 +1,98 @@
+import React from 'react';
+import { StyleSheet, View } from 'react-native';
+import Text from '../../../RN-UI-LIB/src/components/Text';
+import Chevron from '../../../RN-UI-LIB/src/Icons/Chevron';
+import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
+import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
+import CashCollectedIcon from '../../assets/icons/CashCollectedIcon';
+import EmiCollectedIcon from '../../assets/icons/EmiCollectedIcon';
+import PerformanceItem from './PerformanceItem';
+import FilledStarIcon from '../../assets/icons/FilledStarIcon';
+import { PerformanceDetails } from './constants';
+import { PageRouteEnum } from '../auth/ProtectedRouter';
+import { useAppSelector } from '../../hooks';
+import { formatAmount } from '../../../RN-UI-LIB/src/utlis/amount';
+
+const PerformanceOverview = () => {
+ const performanceData = useAppSelector((state) => state.agentPerformance.performanceData);
+ const { totalCashCollected = 0, performanceLevel, cases } = performanceData || {};
+
+ return (
+
+ }
+ navigateTo={PageRouteEnum.CASH_COLLECTED}
+ rightSideView={
+
+
+ {formatAmount(totalCashCollected, false)}
+
+
+
+
+
+ }
+ />
+
+ }
+ rightSideView={
+
+
+ {cases?.atleastOneEmiCollected}{' '}
+
+ / {cases?.totalEmi}
+
+ }
+ />
+
+ }
+ rightSideView={
+
+
+ {performanceLevel?.currentLevel}{' '}
+
+
+ / {performanceLevel?.totalLevel}
+
+
+ }
+ />
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ marginTop: 100,
+ backgroundColor: COLORS.BACKGROUND.PRIMARY,
+ },
+ leftContent: {
+ color: COLORS.TEXT.DARK,
+ },
+ rightContent: {
+ color: COLORS.TEXT.BLACK,
+ },
+ rightIcon: {
+ marginLeft: 10,
+ marginTop: 2,
+ },
+ fw700: {
+ fontWeight: '700',
+ },
+});
+
+export default PerformanceOverview;
diff --git a/src/screens/Dashboard/constants.ts b/src/screens/Dashboard/constants.ts
new file mode 100644
index 00000000..e3e320f1
--- /dev/null
+++ b/src/screens/Dashboard/constants.ts
@@ -0,0 +1,20 @@
+export const PerformanceDetails = {
+ totalCashCollected: 'Total cash collected',
+ caseWithAtleast1Emi: 'Cases with atleast 1 EMI collected',
+ performanceLevel: 'Performance level',
+};
+
+export const PerformanceCardDetails = {
+ visitedCases: 'Visited Cases',
+ unVisitedCases: 'unvisited',
+ contactableCases: 'Contactable cases',
+ nonContactableCases: 'non contactable',
+ totalPtp: 'Total PTPs',
+ nonPtp: 'non PTPs',
+ ptpConverted: 'PTPs converted',
+ brokenPtp: 'broken PTPs',
+};
+
+export const PerfomanceHeaderTitle = 'Performance summary';
+
+export const NO_CASH_COLLECTED_FOUND = 'No Cash Collected';
diff --git a/src/screens/Dashboard/index.tsx b/src/screens/Dashboard/index.tsx
new file mode 100644
index 00000000..52b59fb1
--- /dev/null
+++ b/src/screens/Dashboard/index.tsx
@@ -0,0 +1,113 @@
+import React, { useEffect, useState } from 'react';
+import {
+ ActivityIndicator,
+ RefreshControl,
+ SafeAreaView,
+ ScrollView,
+ StyleSheet,
+ View,
+} from 'react-native';
+import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
+import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
+import { getPerformanceMetrics } from '../../action/agentPerformanceAction';
+import { logError } from '../../components/utlis/errorUtils';
+import { useAppDispatch, useAppSelector } from '../../hooks';
+import { setPerformanceData } from '../../reducer/agentPerformanceSlice';
+import DashboardHeader from './DashboardHeader';
+import PerformanceCard from './PerformanceCard';
+import PerformanceOverview from './PerformanceOverview';
+import { getFiltersCount } from './utils';
+
+const Dashboard = () => {
+ const [refreshing, setRefreshing] = React.useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+ const caseDetailsIds = useAppSelector((state) => Object.keys(state.allCases.caseDetails));
+
+ const dispatch = useAppDispatch();
+
+ const fetchAgentPerformanceMetrics = (callbackFn?: () => void) => {
+ setIsLoading(true);
+ getPerformanceMetrics()
+ .then((res) => {
+ const {
+ visitedCases,
+ contactableCases,
+ ptpCases,
+ convertedPtpCases,
+ atLeastOneEmiCollectedCases,
+ } = res?.casesSplit;
+ const { totalLevel, currentLevel } = res?.performanceLevel;
+ const { unVisitedCount, nonContactableCount, nonPtpCount, brokenPtpCount } =
+ getFiltersCount(caseDetailsIds, res?.casesSplit);
+
+ dispatch(
+ setPerformanceData({
+ cases: {
+ visitedCases,
+ unvisitedCases: unVisitedCount,
+ contactableCases,
+ nonContactableCases: nonContactableCount,
+ totalPtp: ptpCases,
+ nonPtp: nonPtpCount,
+ convertedPtp: convertedPtpCases,
+ brokenPtp: brokenPtpCount,
+ totalEmi: res?.data?.allCases,
+ atleastOneEmiCollected: atLeastOneEmiCollectedCases,
+ },
+ performanceLevel: {
+ totalLevel,
+ currentLevel,
+ },
+ totalCashCollected: res?.data?.totalCashCollected,
+ lastUpdatedAt: res?.data?.lastUpdatedAt,
+ })
+ );
+ })
+ .catch((err) => {
+ logError(err);
+ })
+ .finally(() => {
+ callbackFn && callbackFn();
+ setIsLoading(false);
+ });
+ };
+
+ useEffect(() => {
+ fetchAgentPerformanceMetrics();
+ }, []);
+
+ const onRefresh = React.useCallback(() => {
+ setRefreshing(true);
+ fetchAgentPerformanceMetrics(() => setRefreshing(false));
+ }, []);
+
+ if (isLoading && !refreshing)
+ return (
+
+
+
+ );
+
+ return (
+
+
+
+ }
+ >
+
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: COLORS.BACKGROUND.GREY_LIGHT,
+ position: 'relative',
+ },
+});
+
+export default Dashboard;
diff --git a/src/screens/Dashboard/interface.ts b/src/screens/Dashboard/interface.ts
new file mode 100644
index 00000000..797df5d6
--- /dev/null
+++ b/src/screens/Dashboard/interface.ts
@@ -0,0 +1,76 @@
+import React from 'react';
+
+export interface CasesType {
+ visitedCases: number;
+ unvisitedCases: number;
+ contactableCases: number;
+ nonContactableCases: number;
+ totalPtp: number;
+ nonPtp: number;
+ convertedPtp: number;
+ brokenPtp: number;
+ totalEmi: number;
+ atleastOneEmiCollected: number;
+ unvisitedCaseIds: Array;
+ nonContactableCaseIds: Array;
+ nonPtpCaseIds: Array;
+ brokenPtpCaseIds: Array;
+}
+
+interface PerformanceLevelType {
+ totalLevel: number;
+ currentLevel: number;
+}
+
+export interface PerformanceItemProps {
+ title: string;
+ icon: React.ReactNode;
+ navigateTo?: string;
+ rightSideView: React.ReactNode;
+}
+
+export interface PerformanceDataType {
+ cases: CasesType;
+ performanceLevel: PerformanceLevelType;
+ totalCashCollected: number;
+ totalCashCollectedCaseIds: Array;
+ lastUpdatedAt: string;
+}
+
+export interface PerformanceProps {
+ performanceData: PerformanceDataType | undefined;
+}
+
+export enum CurrentAllocationStats {
+ UNVSIITED = 'UNVSIITED',
+ NON_CONTACTABLE = 'NON_CONTACTABLE',
+ NON_PTP = 'NON_PTP',
+ BROKEN_PTP = 'BROKEN_PTP',
+}
+
+export enum CurrentAllocationStatsFilter {
+ VISIT_STATUS = 'VISIT_STATUS',
+ CONTACTABLE_STATUS = 'CONTACTABLE_STATUS',
+ PTP_STATUS = 'PTP_STATUS',
+ PTP_CONVERSION_STATUS = 'PTP_CONVERSION_STATUS',
+}
+
+export interface CashCollectedDataType {
+ amountCollected: number;
+ isClosed: boolean;
+ caseId: string;
+}
+
+export const CurrentAllocationStatsMap = {
+ [CurrentAllocationStats.UNVSIITED]: 'unvsited',
+ [CurrentAllocationStats.NON_CONTACTABLE]: 'non contactable',
+ [CurrentAllocationStats.NON_PTP]: 'non ptp',
+ [CurrentAllocationStats.BROKEN_PTP]: 'broken ptp',
+};
+
+export const CurrentAllocationStatsFilterMap = {
+ [CurrentAllocationStats.UNVSIITED]: CurrentAllocationStatsFilter.VISIT_STATUS,
+ [CurrentAllocationStats.NON_CONTACTABLE]: CurrentAllocationStatsFilter.CONTACTABLE_STATUS,
+ [CurrentAllocationStats.NON_PTP]: CurrentAllocationStatsFilter.PTP_STATUS,
+ [CurrentAllocationStats.BROKEN_PTP]: CurrentAllocationStatsFilter.PTP_CONVERSION_STATUS,
+};
diff --git a/src/screens/Dashboard/utils.ts b/src/screens/Dashboard/utils.ts
new file mode 100644
index 00000000..04b74807
--- /dev/null
+++ b/src/screens/Dashboard/utils.ts
@@ -0,0 +1,112 @@
+import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
+import { addClickstreamEvent } from '../../services/clickstreamEventService';
+import { CashCollectedItemType } from '../cashCollected/interface';
+import { PerformanceCardDetails } from './constants';
+import { CasesType, CurrentAllocationStats } from './interface';
+
+export const getPerformanceDetailFilter = (item: CurrentAllocationStats, applyFilter: boolean) => {
+ if (!applyFilter) return [];
+
+ switch (item) {
+ case CurrentAllocationStats.UNVSIITED:
+ return { VISIT_STATUS: { FALSE: true } };
+
+ case CurrentAllocationStats.NON_CONTACTABLE:
+ return { CONTACTABLE_STATUS: { FALSE: true } };
+
+ case CurrentAllocationStats.NON_PTP:
+ return { PTP_STATUS: { FALSE: true } };
+
+ case CurrentAllocationStats.BROKEN_PTP:
+ return { PTP_CONVERSION_STATUS: { TRUE: true } };
+
+ default:
+ break;
+ }
+};
+
+export const getPerformanceDetails = (cases: CasesType) => {
+ const {
+ visitedCases,
+ unvisitedCases,
+ contactableCases,
+ nonContactableCases,
+ totalPtp,
+ nonPtp,
+ convertedPtp,
+ brokenPtp,
+ } = cases || {};
+ return [
+ {
+ totalConverted: visitedCases,
+ convertedText: PerformanceCardDetails.visitedCases,
+ totalNotConverted: unvisitedCases,
+ notConvertedText: PerformanceCardDetails.unVisitedCases,
+ redirectionType: CurrentAllocationStats.UNVSIITED,
+ },
+ {
+ totalConverted: contactableCases,
+ convertedText: PerformanceCardDetails.contactableCases,
+ totalNotConverted: nonContactableCases,
+ notConvertedText: PerformanceCardDetails.nonContactableCases,
+ redirectionType: CurrentAllocationStats.NON_CONTACTABLE,
+ },
+ {
+ totalConverted: totalPtp,
+ convertedText: PerformanceCardDetails.totalPtp,
+ totalNotConverted: nonPtp,
+ notConvertedText: PerformanceCardDetails.nonPtp,
+ redirectionType: CurrentAllocationStats.NON_PTP,
+ },
+ {
+ totalConverted: convertedPtp,
+ convertedText: PerformanceCardDetails.ptpConverted,
+ totalNotConverted: brokenPtp,
+ notConvertedText: PerformanceCardDetails.brokenPtp,
+ redirectionType: CurrentAllocationStats.BROKEN_PTP,
+ },
+ ];
+};
+
+export const getFiltersCount = (caseDetailsIds: Array, cases: CasesType) => {
+ const { unvisitedCaseIds, nonContactableCaseIds, nonPtpCaseIds, brokenPtpCaseIds } = cases;
+
+ const unVisitedCount = caseDetailsIds.filter((caseId: string) =>
+ unvisitedCaseIds.includes(caseId)
+ ).length;
+ const nonContactableCount = caseDetailsIds.filter((caseId: string) =>
+ nonContactableCaseIds.includes(caseId)
+ ).length;
+ const nonPtpCount = caseDetailsIds.filter((caseId: string) =>
+ nonPtpCaseIds.includes(caseId)
+ ).length;
+ const brokenPtpCount = caseDetailsIds.filter((caseId: string) =>
+ brokenPtpCaseIds.includes(caseId)
+ ).length;
+
+ if (
+ unvisitedCaseIds.length !== unVisitedCount ||
+ nonContactableCaseIds.length !== nonContactableCount ||
+ nonPtpCaseIds.length !== nonPtpCount ||
+ brokenPtpCaseIds.length !== brokenPtpCount
+ ) {
+ const caseIds = [
+ ...unvisitedCaseIds,
+ ...nonContactableCaseIds,
+ ...nonPtpCaseIds,
+ ...brokenPtpCaseIds,
+ ];
+ addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AD_FIREBASE_SYNC_ISSUE, {
+ caseIds: caseIds.filter((caseId) => !caseDetailsIds.includes(caseId)),
+ });
+ }
+
+ return { unVisitedCount, nonContactableCount, nonPtpCount, brokenPtpCount };
+};
+
+export const getCashCollectedData = (
+ cashCollectedRes: Array,
+ caseDetailsIds: Array
+) => {
+ return cashCollectedRes?.filter((cashData) => caseDetailsIds.includes(cashData.caseId));
+};
diff --git a/src/screens/allCases/CasesList.tsx b/src/screens/allCases/CasesList.tsx
index 0145aae2..4922c12c 100644
--- a/src/screens/allCases/CasesList.tsx
+++ b/src/screens/allCases/CasesList.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useMemo, useRef, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
Animated,
ListRenderItem,
@@ -19,7 +19,7 @@ import CaseItem from './CaseItem';
import { Search } from '../../../RN-UI-LIB/src/utlis/search';
import FiltersContainer from '../../components/screens/allCases/allCasesFilters/FiltersContainer';
import EmptyList from './EmptyList';
-import { useAppSelector } from '../../hooks';
+import { useAppDispatch, useAppSelector } from '../../hooks';
import { addClickstreamEvent } from '../../services/clickstreamEventService';
import {
CLICKSTREAM_EVENT_NAMES,
@@ -44,6 +44,8 @@ import { FlashList } from '@shopify/flash-list';
import { VisitPlanStatus } from '../../reducer/userSlice';
import { dateFormat, DAY_MONTH_DATE_FORMAT } from '../../../RN-UI-LIB/src/utlis/dates';
import { getAttemptedList, getNonAttemptedList } from './utils';
+import { toast } from '../../../RN-UI-LIB/src/components/toast';
+import { setFilteredListToast } from '../../reducer/allCasesSlice';
export const getItem = (item: Array, index: number) => item[index];
export const ESTIMATED_ITEM_SIZE = 250; // Average height of List item
@@ -73,11 +75,15 @@ const CasesList: React.FC = ({ casesList = [], isVisitPlan }) => {
quickFiltersPresent: state?.filters?.quickFilters?.length > 0,
isLockedVisitPlanStatus: state.user?.lock?.visitPlanStatus === VisitPlanStatus.LOCKED,
}));
+ const { showToast = false, caseType = '' } =
+ useAppSelector((state: RootState) => state.allCases.filteredListToast) || {};
const [showFilterModal, setShowFilterModal] = React.useState(false);
const scrollAnimation = useRef(new Animated.Value(0)).current;
+ const dispatch = useAppDispatch();
+
const firePageLoadEvent = () => {
if (getCurrentScreen()?.name === 'Cases') {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_CASE_LIST_LOAD);
@@ -217,6 +223,25 @@ const CasesList: React.FC = ({ casesList = [], isVisitPlan }) => {
intermediateTodoListMap,
]);
+ useEffect(() => {
+ if (showToast) {
+ const filteredCasesCount = filteredCasesList.length;
+ toast({
+ type: 'info',
+ text1: `${filteredCasesCount} ${caseType} case${
+ filteredCasesCount > 1 ? 's' : ''
+ } have been filtered`,
+ visibilityTime: 1,
+ });
+ dispatch(
+ setFilteredListToast({
+ showToast: false,
+ caseType: '',
+ })
+ );
+ }
+ }, [filteredCasesList]);
+
const handleSearchChange = useCallback(
debounce((query: string) => {
setSearchQuery(query);
diff --git a/src/screens/allCases/constants.ts b/src/screens/allCases/constants.ts
index 5fad1303..a1f0ae60 100644
--- a/src/screens/allCases/constants.ts
+++ b/src/screens/allCases/constants.ts
@@ -77,4 +77,5 @@ export enum BOTTOM_TAB_ROUTES {
Cases = 'Cases',
VisitPlan = 'Visit plan',
Profile = 'Profile',
+ Dashboard = 'Dashboard',
}
diff --git a/src/screens/allCases/index.tsx b/src/screens/allCases/index.tsx
index a0770892..75a52d3d 100644
--- a/src/screens/allCases/index.tsx
+++ b/src/screens/allCases/index.tsx
@@ -22,14 +22,18 @@ import { addClickstreamEvent } from '../../services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants';
import { BOTTOM_TAB_ROUTES } from './constants';
import { getSelfieDocument } from '../../action/profileActions';
+import Dashboard from '../Dashboard';
+import DashboardIcon from '../../assets/icons/DashboardIcon';
const AllCasesMain = () => {
const { pendingList, pinnedList, loading } = useAppSelector((state) => state.allCases);
const userState = useAppSelector((state: RootState) => state.user);
+ const isExternalAgent = useAppSelector((state: RootState) => state.user.isExternalAgent);
+
const dispatch = useAppDispatch();
- const HOME_SCREENS: ITabScreen[] = useMemo(
- () => [
+ const HOME_SCREENS: ITabScreen[] = useMemo(() => {
+ const screens = [
{
name: BOTTOM_TAB_ROUTES.Cases,
component: () => ,
@@ -40,14 +44,24 @@ const AllCasesMain = () => {
component: () => ,
icon: VisitPlanIcon,
},
- {
- name: BOTTOM_TAB_ROUTES.Profile,
- component: () => ,
- icon: ProfileIcon,
- },
- ],
- [pendingList, pinnedList]
- );
+ ];
+
+ if (!isExternalAgent) {
+ screens.push({
+ name: BOTTOM_TAB_ROUTES.Dashboard,
+ component: () => ,
+ icon: DashboardIcon,
+ });
+ }
+
+ screens.push({
+ name: BOTTOM_TAB_ROUTES.Profile,
+ component: () => ,
+ icon: ProfileIcon,
+ });
+
+ return screens;
+ }, [pendingList, pinnedList]);
const onTabPressHandler = (e: any) => {
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_TAB_SWITCH, {
diff --git a/src/screens/auth/ProtectedRouter.tsx b/src/screens/auth/ProtectedRouter.tsx
index e7d4f679..c5f820de 100644
--- a/src/screens/auth/ProtectedRouter.tsx
+++ b/src/screens/auth/ProtectedRouter.tsx
@@ -1,5 +1,5 @@
import { createNativeStackNavigator } from '@react-navigation/native-stack';
-import React, { useEffect } from 'react';
+import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { _map } from '../../../RN-UI-LIB/src/utlis/common';
import { UnifiedCaseDetailsTypes, getCaseUnifiedData } from '../../action/caseApiActions';
@@ -32,6 +32,10 @@ import Notifications from '../notifications';
import RegisterPayments from '../registerPayements/RegisterPayments';
import TodoList from '../todoList/TodoList';
import UngroupedAddressContainer from '../addressGeolocation/UngroupedAddressContainer';
+import CashCollected from '../cashCollected';
+import { getAgentDetail } from '../../action/agentPerformanceAction';
+import { setIsExternalAgent } from '../../reducer/userSlice';
+import FullScreenLoader from '../../../RN-UI-LIB/src/components/FullScreenLoader';
const Stack = createNativeStackNavigator();
@@ -43,6 +47,7 @@ export enum PageRouteEnum {
EMI_SCHEDULE = 'EmiSchedule',
PAST_FEEDBACK_DETAIL = 'pastFeedbackDetail',
ADDITIONAL_ADDRESSES = 'additionalAddresses',
+ CASH_COLLECTED = 'cashCollected',
}
const ProtectedRouter = () => {
@@ -50,6 +55,7 @@ const ProtectedRouter = () => {
const { notificationsWithActions } = useAppSelector((state) => state.notifications);
const isOnline = useIsOnline();
const dispatch = useAppDispatch();
+ const [isLoading, setIsLoading] = useState(false);
// Gets unified data for new visit plan cases
// TODO: Move this to another place
@@ -106,6 +112,22 @@ const ProtectedRouter = () => {
// Firestore listener hook
useFirestoreUpdates();
+ useEffect(() => {
+ if (isOnline) {
+ setIsLoading(true);
+ getAgentDetail()
+ .then((res) => {
+ dispatch(setIsExternalAgent(res?.isExternalAgent));
+ })
+ .catch(() => {
+ dispatch(setIsExternalAgent(false));
+ })
+ .finally(() => setIsLoading(false));
+ }
+ }, [isOnline]);
+
+ if (isLoading) return ;
+
return (
{
}}
listeners={getScreenFocusListenerObj}
/>
+ null,
+ animation: 'none',
+ animationDuration: SCREEN_ANIMATION_DURATION,
+ }}
+ listeners={getScreenFocusListenerObj}
+ />
);
};
diff --git a/src/screens/cashCollected/CashCollectedItem.tsx b/src/screens/cashCollected/CashCollectedItem.tsx
new file mode 100644
index 00000000..a85774d7
--- /dev/null
+++ b/src/screens/cashCollected/CashCollectedItem.tsx
@@ -0,0 +1,101 @@
+import React from 'react';
+import { StyleSheet, TouchableOpacity, View } from 'react-native';
+import Avatar from '../../../RN-UI-LIB/src/components/Avatar';
+import Text from '../../../RN-UI-LIB/src/components/Text';
+import Chevron from '../../../RN-UI-LIB/src/Icons/Chevron';
+import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
+import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
+import { formatAmount } from '../../../RN-UI-LIB/src/utlis/amount';
+import { sanitizeString } from '../../components/utlis/commonFunctions';
+import { navigateToScreen } from '../../components/utlis/navigationUtlis';
+import { PageRouteEnum } from '../auth/ProtectedRouter';
+import { CashCollectedItemProps } from './interface';
+
+const CashCollectedItem = (props: CashCollectedItemProps) => {
+ const { cashCollectedItem, isLast, caseDetails } = props;
+ const { amountCollected = 0, isClosed, caseId } = cashCollectedItem || {};
+ const { totalOverdueAmount = 0, imageUri = '', customerName = '' } = caseDetails || {};
+
+ return (
+ navigateToScreen(PageRouteEnum.COLLECTION_CASE_DETAIL, { caseId })}
+ style={[
+ getShadowStyle(2),
+ GenericStyles.row,
+ GenericStyles.alignCenter,
+ GenericStyles.br6,
+ GenericStyles.p16,
+ GenericStyles.mt16,
+ GenericStyles.ml16,
+ GenericStyles.mr16,
+ isLast && GenericStyles.mb24,
+ styles.cashCollectedContainer,
+ ]}
+ >
+
+
+
+ {sanitizeString(customerName)}
+
+ Collected :{' '}
+ = totalOverdueAmount ? styles.green : styles.red}>
+ {formatAmount(amountCollected, false)}
+
+
+
+ Current outstanding :{' '}
+ {formatAmount(totalOverdueAmount, false)}
+
+
+
+
+ {isClosed && (
+
+ Foreclosed
+
+ )}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ cashCollectedContainer: {
+ backgroundColor: COLORS.BACKGROUND.PRIMARY,
+ },
+ textHeading: {
+ fontSize: 14,
+ fontWeight: '700',
+ color: COLORS.TEXT.DARK,
+ },
+ subText: {
+ fontSize: 12,
+ color: COLORS.TEXT.BLACK,
+ },
+ overdueAmountColor: {
+ color: COLORS.TEXT.LIGHT,
+ },
+ green: {
+ color: COLORS.TEXT.GREEN,
+ },
+ red: {
+ color: COLORS.TEXT.RED,
+ },
+ foreclosedContainer: {
+ right: 0,
+ top: 0,
+ paddingLeft: 6,
+ paddingRight: 6,
+ backgroundColor: COLORS.BACKGROUND.TEAL,
+ borderBottomLeftRadius: 4,
+ borderTopRightRadius: 4,
+ },
+ foreclosedText: {
+ color: COLORS.TEXT.TEAL,
+ },
+ flexBasis96: {
+ flexBasis: '96%',
+ },
+});
+
+export default CashCollectedItem;
diff --git a/src/screens/cashCollected/index.tsx b/src/screens/cashCollected/index.tsx
new file mode 100644
index 00000000..6b02a7f6
--- /dev/null
+++ b/src/screens/cashCollected/index.tsx
@@ -0,0 +1,145 @@
+import React, { useEffect, useState } from 'react';
+import { FlatList, RefreshControl, SafeAreaView, StyleSheet, View } from 'react-native';
+import NavigationHeader from '../../../RN-UI-LIB/src/components/NavigationHeader';
+import LineLoader from '../../../RN-UI-LIB/src/components/suspense_loader/LineLoader';
+import SuspenseLoader from '../../../RN-UI-LIB/src/components/suspense_loader/SuspenseLoader';
+import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
+import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
+import { _map } from '../../../RN-UI-LIB/src/utlis/common';
+import { getCashCollectedSplit } from '../../action/agentPerformanceAction';
+import NotificationMenu from '../../components/notificationMenu';
+import { logError } from '../../components/utlis/errorUtils';
+import { goBack } from '../../components/utlis/navigationUtlis';
+import { useAppDispatch, useAppSelector } from '../../hooks';
+import { setCashCollectedData } from '../../reducer/agentPerformanceSlice';
+import { RootState } from '../../store/store';
+import EmptyList from '../allCases/EmptyList';
+import { NO_CASH_COLLECTED_FOUND } from '../Dashboard/constants';
+import { getCashCollectedData } from '../Dashboard/utils';
+import Layout from '../layout/Layout';
+import CashCollectedItem from './CashCollectedItem';
+
+const CashCollected = () => {
+ const cashCollectedData = useAppSelector((state) => state.agentPerformance.cashCollectedData);
+
+ const [isLoading, setIsLoading] = useState(false);
+ const [paginationDetails, setPaginationDetails] = useState({
+ pageNo: 0,
+ totalPages: 0,
+ pageSize: 0,
+ });
+
+ const {
+ allCases: { caseDetails },
+ } = useAppSelector((state: RootState) => ({
+ allCases: state.allCases,
+ }));
+
+ const caseDetailsIds = Object.keys(caseDetails);
+
+ const dispatch = useAppDispatch();
+
+ const fetchCashCollectedData = (pageNo: number, callbackFn?: () => void) => {
+ setIsLoading(true);
+ getCashCollectedSplit({ pageNo, pageSize: 10 })
+ .then((res) => {
+ const cashData = res?.data;
+ setPaginationDetails(res?.pages);
+ const cashCollected = getCashCollectedData(cashData, caseDetailsIds);
+ dispatch(setCashCollectedData(cashCollected));
+ })
+ .catch((err) => {
+ logError(err);
+ })
+ .finally(() => {
+ setIsLoading(false);
+ callbackFn && callbackFn();
+ });
+ };
+
+ const fetchMoreData = () => {
+ const hasMoreData = paginationDetails.pageNo < paginationDetails.totalPages;
+ if (hasMoreData) {
+ setPaginationDetails({ ...paginationDetails, pageNo: paginationDetails.pageNo + 1 });
+ }
+ };
+
+ useEffect(() => {
+ if (paginationDetails.pageNo !== 0) fetchCashCollectedData(paginationDetails.pageNo);
+ }, [paginationDetails.pageNo]);
+
+ const [refreshing, setRefreshing] = React.useState(false);
+
+ const onRefresh = React.useCallback(() => {
+ setRefreshing(true);
+ fetchCashCollectedData(0, () => setRefreshing(false));
+ }, []);
+
+ useEffect(() => {
+ fetchCashCollectedData(0);
+ }, []);
+
+ return (
+
+
+
+
+
+ }
+ />
+
+ {[...Array(9).keys()].map(() => (
+
+ ))}
+ >
+ }
+ >
+ {cashCollectedData?.length > 0 ? (
+ }
+ renderItem={({ item, index }) => (
+
+ )}
+ onEndReached={fetchMoreData}
+ onEndReachedThreshold={0}
+ />
+ ) : (
+
+
+
+ )}
+
+
+
+ );
+};
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: COLORS.BACKGROUND.GREY_LIGHT,
+ position: 'relative',
+ },
+ centerAbsolute: {
+ height: '80%',
+ justifyContent: 'center',
+ alignItems: 'center',
+ paddingHorizontal: 4,
+ },
+});
+
+export default CashCollected;
diff --git a/src/screens/cashCollected/interface.ts b/src/screens/cashCollected/interface.ts
new file mode 100644
index 00000000..410fc215
--- /dev/null
+++ b/src/screens/cashCollected/interface.ts
@@ -0,0 +1,13 @@
+import { CaseDetail } from '../caseDetails/interface';
+
+export interface CashCollectedItemType {
+ amountCollected: number;
+ isClosed: boolean;
+ caseId: string;
+}
+
+export interface CashCollectedItemProps {
+ cashCollectedItem: CashCollectedItemType;
+ isLast: boolean;
+ caseDetails: CaseDetail;
+}
diff --git a/src/store/store.ts b/src/store/store.ts
index dc515532..b4a7bcb9 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -30,6 +30,7 @@ import foregroundServiceSlice from '../reducer/foregroundServiceSlice';
import feedbackImagesSlice from '../reducer/feedbackImagesSlice';
import configSlice from '../reducer/configSlice';
import profileSlice from '../reducer/profileSlice';
+import agentPerformanceSlice from '../reducer/agentPerformanceSlice';
const rootReducer = combineReducers({
case: caseReducer,
@@ -50,6 +51,7 @@ const rootReducer = combineReducers({
feedbackImages: feedbackImagesSlice,
config: configSlice,
profile: profileSlice,
+ agentPerformance: agentPerformanceSlice,
});
const persistConfig = {