diff --git a/package.json b/package.json index ccc82a6b..d53a97c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "AV_APP", - "version": "2.4.1", + "version": "2.3.4", "private": true, "scripts": { "android:dev": "yarn move:dev && react-native run-android", diff --git a/src/action/agentPerformanceAction.ts b/src/action/agentPerformanceAction.ts new file mode 100644 index 00000000..08ed2066 --- /dev/null +++ b/src/action/agentPerformanceAction.ts @@ -0,0 +1,127 @@ +import axiosInstance, { ApiKeys, API_STATUS_CODE, getApiUrl } from '../components/utlis/apiHelper'; +import { logError } from '../components/utlis/errorUtils'; +import { + setCashCollectedData, + setIsloading, + setPerformanceData, +} from '../reducer/agentPerformanceSlice'; +import { IUserRole, setShowAgentDashboard } from '../reducer/userSlice'; +import { + CashCollectedData, + CashCollectedSplitPayload, + PaginationDetails, +} from '../screens/Dashboard/interface'; +import { getCashCollectedData, getFiltersCount } from '../screens/Dashboard/utils'; +import { AppDispatch } from '../store/store'; + +export const getAgentDetail = (callbackFn?: () => void) => (dispatch: AppDispatch) => { + const url = getApiUrl(ApiKeys.GET_AGENT_DETAIL); + return axiosInstance + .get(url) + .then((response) => { + if (response.status === API_STATUS_CODE.OK) { + const roles = response?.data?.roles || []; + const isFieldAgent = roles?.includes(IUserRole.ROLE_FIELD_AGENT); + dispatch( + setShowAgentDashboard( + !response?.data?.isExternalAgent && isFieldAgent && roles?.length === 1 + ) + ); + } + }) + .catch((err) => { + logError(err); + }) + .finally(() => callbackFn?.()); +}; + +export const getPerformanceMetrics = + ( + caseDetailsIds: Array, + setIsLoading: (isLoading: boolean) => void, + callbackFn?: () => void + ) => + (dispatch: AppDispatch) => { + const url = getApiUrl(ApiKeys.GET_PERFORMANCE_METRICS); + return axiosInstance + .get(url) + .then((response) => { + if (response.status === API_STATUS_CODE.OK) { + const { + visitedCases, + contactableCases, + ptpCases, + convertedPtpCases, + atLeastOneEmiCollectedCases, + } = response?.data?.casesSplit || {}; + const { totalLevel, currentLevel } = response?.data?.performanceLevel || {}; + const { unVisitedCount, nonContactableCount, nonPtpCount, brokenPtpCount } = + getFiltersCount(caseDetailsIds, response?.data?.casesSplit); + + dispatch( + setPerformanceData({ + cases: { + visitedCases, + unvisitedCases: unVisitedCount, + contactableCases, + nonContactableCases: nonContactableCount, + totalPtp: ptpCases, + nonPtp: nonPtpCount, + convertedPtp: convertedPtpCases, + brokenPtp: brokenPtpCount, + totalEmi: response?.data?.allCases, + atleastOneEmiCollected: atLeastOneEmiCollectedCases, + }, + performanceLevel: { + totalLevel, + currentLevel, + }, + totalCashCollected: response?.data?.totalCashCollected, + lastUpdatedAt: response?.data?.lastUpdatedAt, + }) + ); + } + }) + .catch((err) => { + logError(err); + }) + .finally(() => { + callbackFn && callbackFn(); + setIsLoading(false); + }); + }; + +export const getCashCollectedSplit = + ( + payload: CashCollectedSplitPayload, + caseDetailsIds: Array, + cashCollectedData: CashCollectedData, + setPaginationDetails: (data: PaginationDetails) => void, + callbackFn?: () => void + ) => + (dispatch: AppDispatch) => { + const url = getApiUrl(ApiKeys.GET_CASH_COLLECTED); + return axiosInstance + .get(url, { + params: payload, + }) + .then((response) => { + if (response.status === API_STATUS_CODE.OK) { + const cashData = response?.data?.data; + setPaginationDetails(response?.data?.pages); + const cashCollected = getCashCollectedData(cashData, caseDetailsIds); + if (response?.data?.pages?.pageNo === 0) { + dispatch(setCashCollectedData(cashCollected)); + return; + } + dispatch(setCashCollectedData([...cashCollectedData.data, ...cashCollected])); + } + }) + .catch((err) => { + logError(err); + }) + .finally(() => { + dispatch(setIsloading(false)); + callbackFn && callbackFn(); + }); + }; 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 8cf8ca88..fb575ae8 100644 --- a/src/common/Constants.ts +++ b/src/common/Constants.ts @@ -550,6 +550,60 @@ export const CLICKSTREAM_EVENT_NAMES = { name: 'FA_AGENT_CASE_LOAD_SUCCESS', description: 'Agent case load success', }, + + // Agent Dashboard + FA_PERFORMANCE_DASHBOARD_FIREBASE_SYNC_ISSUE: { + name: 'FA_PERFORMANCE_DASHBOARD_FIREBASE_SYNC_ISSUE', + description: 'Agent dashboard firebase sync issues', + }, + FA_PERFORMANCE_DASHBOARD_BUTTON_CLICKED: { + name: 'FA_PERFORMANCE_DASHBOARD_BUTTON_CLICKED', + description: 'Performance Dashboard Button Clicked', + }, + FA_PERFORMANCE_DASHBOARD_PAGE_LOAD: { + name: 'FA_PERFORMANCE_DASHBOARD_PAGE_LOAD', + description: 'Performance Dashboard Page Load', + }, + FA_PERFORMANCE_DASHBOARD_CASH_COLLECTED_CLICKED: { + name: 'FA_PERFORMANCE_DASHBOARD_CASH_COLLECTED_CLICKED', + description: 'Performance Dashboard Cash Collected Clicked', + }, + FA_PERFORMANCE_DASHBOARD_CASH_COLLECTED_CASE_LOAD: { + name: 'FA_PERFORMANCE_DASHBOARD_CASH_COLLECTED_CASE_LOAD', + description: 'Performance Dashboard Cash Collected Case Load', + }, + FA_PERFORMANCE_DASHBOARD_UNVISITED_CASES_CLICKED: { + name: 'FA_PERFORMANCE_DASHBOARD_UNVISITED_CASES_CLICKED', + description: 'Performance Dashboard Unvisited Cases Clicked', + }, + FA_PERFORMANCE_DASHBOARD_UNVISITED_CASES_LOAD: { + name: 'FA_PERFORMANCE_DASHBOARD_UNVISITED_CASES_LOAD', + description: 'Performance Dashboard Button Clicked', + }, + FA_PERFORMANCE_DASHBOARD_NON_CONTACTABLE_CASES_CLICKED: { + name: 'FA_PERFORMANCE_DASHBOARD_NON_CONTACTABLE_CASES_CLICKED', + description: 'Performance Dashboard Non Contactable Cases Clicked', + }, + FA_PERFORMANCE_DASHBOARD_NON_CONTACTABLE_CASES_LOAD: { + name: 'FA_PERFORMANCE_DASHBOARD_NON_CONTACTABLE_CASES_LOAD', + description: 'Performance Dashboard Non Contactable Cases Load', + }, + FA_PERFORMANCE_DASHBOARD_NON_PTP_CASES_CLICKED: { + name: 'FA_PERFORMANCE_DASHBOARD_NON_PTP_CASES_CLICKED', + description: 'Performance Dashboard Non PTP Cases Clicked', + }, + FA_PERFORMANCE_DASHBOARD_NON_PTP_CASES_LOAD: { + name: 'FA_PERFORMANCE_DASHBOARD_NON_PTP_CASES_LOAD', + description: 'Performance Dashboard Non PTP Cases Load', + }, + FA_PERFORMANCE_DASHBOARD_BROKEN_PTP_CASES_CLICKED: { + name: 'FA_PERFORMANCE_DASHBOARD_BROKEN_PTP_CASES_CLICKED', + description: 'Performance Dashboard Broken PTP Cases Clicked', + }, + FA_PERFORMANCE_DASHBOARD_BROKEN_PTP_CASES_LOAD: { + name: 'FA_PERFORMANCE_DASHBOARD_BROKEN_PTP_CASES_LOAD', + description: 'Performance Dashboard Broken PTP Cases Load', + }, } 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/screens/allCases/allCasesFilters/FiltersContainer.tsx b/src/components/screens/allCases/allCasesFilters/FiltersContainer.tsx index cc92b051..f2ad18f6 100644 --- a/src/components/screens/allCases/allCasesFilters/FiltersContainer.tsx +++ b/src/components/screens/allCases/allCasesFilters/FiltersContainer.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react'; -import { TouchableOpacity, View } from 'react-native'; +import { ScrollView, TouchableOpacity, View } from 'react-native'; import { GenericStyles } from '../../../../../RN-UI-LIB/src/styles'; import Heading from '../../../../../RN-UI-LIB/src/components/Heading'; import Text from '../../../../../RN-UI-LIB/src/components/Text'; @@ -10,20 +10,23 @@ import { useAppDispatch } from '../../../../hooks'; import { CLICKSTREAM_EVENT_NAMES } from '../../../../common/Constants'; import SearchIcon from '../../../../../RN-UI-LIB/src/Icons/SearchIcon'; import { addClickstreamEvent } from '../../../../services/clickstreamEventService'; -import { setSelectedFilters, setSelectedFiltersVisitPlan } from '../../../../reducer/filtersSlice'; +import { + setSelectedAgentDashboardFilter, + setSelectedFilters, + setSelectedFiltersVisitPlan, +} from '../../../../reducer/filtersSlice'; import { FilterContainerProps, ISelectedFilterKey } from './Interface'; import styles from './styles'; import FilterOptions from './FilterOptions'; import { toast } from '../../../../../RN-UI-LIB/src/components/toast'; import { ToastMessages } from '../../../../screens/allCases/constants'; +import { getSelectedFilters } from '../../../../screens/Dashboard/utils'; const FiltersContainer: React.FC = (props) => { - const { closeFilterModal, isVisitPlan } = props; + const { closeFilterModal, isVisitPlan, isAgentDashboard } = props; const { filters, selectedFilters } = useSelector((state: RootState) => ({ filters: state.filters.filters, - selectedFilters: isVisitPlan - ? state.filters.selectedFiltersVisitPlan - : state.filters.selectedFilters, + selectedFilters: getSelectedFilters(state, isAgentDashboard, isVisitPlan), })); const [selectedFiltersMap, setSelectedFiltersMap] = React.useState>(selectedFilters); @@ -56,9 +59,6 @@ const FiltersContainer: React.FC = (props) => { }; const applyFilter = () => { - isVisitPlan - ? dispatch(setSelectedFiltersVisitPlan(selectedFiltersMap)) - : dispatch(setSelectedFilters(selectedFiltersMap)); addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FILTERS_APPLY_CLICKED, { selectedFilters: selectedFiltersMap, }); @@ -68,12 +68,26 @@ const FiltersContainer: React.FC = (props) => { visibilityTime: 1, }); closeFilterModal(); + if (isAgentDashboard) { + dispatch(setSelectedAgentDashboardFilter(selectedFiltersMap)); + return; + } + isVisitPlan + ? dispatch(setSelectedFiltersVisitPlan(selectedFiltersMap)) + : dispatch(setSelectedFilters(selectedFiltersMap)); + }; + + const clearFilters = () => { + if (isAgentDashboard) { + dispatch(setSelectedAgentDashboardFilter({})); + return; + } + isVisitPlan ? dispatch(setSelectedFiltersVisitPlan({})) : dispatch(setSelectedFilters({})); }; const onClearAll = () => { setSelectedFiltersMap({}); - Object.keys(selectedFilters).length > 0 && - (isVisitPlan ? dispatch(setSelectedFiltersVisitPlan({})) : dispatch(setSelectedFilters({}))); + Object.keys(selectedFilters).length > 0 && clearFilters(); addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FILTERS_CLEAR_CLICKED); }; @@ -116,77 +130,82 @@ const FiltersContainer: React.FC = (props) => { - {filterGroupKeys.map((filterGroupKey) => ( - <> - - - {filters[filterGroupKey].headerText} - - - - {filterKeys[filterGroupKey].map((filterKey) => ( - { - setSelectedFilterKey({ - filterGroup: filterGroupKey, - filterKey, - }); - setFilterSearchString(''); - addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FILTERS_TAB_CLICKED, { - currentFilterTab: selectedFilterKey, - }); - }} - > - - {filters[filterGroupKey].filters[filterKey].displayText} - - {selectedFiltersMap[filterKey] && ( - + {filterGroupKeys.map((filterGroupKey) => ( + <> + + + {filters[filterGroupKey].headerText} + + + + {filterKeys[filterGroupKey].map((filterKey) => + filters[filterGroupKey].filters[filterKey]?.visible !== false ? ( + { + setSelectedFilterKey({ + filterGroup: filterGroupKey, + filterKey, + }); + setFilterSearchString(''); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.AV_FILTERS_TAB_CLICKED, { + currentFilterTab: selectedFilterKey, + }); + }} > - {typeof selectedFiltersMap[filterKey] === 'object' - ? Object.keys(selectedFiltersMap[filterKey]).length - : 1} + {filters[filterGroupKey].filters[filterKey].displayText} - - )} - - ))} - - - ))} + {selectedFiltersMap[filterKey] && ( + + + {typeof selectedFiltersMap[filterKey] === 'object' + ? Object.keys(selectedFiltersMap[filterKey]).length + : 1} + + + )} + + ) : null + )} + + + ))} + {selectedFilterKey && ( diff --git a/src/components/screens/allCases/allCasesFilters/Interface.ts b/src/components/screens/allCases/allCasesFilters/Interface.ts index c6134c43..621c18e2 100644 --- a/src/components/screens/allCases/allCasesFilters/Interface.ts +++ b/src/components/screens/allCases/allCasesFilters/Interface.ts @@ -1,6 +1,7 @@ export interface FilterContainerProps { closeFilterModal: () => void; isVisitPlan?: boolean; + isAgentDashboard?: boolean; } export interface ISelectedFilterKey { diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts index 3cec3a0a..ceea009c 100644 --- a/src/components/utlis/apiHelper.ts +++ b/src/components/utlis/apiHelper.ts @@ -57,6 +57,8 @@ export enum ApiKeys { REPORTEES = 'REPORTEES', GET_AGENT_DETAIL = 'GET_AGENT_DETAIL', GET_SIGNED_URL_FOR_REPORTEE = 'GET_SIGNED_URL_FOR_REPORTEE', + GET_PERFORMANCE_METRICS = 'GET_PERFORMANCE_METRICS', + GET_CASH_COLLECTED = 'GET_CASH_COLLECTED', } export const API_URLS: Record = {} as Record; @@ -99,6 +101,8 @@ API_URLS[ApiKeys.UPLOAD_IMAGE_ID] = '/user/documents/selfie'; API_URLS[ApiKeys.GET_DOCUMENTS] = '/user/documents'; API_URLS[ApiKeys.REPORTEES] = '/user/all-field-reportees'; API_URLS[ApiKeys.GET_AGENT_DETAIL] = '/user/role-info'; +API_URLS[ApiKeys.GET_PERFORMANCE_METRICS] = '/allocation-cycle/agent-performance'; +API_URLS[ApiKeys.GET_CASH_COLLECTED] = '/allocation-cycle/cash-collected-split'; export const API_STATUS_CODE = { OK: 200, diff --git a/src/components/utlis/navigationUtlis.ts b/src/components/utlis/navigationUtlis.ts index a2d5b195..75012a9d 100644 --- a/src/components/utlis/navigationUtlis.ts +++ b/src/components/utlis/navigationUtlis.ts @@ -54,3 +54,9 @@ export function getWidgetNameFromRoute(routeName: string, caseType: CaseAllocati : TemplateRoutePrefix.AV.length ); } + +export const clearNavigationStack = (stackScreenName: string) => { + navigationRef?.current?.reset({ + routes: [{ index: 0, name: stackScreenName }], + }); +}; diff --git a/src/reducer/agentPerformanceSlice.ts b/src/reducer/agentPerformanceSlice.ts new file mode 100644 index 00000000..b79fb288 --- /dev/null +++ b/src/reducer/agentPerformanceSlice.ts @@ -0,0 +1,59 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { CashCollectedData, PerformanceDataType } from '../screens/Dashboard/interface'; + +interface AgentPerformanceInterface { + performanceData: PerformanceDataType; + cashCollectedData: CashCollectedData; +} +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: 0, + }, + cashCollectedData: { + data: [], + isLoading: false, + }, +}; + +const agentPerformanceSlice = createSlice({ + name: 'agentPerformance', + initialState, + reducers: { + setPerformanceData: (state, action) => { + state.performanceData = action.payload; + }, + setCashCollectedData: (state, action) => { + state.cashCollectedData.data = action.payload; + }, + setIsloading: (state, action) => { + state.cashCollectedData.isLoading = action.payload; + }, + }, +}); + +export const { setPerformanceData, setCashCollectedData, setIsloading } = + agentPerformanceSlice.actions; + +export default agentPerformanceSlice.reducer; diff --git a/src/reducer/allCasesSlice.ts b/src/reducer/allCasesSlice.ts index bf1ffab6..245f3069 100644 --- a/src/reducer/allCasesSlice.ts +++ b/src/reducer/allCasesSlice.ts @@ -23,6 +23,12 @@ import { IAvatarUri } from '../action/caseListAction'; import { MY_CASE_ITEM } from './userSlice'; export type ICasesMap = { [key: string]: ICaseItem }; + +interface FilteredListToast { + showToast: boolean; + caseType: string; +} + interface IAllCasesSlice { casesList: ICaseItem[]; casesListMap: ICasesMap; @@ -44,6 +50,7 @@ interface IAllCasesSlice { completedList: ICaseItem[]; pinnedList: ICaseItem[]; newVisitedCases: string[]; + filteredListToast: FilteredListToast; } const initialState: IAllCasesSlice = { @@ -67,6 +74,10 @@ const initialState: IAllCasesSlice = { completedList: [], pinnedList: [], newVisitedCases: [], + filteredListToast: { + showToast: false, + caseType: '', + }, }; const getCaseListComponents = (casesList: ICaseItem[], caseDetails: Record) => { @@ -563,6 +574,9 @@ const allCasesSlice = createSlice({ } }); }, + setFilteredListToast: (state, action) => { + state.filteredListToast = action.payload; + }, }, }); @@ -584,6 +598,7 @@ export const { resetNewVisitedCases, syncCasesByFallback, setCasesImageUri, + setFilteredListToast, } = allCasesSlice.actions; export default allCasesSlice.reducer; diff --git a/src/reducer/filtersSlice.ts b/src/reducer/filtersSlice.ts index 1902225f..a8f455af 100644 --- a/src/reducer/filtersSlice.ts +++ b/src/reducer/filtersSlice.ts @@ -11,6 +11,8 @@ interface IFiltersSlice { selectedFiltersVisitPlan: Record; filterCount: number; filterCountVisitPlan: number; + selectedAgentDashboardFilter: Record; + agentDashboardFilterCount: number; } const initialState: IFiltersSlice = { @@ -20,6 +22,8 @@ const initialState: IFiltersSlice = { filterCountVisitPlan: 0, selectedFilters: {}, selectedFiltersVisitPlan: {}, + selectedAgentDashboardFilter: {}, + agentDashboardFilterCount: 0, }; const filtersSlice = createSlice({ @@ -74,10 +78,36 @@ const filtersSlice = createSlice({ state.filterCountVisitPlan = filterCount; }, resetFilters: () => initialState, + setSelectedAgentDashboardFilter: (state, action) => { + state.selectedAgentDashboardFilter = { + ...action.payload, + }; + let filterCount = 0; + _map(state.selectedAgentDashboardFilter, (filterKey) => { + switch (typeof state.selectedAgentDashboardFilter[filterKey]) { + case 'object': + filterCount += + state.selectedAgentDashboardFilter[filterKey] && + Object.keys(state.selectedAgentDashboardFilter[filterKey]).length; + break; + case 'string': + filterCount += 1; + break; + default: + break; + } + }); + state.agentDashboardFilterCount = filterCount; + }, }, }); -export const { setFilters, setSelectedFilters, setSelectedFiltersVisitPlan, resetFilters } = - filtersSlice.actions; +export const { + setFilters, + setSelectedFilters, + setSelectedFiltersVisitPlan, + resetFilters, + setSelectedAgentDashboardFilter, +} = filtersSlice.actions; export default filtersSlice.reducer; diff --git a/src/reducer/userSlice.ts b/src/reducer/userSlice.ts index cef83181..00a6a8c4 100644 --- a/src/reducer/userSlice.ts +++ b/src/reducer/userSlice.ts @@ -13,6 +13,7 @@ export enum IUserRole { ROLE_TEAM_LEAD = 'ROLE_TEAM_LEAD', ROLE_NAVI_FIELD_TEAM_LEAD = 'ROLE_NAVI_FIELD_TEAM_LEAD', ROLE_NAVI_FIELD_EXTERNAL_TEAM_LEAD = 'ROLE_NAVI_FIELD_EXTERNAL_TEAM_LEAD', + ROLE_FIELD_AGENT = 'ROLE_FIELD_AGENT', } export const MY_CASE_ITEM = { @@ -60,6 +61,7 @@ export interface IUserSlice extends IUser { lock: ILockData; selectedAgent: IReportee; isTeamLead: boolean; + showAgentDashboard: boolean; } const initialState: IUserSlice = { @@ -74,6 +76,7 @@ const initialState: IUserSlice = { }, selectedAgent: MY_CASE_ITEM, isTeamLead: false, + showAgentDashboard: false, }; export const userSlice = createSlice({ @@ -116,10 +119,19 @@ export const userSlice = createSlice({ state.isTeamLead = false; } }, + setShowAgentDashboard: (state, action) => { + state.showAgentDashboard = action.payload; + }, }, }); -export const { setAuthData, setDeviceId, setLockData, setSelectedAgent, setAgentRole } = - userSlice.actions; +export const { + setAuthData, + setDeviceId, + setLockData, + setSelectedAgent, + setAgentRole, + setShowAgentDashboard, +} = userSlice.actions; export default userSlice.reducer; diff --git a/src/screens/Dashboard/DashBoardScreens.tsx b/src/screens/Dashboard/DashBoardScreens.tsx new file mode 100644 index 00000000..6315a7a8 --- /dev/null +++ b/src/screens/Dashboard/DashBoardScreens.tsx @@ -0,0 +1,39 @@ +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +import React from 'react'; +import Dashboard from '.'; +import { SCREEN_ANIMATION_DURATION } from '../../common/Constants'; +import { useAppSelector } from '../../hooks'; +import { PageRouteEnum } from '../auth/ProtectedRouter'; +import CashCollected from '../cashCollected'; +import FilteredCases from '../filteredCases'; + +const Stack = createNativeStackNavigator(); + +function DashBoardScreens() { + const { pendingList, pinnedList, completedList } = useAppSelector((state) => state.allCases); + + return ( + null, + animation: 'none', + animationDuration: SCREEN_ANIMATION_DURATION, + }} + > + null, + animation: 'none', + animationDuration: SCREEN_ANIMATION_DURATION, + }} + /> + + + + ); +} + +export default DashBoardScreens; diff --git a/src/screens/Dashboard/DashboardHeader.tsx b/src/screens/Dashboard/DashboardHeader.tsx new file mode 100644 index 00000000..3027169a --- /dev/null +++ b/src/screens/Dashboard/DashboardHeader.tsx @@ -0,0 +1,59 @@ +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 ?? 0), 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..206a65c1 --- /dev/null +++ b/src/screens/Dashboard/PerformanceCard.tsx @@ -0,0 +1,122 @@ +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 { setSelectedAgentDashboardFilter } from '../../reducer/filtersSlice'; +import { addClickstreamEvent } from '../../services/clickstreamEventService'; +import { PageRouteEnum } from '../auth/ProtectedRouter'; +import { + CurrentAllocationStatsFilterMap, + CurrentAllocationStatsMap, + PerformanceDetailsType, +} from './interface'; +import { getClickedEventName, 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(); + + const handlePress = (item: PerformanceDetailsType) => { + dispatch( + setSelectedAgentDashboardFilter( + getPerformanceDetailFilter( + item.redirectionType, + Object.keys(filters?.['COMMON']?.filters ?? {})?.includes( + CurrentAllocationStatsFilterMap[item.redirectionType] + ) + ) + ) + ); + dispatch( + setFilteredListToast({ + showToast: true, + caseType: CurrentAllocationStatsMap[item.redirectionType], + }) + ); + navigateToScreen(PageRouteEnum.FILTERED_CASES); + addClickstreamEvent(getClickedEventName(item.redirectionType), {}); + }; + return ( + + {performanceDetails.map((item) => ( + handlePress(item)} + 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..c55cc5a9 --- /dev/null +++ b/src/screens/Dashboard/PerformanceItem.tsx @@ -0,0 +1,49 @@ +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 { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants'; +import { navigateToScreen } from '../../components/utlis/navigationUtlis'; +import { addClickstreamEvent } from '../../services/clickstreamEventService'; +import { PerformanceItemProps } from './interface'; + +const PerformanceItem = (props: PerformanceItemProps) => { + const { title, icon, navigateTo, rightSideView } = props; + + const handlePress = () => { + if (navigateTo) { + navigateToScreen(navigateTo); + addClickstreamEvent( + CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_CASH_COLLECTED_CLICKED, + {} + ); + } + }; + + return ( + + + {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..04388dbb --- /dev/null +++ b/src/screens/Dashboard/PerformanceOverview.tsx @@ -0,0 +1,104 @@ +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(Number(totalCashCollected.toFixed(2)), 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', + }, + width150: { + maxWidth: 150, + }, +}); + +export default PerformanceOverview; diff --git a/src/screens/Dashboard/constants.ts b/src/screens/Dashboard/constants.ts new file mode 100644 index 00000000..fbe07d8f --- /dev/null +++ b/src/screens/Dashboard/constants.ts @@ -0,0 +1,22 @@ +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'; + +export const PAGE_SIZE = 10; diff --git a/src/screens/Dashboard/index.tsx b/src/screens/Dashboard/index.tsx new file mode 100644 index 00000000..cdcbc33d --- /dev/null +++ b/src/screens/Dashboard/index.tsx @@ -0,0 +1,78 @@ +import { useIsFocused } from '@react-navigation/native'; +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 { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants'; +import { useAppDispatch, useAppSelector } from '../../hooks'; +import { resetTodoList } from '../../reducer/allCasesSlice'; +import { addClickstreamEvent } from '../../services/clickstreamEventService'; +import DashboardHeader from './DashboardHeader'; +import PerformanceCard from './PerformanceCard'; +import PerformanceOverview from './PerformanceOverview'; + +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 isFocused = useIsFocused(); + + const fetchAgentPerformanceMetrics = (callbackFn?: () => void) => { + setIsLoading(true); + dispatch(getPerformanceMetrics(caseDetailsIds, setIsLoading, callbackFn)); + }; + + useEffect(() => { + if (!isFocused) { + dispatch(resetTodoList()); + return; + } + fetchAgentPerformanceMetrics(); + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_PAGE_LOAD, {}); + }, [isFocused]); + + 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..3dc927a7 --- /dev/null +++ b/src/screens/Dashboard/interface.ts @@ -0,0 +1,101 @@ +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: number; +} + +export interface CashCollectedData { + data: Array; + isLoading: boolean; +} + +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', + CONTACTABILITY = 'CONTACTABILITY', + PTP_STATUS = 'PTP_STATUS', + PTP_BREAKAGE = 'PTP_BREAKAGE', +} + +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.CONTACTABILITY, + [CurrentAllocationStats.NON_PTP]: CurrentAllocationStatsFilter.PTP_STATUS, + [CurrentAllocationStats.BROKEN_PTP]: CurrentAllocationStatsFilter.PTP_BREAKAGE, +}; + +export interface PerformanceDetailsType { + totalConverted: number; + convertedText: string; + totalNotConverted: number; + notConvertedText: string; + redirectionType: CurrentAllocationStats; +} + +export interface PaginationDetails { + pageNo: number; + totalPages: number; + pageSize: number; + totalElements: number; +} + +export interface CashCollectedSplitPayload { + pageNo: number; + pageSize: number; +} diff --git a/src/screens/Dashboard/utils.ts b/src/screens/Dashboard/utils.ts new file mode 100644 index 00000000..d85dd45b --- /dev/null +++ b/src/screens/Dashboard/utils.ts @@ -0,0 +1,167 @@ +import { CLICKSTREAM_EVENT_NAMES } from '../../common/Constants'; +import { addClickstreamEvent } from '../../services/clickstreamEventService'; +import { RootState } from '../../store/store'; +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 { CONTACTABILITY: { true: true } }; + + case CurrentAllocationStats.NON_PTP: + return { PTP_STATUS: { false: true } }; + + case CurrentAllocationStats.BROKEN_PTP: + return { PTP_BREAKAGE: { 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 || {}; + + let unVisitedCount = 0, + nonContactableCount = 0, + nonPtpCount = 0, + brokenPtpCount = 0; + + caseDetailsIds.forEach((caseId) => { + unVisitedCount += Number(unvisitedCaseIds.includes(caseId)); + nonContactableCount += Number(nonContactableCaseIds.includes(caseId)); + nonPtpCount += Number(nonPtpCaseIds.includes(caseId)); + brokenPtpCount += Number(brokenPtpCaseIds.includes(caseId)); + }); + + if ( + unvisitedCaseIds.length !== unVisitedCount || + nonContactableCaseIds.length !== nonContactableCount || + nonPtpCaseIds.length !== nonPtpCount || + brokenPtpCaseIds.length !== brokenPtpCount + ) { + const caseIds = [ + ...unvisitedCaseIds, + ...nonContactableCaseIds, + ...nonPtpCaseIds, + ...brokenPtpCaseIds, + ]; + addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_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)); +}; + +export const getClickedEventName = (item: CurrentAllocationStats) => { + switch (item) { + case CurrentAllocationStats.UNVSIITED: + return CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_UNVISITED_CASES_CLICKED; + + case CurrentAllocationStats.NON_CONTACTABLE: + return CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_NON_CONTACTABLE_CASES_CLICKED; + + case CurrentAllocationStats.NON_PTP: + return CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_NON_PTP_CASES_CLICKED; + + case CurrentAllocationStats.BROKEN_PTP: + return CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_BROKEN_PTP_CASES_CLICKED; + } +}; + +export const getPageLoadEventName = (caseType: string) => { + switch (caseType) { + case 'unvsited': + return CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_UNVISITED_CASES_LOAD; + + case 'non contactable': + return CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_NON_CONTACTABLE_CASES_LOAD; + + case 'non PTP': + return CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_NON_PTP_CASES_LOAD; + + case 'broken PTP': + return CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_BROKEN_PTP_CASES_LOAD; + + default: + return CLICKSTREAM_EVENT_NAMES.FA_PERFORMANCE_DASHBOARD_UNVISITED_CASES_LOAD; + } +}; + +export const getSelectedFilters = ( + state: RootState, + isAgentDashboard?: boolean, + isVisitPlan?: boolean +) => { + if (isAgentDashboard) return state.filters.selectedAgentDashboardFilter; + if (isVisitPlan) return state.filters.selectedFiltersVisitPlan; + return state.filters.selectedFilters; +}; + +export const getFilterCount = ( + state: RootState, + isAgentDashboard?: boolean, + isVisitPlan?: boolean +) => { + if (isAgentDashboard) return state.filters.agentDashboardFilterCount; + if (isVisitPlan) return state.filters.filterCountVisitPlan; + return state.filters.filterCount; +}; diff --git a/src/screens/allCases/CaseItem.tsx b/src/screens/allCases/CaseItem.tsx index b509b867..f363f699 100644 --- a/src/screens/allCases/CaseItem.tsx +++ b/src/screens/allCases/CaseItem.tsx @@ -15,6 +15,7 @@ interface ICaseItemProps extends ViewProps { isTodoItem?: boolean; shouldBatchAvatar?: boolean; allCasesView?: boolean; + isAgentDashboard?: boolean; } const CaseItem: React.FC = ({ @@ -24,6 +25,7 @@ const CaseItem: React.FC = ({ isTodoItem = false, shouldBatchAvatar = false, allCasesView = false, + isAgentDashboard = false, ...restProps }) => { const { ADD_VISIT_PLAN, ATTEMPTED_CASES } = CaseTypes; @@ -88,6 +90,7 @@ const CaseItem: React.FC = ({ isCompleted={isCompleted} isTodoItem={isTodoItem} allCasesView={allCasesView} + isAgentDashboard={isAgentDashboard} /> ); diff --git a/src/screens/allCases/CaseListHeader.tsx b/src/screens/allCases/CaseListHeader.tsx index 43f74431..b5c22674 100644 --- a/src/screens/allCases/CaseListHeader.tsx +++ b/src/screens/allCases/CaseListHeader.tsx @@ -1,10 +1,12 @@ -import { Animated, StyleSheet, View } from 'react-native'; +import { Animated, StyleSheet, TouchableHighlight, View } from 'react-native'; import React from 'react'; import { COLORS } from '../../../RN-UI-LIB/src/styles/colors'; import { GenericStyles } from '../../../RN-UI-LIB/src/styles'; import Filters from './Filters'; import NotificationMenu from '../../components/notificationMenu'; import HeaderLabel from './HeaderLabel'; +import { goBack } from '../../components/utlis/navigationUtlis'; +import BackArrowIcon from '../../../RN-UI-LIB/src/Icons/BackArrowIcon'; interface ICaseListHeader { searchQuery: string; @@ -15,6 +17,7 @@ interface ICaseListHeader { setShowAgentSelectionBottomSheet: (val: boolean) => void; filteredListCount: number; isVisitPlan?: boolean; + isAgentDashboard?: boolean; } const CaseListHeader: React.FC = ({ @@ -26,6 +29,7 @@ const CaseListHeader: React.FC = ({ setShowAgentSelectionBottomSheet, filteredListCount, isVisitPlan, + isAgentDashboard, }) => { return ( = ({ GenericStyles.pv10, ]} > - + + {isAgentDashboard && ( + + + + )} + + {showFilters && ( @@ -53,6 +69,7 @@ const CaseListHeader: React.FC = ({ handleSearchChange={handleSearchChange} toggleFilterModal={toggleFilterModal} isVisitPlan={isVisitPlan} + isAgentDashboard={isAgentDashboard} /> )} diff --git a/src/screens/allCases/CasesActionButtons.tsx b/src/screens/allCases/CasesActionButtons.tsx index 082da8c0..da0b0a7a 100644 --- a/src/screens/allCases/CasesActionButtons.tsx +++ b/src/screens/allCases/CasesActionButtons.tsx @@ -103,11 +103,14 @@ export const CasesActionButtons: React.FC = () => { dispatch(resetSelectedTodoList()); }; - if (!newlyPinnedCases && !selectedTodoListCount) { + if ( + (!newlyPinnedCases && !selectedTodoListCount) || + ['dashboardMain', 'cashCollected'].includes(currentRoute) + ) { return null; } const renderActions = () => { - if (newlyPinnedCases && currentRoute === 'Cases') { + if (newlyPinnedCases && ['Cases', 'filteredCases'].includes(currentRoute)) { return ( <>