From 35330d2488dea545090525f1f17c5c379c55e246 Mon Sep 17 00:00:00 2001 From: "aishwarya.srivastava" Date: Mon, 5 May 2025 19:55:09 +0530 Subject: [PATCH] NTP-59601 | Anomaly Tracker for Inhouse --- src/assets/icons/AnomalyIcon.tsx | 34 ++++++ src/assets/icons/NoAnomaliesFoundIcon.tsx | 112 ++++++++++++++++++ src/assets/icons/UpdateIcon.tsx | 18 +++ src/components/utlis/apiHelper.ts | 15 ++- src/reducer/anomalyTrackerSlice.ts | 49 ++++++++ .../AnomalyTracker/AnomaliesDetailsList.tsx | 106 +++++++++++++++++ .../AnomalyTracker/AnomalyCardBody.tsx | 29 +++++ .../AnomalyTracker/AnomalyCardHeader.tsx | 40 +++++++ .../AnomalyTracker/AnomalyDetailsItem.tsx | 30 +++++ .../AnomalyTracker/AnomalyOverviewCard.tsx | 65 ++++++++++ .../AnomalyTracker/AnomlayTracker.tsx | 43 +++++++ .../AnomalyTracker/SingleAnomalyDetails.tsx | 66 +++++++++++ .../Dashboard/AnomalyTracker/constants.ts | 17 +++ src/screens/Dashboard/AnomalyTracker/utils.ts | 56 +++++++++ .../Dashboard/InternalAgentDashboard.tsx | 9 ++ src/screens/Dashboard/index.tsx | 10 +- src/screens/caseDetails/CaseDetailStack.tsx | 3 + src/store/store.ts | 4 +- 18 files changed, 699 insertions(+), 7 deletions(-) create mode 100644 src/assets/icons/AnomalyIcon.tsx create mode 100644 src/assets/icons/NoAnomaliesFoundIcon.tsx create mode 100644 src/assets/icons/UpdateIcon.tsx create mode 100644 src/reducer/anomalyTrackerSlice.ts create mode 100644 src/screens/Dashboard/AnomalyTracker/AnomaliesDetailsList.tsx create mode 100644 src/screens/Dashboard/AnomalyTracker/AnomalyCardBody.tsx create mode 100644 src/screens/Dashboard/AnomalyTracker/AnomalyCardHeader.tsx create mode 100644 src/screens/Dashboard/AnomalyTracker/AnomalyDetailsItem.tsx create mode 100644 src/screens/Dashboard/AnomalyTracker/AnomalyOverviewCard.tsx create mode 100644 src/screens/Dashboard/AnomalyTracker/AnomlayTracker.tsx create mode 100644 src/screens/Dashboard/AnomalyTracker/SingleAnomalyDetails.tsx create mode 100644 src/screens/Dashboard/AnomalyTracker/constants.ts create mode 100644 src/screens/Dashboard/AnomalyTracker/utils.ts diff --git a/src/assets/icons/AnomalyIcon.tsx b/src/assets/icons/AnomalyIcon.tsx new file mode 100644 index 00000000..9cf73a97 --- /dev/null +++ b/src/assets/icons/AnomalyIcon.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import Svg, { Path, Rect } from 'react-native-svg'; + +const AnomalyTrackerIcon = () => ( + + + + + + + +); + +export default AnomalyTrackerIcon; diff --git a/src/assets/icons/NoAnomaliesFoundIcon.tsx b/src/assets/icons/NoAnomaliesFoundIcon.tsx new file mode 100644 index 00000000..d73f442f --- /dev/null +++ b/src/assets/icons/NoAnomaliesFoundIcon.tsx @@ -0,0 +1,112 @@ +import * as React from 'react'; +import Svg, { Path } from 'react-native-svg'; +const NoAnomaliesFoundIcon = () => ( + + + + + + + + + + + + + + + + + +); +export default NoAnomaliesFoundIcon; diff --git a/src/assets/icons/UpdateIcon.tsx b/src/assets/icons/UpdateIcon.tsx new file mode 100644 index 00000000..115362ef --- /dev/null +++ b/src/assets/icons/UpdateIcon.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import Svg, { G, Mask, Path, Rect } from 'react-native-svg'; + +const UpdateIcon = () => ( + + + + + + + + +); + +export default UpdateIcon; diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts index f508e4b4..9ef2f121 100644 --- a/src/components/utlis/apiHelper.ts +++ b/src/components/utlis/apiHelper.ts @@ -119,8 +119,10 @@ export enum ApiKeys { GET_FEEDBACK_ADDRESSES = 'GET_FEEDBACK_ADDRESSES', GET_TRAINING_MATERIAL_LIST = 'GET_TRAINING_MATERIAL_LIST', GET_TRAINING_MATERIAL_DETAILS = 'GET_TRAINING_MATERIAL_DETAILS', - SELF_CALL_ACK= '/api/v1/self-call', - GET_CASES_GEOLOCATION_DISTANCE_FROM_AGENT_LOCATION = "GET_CASES_GEOLOCATION_DISTANCE_FROM_AGENT_LOCATION" + SELF_CALL_ACK = '/api/v1/self-call', + GET_CASES_GEOLOCATION_DISTANCE_FROM_AGENT_LOCATION = 'GET_CASES_GEOLOCATION_DISTANCE_FROM_AGENT_LOCATION', + GET_ANOMALY_DETAILS = 'GET_ANOMALY_DETAILS', + GET_ANOMALY_ACTIVITY_LOG = 'GET_ANOMALY_ACTIVITY_LOG', } export const API_URLS: Record = {} as Record; @@ -229,7 +231,10 @@ API_URLS[ApiKeys.GET_TRAINING_MATERIAL_DETAILS] = '/training-page/{docRefId}'; API_URLS[ApiKeys.SELF_CALL_ACK] = '/sync-data/self-call-metadata'; API_URLS[ApiKeys.GET_TOP_ADDRESSES] = '/collection-cases/unified-locations'; API_URLS[ApiKeys.GET_FEEDBACK_ADDRESSES] = '/collection-cases/unified-locations/lite'; -API_URLS[ApiKeys.GET_CASES_GEOLOCATION_DISTANCE_FROM_AGENT_LOCATION] = '/geolocation-distance/single-source' +API_URLS[ApiKeys.GET_ANOMALY_DETAILS] = '/anomaly-tracker'; +API_URLS[ApiKeys.GET_ANOMALY_ACTIVITY_LOG] = '/anomaly-tracker/activity-logs/{anomalyReferenceId}'; +API_URLS[ApiKeys.GET_CASES_GEOLOCATION_DISTANCE_FROM_AGENT_LOCATION] = + '/geolocation-distance/single-source'; export const API_STATUS_CODE = { OK: 200, @@ -360,7 +365,7 @@ axiosInstance.interceptors.response.use( const url = response?.config?.url; const apiKey = getKeyByValue(url, API_URLS); sendApiToClickstreamEvent(response, milliseconds, false); - if(apiKey) { + if (apiKey) { logger({ msg: 'api response error', extras: { @@ -370,7 +375,7 @@ axiosInstance.interceptors.response.use( endpoint: API_URLS[apiKey as keyof typeof API_URLS], method: response?.config?.method || '', }, - type: 'error' + type: 'error', }); } addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_API_FAILED, { diff --git a/src/reducer/anomalyTrackerSlice.ts b/src/reducer/anomalyTrackerSlice.ts new file mode 100644 index 00000000..341c0c99 --- /dev/null +++ b/src/reducer/anomalyTrackerSlice.ts @@ -0,0 +1,49 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState: any = { + openAnomaliesList: {}, + closedAnomaliesList: {}, + anomalyDetailsLoading: false, + anomalyDetails: { + data: {}, + loading: false, + }, + actionLoading: false, + acitvityLogs: { + data: [], + loading: false, + }, +}; + +const anomalyTrackerSlice = createSlice({ + name: 'anomalyTracker', + initialState, + reducers: { + setOpenAnomalyList: (state, action) => { + state.openAnomaliesList = action.payload; + }, + setClosedAnomalyList: (state, action) => { + state.closedAnomaliesList = action.payload; + }, + setAnomalyDetailsLoading: (state, action) => { + state.anomalyDetailsLoading = action.payload; + }, + setActivityLogs: (state, action) => { + state.acitvityLogs.data = action.payload; + }, + setActivityLogsLoading: (state, action) => { + state.acitvityLogs.loading = action.payload; + }, + }, +}); + +export const { + setOpenAnomalyList, + setClosedAnomalyList, + setAnomalyDetailsLoading, + setRcaReasons, + setActivityLogs, + setActivityLogsLoading, +} = anomalyTrackerSlice.actions; + +export default anomalyTrackerSlice.reducer; diff --git a/src/screens/Dashboard/AnomalyTracker/AnomaliesDetailsList.tsx b/src/screens/Dashboard/AnomalyTracker/AnomaliesDetailsList.tsx new file mode 100644 index 00000000..e1c33eb7 --- /dev/null +++ b/src/screens/Dashboard/AnomalyTracker/AnomaliesDetailsList.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import { View, StyleSheet, ActivityIndicator, FlatList } from 'react-native'; +import AnomalyDetailsItem from './AnomalyDetailsItem'; +import { useAppSelector } from '@hooks'; +import { AnomalyType } from './constants'; +import { GenericStyles } from '@rn-ui-lib/styles'; +import { COLORS } from '@rn-ui-lib/colors'; +import Text from '@rn-ui-lib/components/Text'; +import NoAnomaliesFoundIcon from '@assets/icons/NoAnomaliesFoundIcon'; +import Heading from '@rn-ui-lib/components/Heading'; + +interface IAnomaliesDetailsListProps { + anomalyType: string; +} + +const AnomaliesDetailsList = ({ anomalyType }: IAnomaliesDetailsListProps) => { + const openAnomaliesData = useAppSelector( + (state) => state?.anomalyTracker?.openAnomaliesList?.data || [] + ); + const closedAnomaliesData = useAppSelector( + (state) => state?.anomalyTracker?.closedAnomaliesList?.data || [] + ); + + const isLoading = useAppSelector((state) => state?.anomalyTracker?.anomalyDetailsLoading); + if (isLoading) { + return ( + + + + ); + } + if (anomalyType === AnomalyType.OPEN) { + return ( + <> + {openAnomaliesData?.length > 0 ? ( + item?.anomalyId} + renderItem={({ item }) => } + contentContainerStyle={[GenericStyles.pb16]} + /> + ) : ( + + + + + No open issues + + + You have closed all the issues + + + + )} + + ); + } + return ( + <> + {closedAnomaliesData?.length > 0 ? ( + item?.anomalyId} + renderItem={({ item }) => } + contentContainerStyle={[GenericStyles.pb16]} + /> + ) : ( + + + + + No closed issues + + + Closed issues are shown here + + + + )} + + ); +}; + +const styles = StyleSheet.create({ + centerAbsolute: { + // height: '100%', + marginTop: 160, + justifyContent: 'center', + alignItems: 'center', + }, + text: { + width: '100%', + }, +}); + +export default AnomaliesDetailsList; diff --git a/src/screens/Dashboard/AnomalyTracker/AnomalyCardBody.tsx b/src/screens/Dashboard/AnomalyTracker/AnomalyCardBody.tsx new file mode 100644 index 00000000..74caaa4f --- /dev/null +++ b/src/screens/Dashboard/AnomalyTracker/AnomalyCardBody.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +import { View, StyleSheet } from 'react-native'; +import SingleAnomalyDetails from './SingleAnomalyDetails'; +import RcaEtaContainer from './RcaEtaContainer'; + +interface IAnomalyCardBodyProps { + item: any; + anomalyType: string; +} +const AnomalyCardBody = (props: IAnomalyCardBodyProps) => { + const { item, anomalyType } = props; + return ( + + + + + ); +}; +const styles = StyleSheet.create({ + container: { + borderBottomRightRadius: 6, + borderBottomLeftRadius: 6, + paddingHorizontal: 16, + paddingTop: 20, + paddingBottom: 16, + }, +}); +export default AnomalyCardBody; diff --git a/src/screens/Dashboard/AnomalyTracker/AnomalyCardHeader.tsx b/src/screens/Dashboard/AnomalyTracker/AnomalyCardHeader.tsx new file mode 100644 index 00000000..699b568c --- /dev/null +++ b/src/screens/Dashboard/AnomalyTracker/AnomalyCardHeader.tsx @@ -0,0 +1,40 @@ +import { COLORS } from '@rn-ui-lib/colors'; +import { GenericStyles, getShadowStyle } from '@rn-ui-lib/styles'; +import Text from '@rn-ui-lib/components/Text'; + +import React from 'react'; +import { View, StyleSheet } from 'react-native'; +const AnomalyCardHeader = (props: any) => { + const { item } = props; + + return ( + + + {item?.type} + + + {item?.priority} + + + ); +}; +const styles = StyleSheet.create({ + container: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + backgroundColor: COLORS.BACKGROUND.RED, + borderTopRadius: 6, + paddingHorizontal: 16, + paddingVertical: 12, + alignItems: 'center', + }, + priorityLabel: { + borderRadius: 4, + backgroundColor: COLORS.TEXT.RED, + paddingHorizontal: 8, + paddingVertical: 1, + }, + text: { fontSize: 14, fontWeight: '700', color: COLORS.TEXT.DARK }, +}); +export default AnomalyCardHeader; diff --git a/src/screens/Dashboard/AnomalyTracker/AnomalyDetailsItem.tsx b/src/screens/Dashboard/AnomalyTracker/AnomalyDetailsItem.tsx new file mode 100644 index 00000000..503e4ff9 --- /dev/null +++ b/src/screens/Dashboard/AnomalyTracker/AnomalyDetailsItem.tsx @@ -0,0 +1,30 @@ +import { COLORS } from '@rn-ui-lib/colors'; +import { GenericStyles, getShadowStyle } from '@rn-ui-lib/styles'; +import React from 'react'; +import { View, StyleSheet } from 'react-native'; +import AnomalyCardBody from './AnomalyCardBody'; +import AnomalyCardHeader from './AnomalyCardHeader'; + +interface IAnomalyDetailsItemProps { + anomalyType: string; + item: any; +} +const AnomalyDetailsItem = (props: IAnomalyDetailsItemProps) => { + const { anomalyType, item } = props; + return ( + + + + + ); +}; +const styles = StyleSheet.create({ + container: { + backgroundColor: COLORS.BACKGROUND.PRIMARY, + borderRadius: 8, + marginHorizontal: 16, + marginTop: 16, + ...getShadowStyle(2), + }, +}); +export default AnomalyDetailsItem; diff --git a/src/screens/Dashboard/AnomalyTracker/AnomalyOverviewCard.tsx b/src/screens/Dashboard/AnomalyTracker/AnomalyOverviewCard.tsx new file mode 100644 index 00000000..080dad06 --- /dev/null +++ b/src/screens/Dashboard/AnomalyTracker/AnomalyOverviewCard.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { Pressable, View, StyleSheet } from 'react-native'; +import Text from '@rn-ui-lib/components/Text'; +import AnomalyIcon from '@assets/icons/AnomalyIcon'; +import { navigateToScreen } from '@components/utlis/navigationUtlis'; +import { PageRouteEnum } from '@screens/auth/ProtectedRouter'; +import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack'; +import { GenericStyles, getShadowStyle } from '@rn-ui-lib/styles'; +import { COLORS } from '@rn-ui-lib/colors'; +import Chevron from '@rn-ui-lib/icons/Chevron'; +import { useAppSelector } from '@hooks'; + +const AnomalyOverviewCard = () => { + const totalOpenAnomalies = + useAppSelector((state) => state?.anomalyTracker?.openAnomaliesList?.pages?.totalElements); + + const onClick = () => { + navigateToScreen(PageRouteEnum.CASE_DETAIL_STACK, { + screen: CaseDetailStackEnum.ANOMALY_TRACKER, + }); + }; + + return ( + + + + {totalOpenAnomalies} Open issues + + + View + + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + display: 'flex', + flexDirection: 'row', + borderRadius: 4, + marginHorizontal: 16, + ...getShadowStyle(2), + backgroundColor: COLORS.BACKGROUND.ORANGE, + borderWidth: 1, + borderColor: COLORS.BORDER.ORANGE, + marginBottom: 16, + padding: 12, + justifyContent: 'space-between', + }, + textContainer: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: 6, + }, + rightIcon: { + marginTop: 3, + }, + text: { fontSize: 13, fontWeight: '600' }, + buttonText: { color: COLORS.TEXT.BLUE, fontWeight: '600', fontSize: 13 }, +}); +export default AnomalyOverviewCard; diff --git a/src/screens/Dashboard/AnomalyTracker/AnomlayTracker.tsx b/src/screens/Dashboard/AnomalyTracker/AnomlayTracker.tsx new file mode 100644 index 00000000..e56070cb --- /dev/null +++ b/src/screens/Dashboard/AnomalyTracker/AnomlayTracker.tsx @@ -0,0 +1,43 @@ +import React, { useEffect, useState } from 'react'; +import { anomalyPageTitle, AnomalyType, TABS } from './constants'; +import { GenericStyles, getShadowStyle } from '@rn-ui-lib/styles'; +import NavigationHeader from '@rn-ui-lib/components/NavigationHeader'; +import CustomTabs from '@rn-ui-lib/components/customTabs/CustomTabs'; +import { goBack } from '@components/utlis/navigationUtlis'; +import AnomaliesDetailsList from './AnomaliesDetailsList'; +import { useAppDispatch } from '@hooks'; +import { getAnomalyDetails } from './utils'; + +interface IAnomalyTracker {} + +const AnomalyTracker: React.FC = (props: IAnomalyTracker) => { + const [currentTab, setCurrentTab] = useState(AnomalyType.OPEN); + const dispatch = useAppDispatch(); + useEffect(() => { + dispatch(getAnomalyDetails(AnomalyType.OPEN, {}, true)); + }); + const handleTabChange = (tab: string) => { + if (tab === currentTab) return; + if (tab === AnomalyType.OPEN) { + dispatch(getAnomalyDetails(AnomalyType.OPEN, {}, true)); + } else { + dispatch(getAnomalyDetails(AnomalyType.RESOLVED, {}, true)); + } + setCurrentTab(tab); + }; + + return ( + <> + + + + + ); +}; + +export default AnomalyTracker; diff --git a/src/screens/Dashboard/AnomalyTracker/SingleAnomalyDetails.tsx b/src/screens/Dashboard/AnomalyTracker/SingleAnomalyDetails.tsx new file mode 100644 index 00000000..16ed2b48 --- /dev/null +++ b/src/screens/Dashboard/AnomalyTracker/SingleAnomalyDetails.tsx @@ -0,0 +1,66 @@ +import { COLORS } from '@rn-ui-lib/colors'; +import Text from '@rn-ui-lib/components/Text'; + +import React from 'react'; +import { View, StyleSheet } from 'react-native'; +import { sanitizeString } from '@components/utlis/commonFunctions'; +import { BUSINESS_DATE_FORMAT, dateFormat } from '@rn-ui-lib/utils/dates'; +import { AnomalyType } from './constants'; +import DaysTillEscalationComponent from './DaysTillEscalationComponent'; + +interface ISingleAnomalyDetailsProps { + item: any; + anomalyType: string; +} + +const SingleAnomalyDetails = (props: ISingleAnomalyDetailsProps) => { + const { item, anomalyType } = props; + + return ( + + + Created on + + {sanitizeString(dateFormat(new Date(item?.createdAt), BUSINESS_DATE_FORMAT))} + + + {anomalyType === AnomalyType.OPEN ? ( + + Days till Escalation + + + ) : ( + + Resolved on + + {sanitizeString(dateFormat(new Date(item?.resolution?.at), BUSINESS_DATE_FORMAT))} + + + )} + + ); +}; +const styles = StyleSheet.create({ + container: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + paddingBottom: 16, + alignItems: 'center', + }, + priorityLabel: { + borderRadius: 4, + backgroundColor: COLORS.TEXT.RED, + paddingHorizontal: 6, + paddingVertical: 1, + }, + textLabel: { fontSize: 12, color: COLORS.TEXT.LIGHT }, + date: { fontSize: 14, color: COLORS.TEXT.BLACK, fontWeight: '600' }, + daysTillEscalationText: { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 8 }, + textAlign: { textAlign: 'right' }, + w60: { width: 60 }, +}); +export default SingleAnomalyDetails; diff --git a/src/screens/Dashboard/AnomalyTracker/constants.ts b/src/screens/Dashboard/AnomalyTracker/constants.ts new file mode 100644 index 00000000..a0d74ef4 --- /dev/null +++ b/src/screens/Dashboard/AnomalyTracker/constants.ts @@ -0,0 +1,17 @@ +export enum AnomalyType { + OPEN = 'OPEN', + RESOLVED = 'RESOLVED', +} + +export const TABS = [ + { + key: AnomalyType.OPEN, + label: 'Open', + }, + { + key: AnomalyType.RESOLVED, + label: 'Closed', + }, +]; + +export const anomalyPageTitle = 'Your issues'; diff --git a/src/screens/Dashboard/AnomalyTracker/utils.ts b/src/screens/Dashboard/AnomalyTracker/utils.ts new file mode 100644 index 00000000..0b6d5e2c --- /dev/null +++ b/src/screens/Dashboard/AnomalyTracker/utils.ts @@ -0,0 +1,56 @@ +import axiosInstance, { API_STATUS_CODE, ApiKeys, getApiUrl } from '@components/utlis/apiHelper'; +import { AnomalyType } from './constants'; +import { logError } from '@components/utlis/errorUtils'; + +import { AppDispatch } from '@store'; +import { + setActivityLogs, + setActivityLogsLoading, + setAnomalyDetailsLoading, + setClosedAnomalyList, + setOpenAnomalyList, +} from '@reducers/anomalyTrackerSlice'; + +export const getAnomalyDetails = + (anomalyType: string, payload: {}, isLoading: boolean) => (dispatch: AppDispatch) => { + const url = getApiUrl(ApiKeys.GET_ANOMALY_DETAILS); + if (isLoading) dispatch(setAnomalyDetailsLoading(true)); + axiosInstance + .get(url, { + baseURL: 'https://qa-longhorn-portal.np.navi-ppl.in/api/longhorn', //TODO: remove this + params: { status: anomalyType }, + }) + .then((res) => { + if (res?.status === API_STATUS_CODE.OK) { + if (anomalyType === AnomalyType.OPEN) { + dispatch(setOpenAnomalyList(res?.data)); + return; + } + dispatch(setClosedAnomalyList(res?.data)); + } + }) + .catch((err) => { + logError(err); + }) + .finally(() => { + dispatch(setAnomalyDetailsLoading(false)); + }); + }; + +export const getActivityLogs = (anomalyReferenceId: string) => (dispatch: AppDispatch) => { + const url = getApiUrl(ApiKeys.GET_ANOMALY_ACTIVITY_LOG, { anomalyReferenceId }); + dispatch(setActivityLogsLoading(true)); + axiosInstance + .get(url, { + baseURL: 'https://qa-longhorn-portal.np.navi-ppl.in/api/longhorn', //TODO: remove this + }) + .then((response) => { + if (response.status === API_STATUS_CODE.OK) { + dispatch(setActivityLogs(response?.data)); + } + }) + .catch((err) => { + logError(err); + }) + .finally(() => dispatch(setActivityLogsLoading(false))); +}; diff --git a/src/screens/Dashboard/InternalAgentDashboard.tsx b/src/screens/Dashboard/InternalAgentDashboard.tsx index 184a5d6c..edc57d10 100644 --- a/src/screens/Dashboard/InternalAgentDashboard.tsx +++ b/src/screens/Dashboard/InternalAgentDashboard.tsx @@ -3,6 +3,7 @@ import React from 'react'; import InternalAgentPerformanceCard from './InternalAgentPerformanceCard'; import PerformanceMeter from './PerformanceMeter'; import PerformanceOverview from './PerformanceOverview'; +import AnomalyOverviewCard from './AnomalyTracker/AnomalyOverviewCard'; const InternalAgentDashboard = () => { const performanceData = useAppSelector((state) => state.agentPerformance.performanceData); @@ -14,9 +15,17 @@ const InternalAgentDashboard = () => { atleastOneEmiCollected: cases?.atleastOneEmiCollected, totalEmi: cases?.totalEmi, }; + const totalOpenAnomalies = useAppSelector( + (state) => state?.anomalyTracker?.openAnomaliesList?.pages?.totalElements + ); + const totalClosedAnomalies = useAppSelector( + (state) => state?.anomalyTracker?.closedAnomaliesList?.pages?.totalElements + ); + const isOpenOrClosedAnomaliesPresent = totalOpenAnomalies + totalClosedAnomalies > 0; return ( <> + {isOpenOrClosedAnomaliesPresent ? : null} diff --git a/src/screens/Dashboard/index.tsx b/src/screens/Dashboard/index.tsx index daedd8f3..9e6eae1b 100644 --- a/src/screens/Dashboard/index.tsx +++ b/src/screens/Dashboard/index.tsx @@ -16,6 +16,8 @@ import { addClickstreamEvent } from '../../services/clickstreamEventService'; import DashboardHeader from './DashboardHeader'; import ExternalAgentDashboard from './ExternalAgentDashboard'; import InternalAgentDashboard from './InternalAgentDashboard'; +import { getAnomalyDetails } from './AnomalyTracker/utils'; +import { AnomalyType } from './AnomalyTracker/constants'; const Dashboard = () => { const [refreshing, setRefreshing] = React.useState(false); @@ -27,7 +29,13 @@ const Dashboard = () => { const fetchAgentPerformanceMetrics = () => { setIsLoading(true); - dispatch(getPerformanceMetrics(Object.keys(caseDetailsIds ?? {}), isExternalAgent, setIsLoading)); + dispatch( + getPerformanceMetrics(Object.keys(caseDetailsIds ?? {}), isExternalAgent, setIsLoading) + ); + if (!isExternalAgent) { + dispatch(getAnomalyDetails(AnomalyType.OPEN, {}, false)); + dispatch(getAnomalyDetails(AnomalyType.RESOLVED, {}, false)); + } }; useEffect(() => { diff --git a/src/screens/caseDetails/CaseDetailStack.tsx b/src/screens/caseDetails/CaseDetailStack.tsx index a1871169..044a9192 100644 --- a/src/screens/caseDetails/CaseDetailStack.tsx +++ b/src/screens/caseDetails/CaseDetailStack.tsx @@ -31,6 +31,7 @@ import CallCustomer from './CallCustomer'; import TopAddresses from '@screens/addresses/topAddresses/TopAddresses'; import OtherAddresses from '@screens/addresses/otherAddresses/OtherAddresses'; import Escalations from '@screens/escalations/Escalations'; +import AnomalyTracker from '@screens/Dashboard/AnomalyTracker/AnomlayTracker'; const Stack = createNativeStackNavigator(); @@ -59,6 +60,7 @@ export enum CaseDetailStackEnum { TOP_ADDRESSES = 'TopAddresses', OTHER_ADDRESSES = 'OtherAddresses', ESCALATIONS = 'Escalations', + ANOMALY_TRACKER = "ANOMALY_TRACKER", } const CaseDetailStack = () => { @@ -124,6 +126,7 @@ const CaseDetailStack = () => { ))} + ); }; diff --git a/src/store/store.ts b/src/store/store.ts index 0a540170..20304340 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -40,6 +40,7 @@ import skipTracingAddressesSlice from '@reducers/skipTracingAddressesSlice'; import trainingMaterialSlice from '@reducers/trainingMaterialSlice'; import feedbackFormSlice from '@reducers/feedbackFormSlice'; import topAddressesSlice from '@reducers/topAddressesSlice'; +import anomalyTrackerSlice from '@reducers/anomalyTrackerSlice'; const rootReducer = combineReducers({ case: caseReducer, @@ -79,7 +80,8 @@ const rootReducer = combineReducers({ postOperationalHourRestrictionsSlice: postOperationalHourRestrictionsSlice, trainingMaterial: trainingMaterialSlice, feedbackForm: feedbackFormSlice, - topAddresses: topAddressesSlice + topAddresses: topAddressesSlice, + anomalyTracker: anomalyTrackerSlice, }); const persistConfig = {