From 8e80b34ceae5b200f3642d44fab608f6ed852df9 Mon Sep 17 00:00:00 2001 From: Aishwarya Srivastava Date: Mon, 2 Jun 2025 17:53:35 +0530 Subject: [PATCH] NTP-68615 | Go live anomaly tracker (#1187) --- RN-UI-LIB | 2 +- src/assets/icons/AnomalyIcon.tsx | 63 +++---- src/assets/icons/EscalatedAnomalyIcon.tsx | 43 +++++ src/common/FormInput.tsx | 158 ++++++++++++++++++ src/components/utlis/apiHelper.ts | 5 + src/reducer/anomalyTrackerSlice.ts | 51 +++++- src/reducer/userSlice.ts | 2 + .../AnomalyTracker/AnomaliesDetailsList.tsx | 4 +- .../AnomalyTracker/AnomalyOverviewCard.tsx | 43 +++-- .../AnomalyTracker/AnomalyTrackerActions.ts | 103 ++++++++++++ .../AnomalyTracker/AnomlayTracker.tsx | 13 +- .../AnomalyTracker/BottomsheetHeader.tsx | 24 +-- .../DaysTillEscalationComponent.tsx | 19 ++- .../AnomalyTracker/Forms.tsx/EtaForm.tsx | 151 +++++++++++++++++ .../AnomalyTracker/Forms.tsx/RcaForm.tsx | 156 +++++++++++++++++ .../AnomalyTracker/Forms.tsx/utils.ts | 6 + .../AnomalyTracker/RcaEtaContainer.tsx | 77 +++++---- .../AnomalyTracker/SingleAnomalyDetails.tsx | 16 +- .../AnomalyTracker/ViewRcaEtaDetails.tsx | 82 ++++++--- .../Dashboard/AnomalyTracker/constants.ts | 12 ++ .../Dashboard/AnomalyTracker/interfaces.ts | 101 ++++++++++- src/screens/Dashboard/AnomalyTracker/utils.ts | 53 ------ .../Dashboard/InternalAgentDashboard.tsx | 7 +- src/screens/Dashboard/index.tsx | 11 +- src/screens/caseDetails/CaseDetailStack.tsx | 8 +- .../notifications/NotificationItem.tsx | 12 ++ 26 files changed, 1029 insertions(+), 193 deletions(-) create mode 100644 src/assets/icons/EscalatedAnomalyIcon.tsx create mode 100644 src/common/FormInput.tsx create mode 100644 src/screens/Dashboard/AnomalyTracker/AnomalyTrackerActions.ts create mode 100644 src/screens/Dashboard/AnomalyTracker/Forms.tsx/EtaForm.tsx create mode 100644 src/screens/Dashboard/AnomalyTracker/Forms.tsx/RcaForm.tsx create mode 100644 src/screens/Dashboard/AnomalyTracker/Forms.tsx/utils.ts delete mode 100644 src/screens/Dashboard/AnomalyTracker/utils.ts diff --git a/RN-UI-LIB b/RN-UI-LIB index efa590cd..ede6bd38 160000 --- a/RN-UI-LIB +++ b/RN-UI-LIB @@ -1 +1 @@ -Subproject commit efa590cd27c169a2f479b1caf7aad0c35a81aabb +Subproject commit ede6bd382eb6e02aee1edb7b86f41be9d3763c80 diff --git a/src/assets/icons/AnomalyIcon.tsx b/src/assets/icons/AnomalyIcon.tsx index 20a47ec0..7d598ef5 100644 --- a/src/assets/icons/AnomalyIcon.tsx +++ b/src/assets/icons/AnomalyIcon.tsx @@ -1,33 +1,38 @@ +import { COLORS } from '@rn-ui-lib/colors'; +import { IconProps } from '@rn-ui-lib/icons/types'; import * as React from 'react'; -import Svg, { Path, Rect } from 'react-native-svg'; +import Svg, { Path } from 'react-native-svg'; -const AnomalyTrackerIcon = () => ( - - - - - - -); +const AnomalyTrackerIcon: React.FC = (props) => { + const { fillColor = COLORS.TEXT.YELLOW_LIGHT, width = 32, height = 36 } = props; + return ( + + + + + + + ); +}; export default AnomalyTrackerIcon; diff --git a/src/assets/icons/EscalatedAnomalyIcon.tsx b/src/assets/icons/EscalatedAnomalyIcon.tsx new file mode 100644 index 00000000..82cdabb6 --- /dev/null +++ b/src/assets/icons/EscalatedAnomalyIcon.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import Svg, { ClipPath, Defs, G, Path, Rect } from 'react-native-svg'; + +const EscalatedAnomalyIcon = () => { + return ( + + + + + + + + + + + + + + ); +}; + +export default EscalatedAnomalyIcon; diff --git a/src/common/FormInput.tsx b/src/common/FormInput.tsx new file mode 100644 index 00000000..cd2045ed --- /dev/null +++ b/src/common/FormInput.tsx @@ -0,0 +1,158 @@ +import { GenericStyles } from '@rn-ui-lib/styles'; +import React from 'react'; +import { Control, Controller, ControllerRenderProps } from 'react-hook-form'; +import { View, StyleSheet } from 'react-native'; +import Text from '@rn-ui-lib/components/Text'; +import { + BUSINESS_DATE_FORMAT, + CUSTOM_ISO_DATE_FORMAT, + dateFormat, + ISO_DATE_FORMAT, +} from '@rn-ui-lib/utils/dates'; +import dayjs from 'dayjs'; +import WebBasedDatePicker from '@rn-ui-lib/components/WebBasedDatePicker'; +import ErrorMessage from '@components/form/components/ErrorMessage'; +import { AnswerType } from '@components/form/interface'; +import RadioGroup from '@rn-ui-lib/components/radio_button/RadioGroup'; +import { IOption, IEtaFormData, IRcaFormData } from '@screens/Dashboard/AnomalyTracker/interfaces'; +import TextInput from '@rn-ui-lib/components/TextInput'; +import { COLORS } from '@rn-ui-lib/colors'; +import RNRadioButton from '@rn-ui-lib/components/radio_button/RadioButton'; + +interface IFormInputProps { + control: Control; + question: { + text: string; + type: AnswerType; + }; + maxDate?: string; + name: 'at' | 'comment' | 'reason'; + isQuestionMandatory?: boolean; + placeholder?: string; + keyboardType?: 'default' | 'number-pad' | 'email-address' | 'phone-pad'; + answerOptions?: IOption[]; + rules?: {}; + maxLength?: number; +} + +const FormInput = (props: IFormInputProps) => { + const { + control, + maxDate, + question, + name, + isQuestionMandatory = false, + placeholder, + keyboardType = 'default', + answerOptions, + rules, + maxLength = 300, + } = props; + + const todaysDate = dateFormat(new Date(), ISO_DATE_FORMAT); + + const renderInput = ( + field: ControllerRenderProps, + hasError: boolean + ) => { + const { onChange, value, onBlur } = field; + switch (question.type) { + case AnswerType.date: + let dateString = value; + if (dateString) { + const parsedDate = dayjs(dateString, CUSTOM_ISO_DATE_FORMAT, true); + dateString = parsedDate.format(ISO_DATE_FORMAT); + } + return ( + + ); + case AnswerType.option: + return ( + + {answerOptions?.map((option: IOption) => { + return ( + + ); + })} + + ); + + case AnswerType.text: + return ( + + ); + + default: + return ( + + ); + } + }; + + return ( + + + {question?.text} + {isQuestionMandatory && *} + + ( + <> + {renderInput(field, !!fieldState?.error)} + {fieldState?.error?.message ? ( + + ) : null} + + )} + /> + + ); +}; + +const styles = StyleSheet.create({ + inputContainerStyle: { + backgroundColor: COLORS.BACKGROUND.PRIMARY, + borderRadius: 8, + borderWidth: 1, + padding: 0, + borderColor: COLORS.BORDER.PRIMARY, + color: COLORS.TEXT.BLACK, + }, +}); + +export default FormInput; diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts index 8874192c..7edc961c 100644 --- a/src/components/utlis/apiHelper.ts +++ b/src/components/utlis/apiHelper.ts @@ -126,6 +126,8 @@ export enum ApiKeys { FEEDBACK_ORIGINAL_IMAGE_ACK = 'FEEDBACK_ORIGINAL_IMAGE_ACK', GET_ANOMALY_DETAILS = 'GET_ANOMALY_DETAILS', GET_ANOMALY_ACTIVITY_LOG = 'GET_ANOMALY_ACTIVITY_LOG', + GET_ANOMALY_RCA_QUESTION = 'GET_ANOMALY_RCA_QUESTION', + UPDATE_ANOMALY_ACTION = 'UPDATE_ANOMALY_ACTION', } export const API_URLS: Record = {} as Record; @@ -241,6 +243,9 @@ API_URLS[ApiKeys.GET_PRE_SIGNED_URL_FOR_FEEDBACK_IMAGE] = '/file-upload/presigne API_URLS[ApiKeys.FEEDBACK_ORIGINAL_IMAGE_ACK] = '/file-upload/acknowledge'; API_URLS[ApiKeys.GET_ANOMALY_DETAILS] = '/anomaly-tracker'; API_URLS[ApiKeys.GET_ANOMALY_ACTIVITY_LOG] = '/anomaly-tracker/activity-logs/{anomalyReferenceId}'; +API_URLS[ApiKeys.UPDATE_ANOMALY_ACTION] = '/anomaly-tracker/action/{anomalyReferenceId}'; +API_URLS[ApiKeys.GET_ANOMALY_RCA_QUESTION] = + '/anomaly-tracker/question-tree/rca/{anomalyReferenceId}'; export const API_STATUS_CODE = { OK: 200, diff --git a/src/reducer/anomalyTrackerSlice.ts b/src/reducer/anomalyTrackerSlice.ts index 85830429..db95cb75 100644 --- a/src/reducer/anomalyTrackerSlice.ts +++ b/src/reducer/anomalyTrackerSlice.ts @@ -1,8 +1,44 @@ import { createSlice } from '@reduxjs/toolkit'; +import { ActivityLog, AnomalyItem, AnomalyResponse, IOption } from '@screens/Dashboard/AnomalyTracker/interfaces'; -const initialState = { - openAnomaliesList: {}, - closedAnomaliesList: {}, +interface AnomalyTrackerState { + openAnomaliesList: AnomalyResponse; + closedAnomaliesList: AnomalyResponse; + anomalyDetailsLoading: boolean; + anomalyDetails: { + data: AnomalyItem; + loading: boolean; + }; + actionLoading: boolean; + acitvityLogs: { + data: ActivityLog[]; + loading: boolean; + }; + rcaReasons: Array<{ + options: IOption[] | undefined; label: string; value: string +}>; + updatingForm: boolean; +} + +const initialState: AnomalyTrackerState = { + openAnomaliesList: { + data: [], + pages: { + pageNo: 0, + totalPages: 0, + pageSize: 0, + totalElements: 0 + } + }, + closedAnomaliesList: { + data: [], + pages: { + pageNo: 0, + totalPages: 0, + pageSize: 0, + totalElements: 0 + } + }, anomalyDetailsLoading: false, anomalyDetails: { data: {}, @@ -13,6 +49,8 @@ const initialState = { data: [], loading: false, }, + rcaReasons: [], + updatingForm: false, }; const anomalyTrackerSlice = createSlice({ @@ -34,6 +72,12 @@ const anomalyTrackerSlice = createSlice({ setActivityLogsLoading: (state, action) => { state.acitvityLogs.loading = action.payload; }, + setRcaReasons: (state, action) => { + state.rcaReasons = action.payload; + }, + setUpdatingForm: (state, action) => { + state.updatingForm = action.payload; + }, }, }); @@ -44,6 +88,7 @@ export const { setRcaReasons, setActivityLogs, setActivityLogsLoading, + setUpdatingForm, } = anomalyTrackerSlice.actions; export default anomalyTrackerSlice.reducer; diff --git a/src/reducer/userSlice.ts b/src/reducer/userSlice.ts index 050a69ac..c0f68d32 100644 --- a/src/reducer/userSlice.ts +++ b/src/reducer/userSlice.ts @@ -76,6 +76,7 @@ export interface IUserSlice extends IUser { fieldAgentPerformanceDashboardEnabled: boolean; isCallRecordingCosmosExotelEnabled: boolean; isCosmosDiallerEnabled: boolean; + enableFieldAppAnomalyTracker: boolean; }; employeeId: string; is1To30FieldAgent: boolean; @@ -111,6 +112,7 @@ const initialState: IUserSlice = { fieldAgentPerformanceDashboardEnabled: false, isCallRecordingCosmosExotelEnabled: false, isCosmosDiallerEnabled: false, + enableFieldAppAnomalyTracker: false, }, employeeId: '', is1To30FieldAgent: false, diff --git a/src/screens/Dashboard/AnomalyTracker/AnomaliesDetailsList.tsx b/src/screens/Dashboard/AnomalyTracker/AnomaliesDetailsList.tsx index b4acd477..6474aa73 100644 --- a/src/screens/Dashboard/AnomalyTracker/AnomaliesDetailsList.tsx +++ b/src/screens/Dashboard/AnomalyTracker/AnomaliesDetailsList.tsx @@ -35,7 +35,7 @@ const AnomaliesDetailsList = ({ anomalyType }: IAnomaliesDetailsListProps) => { {openAnomaliesData?.length > 0 ? ( item?.anomalyId} + keyExtractor={(item) => item?.anomalyId as string} renderItem={({ item }) => } contentContainerStyle={[GenericStyles.pb16]} /> @@ -65,7 +65,7 @@ const AnomaliesDetailsList = ({ anomalyType }: IAnomaliesDetailsListProps) => { {closedAnomaliesData?.length > 0 ? ( item?.anomalyId} + keyExtractor={(item) => item?.anomalyId as string} renderItem={({ item }) => } contentContainerStyle={[GenericStyles.pb16]} /> diff --git a/src/screens/Dashboard/AnomalyTracker/AnomalyOverviewCard.tsx b/src/screens/Dashboard/AnomalyTracker/AnomalyOverviewCard.tsx index 080dad06..d58eaefd 100644 --- a/src/screens/Dashboard/AnomalyTracker/AnomalyOverviewCard.tsx +++ b/src/screens/Dashboard/AnomalyTracker/AnomalyOverviewCard.tsx @@ -5,34 +5,47 @@ 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 { 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 totalOpenAnomalies = useAppSelector( + (state) => state?.anomalyTracker?.openAnomaliesList?.pages?.totalElements + ); const onClick = () => { navigateToScreen(PageRouteEnum.CASE_DETAIL_STACK, { screen: CaseDetailStackEnum.ANOMALY_TRACKER, }); }; + const areAllIssuesResolved = totalOpenAnomalies === 0; return ( - + - - {totalOpenAnomalies} Open issues + + + {areAllIssuesResolved ? 'All issues are closed' : `${totalOpenAnomalies} Open issues`} + - + + View - - + + ); }; @@ -40,12 +53,10 @@ const styles = StyleSheet.create({ container: { display: 'flex', flexDirection: 'row', - borderRadius: 4, + borderRadius: 6, marginHorizontal: 16, ...getShadowStyle(2), - backgroundColor: COLORS.BACKGROUND.ORANGE, borderWidth: 1, - borderColor: COLORS.BORDER.ORANGE, marginBottom: 16, padding: 12, justifyContent: 'space-between', @@ -56,6 +67,14 @@ const styles = StyleSheet.create({ alignItems: 'center', gap: 6, }, + openAnomalyState: { + backgroundColor: COLORS.BACKGROUND.ORANGE, + borderColor: COLORS.BORDER.ORANGE, + }, + closedAnomalyState: { + backgroundColor: COLORS.BACKGROUND.GREEN, + borderColor: COLORS.BORDER.GREEN, + }, rightIcon: { marginTop: 3, }, diff --git a/src/screens/Dashboard/AnomalyTracker/AnomalyTrackerActions.ts b/src/screens/Dashboard/AnomalyTracker/AnomalyTrackerActions.ts new file mode 100644 index 00000000..15a604c1 --- /dev/null +++ b/src/screens/Dashboard/AnomalyTracker/AnomalyTrackerActions.ts @@ -0,0 +1,103 @@ +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, + setRcaReasons, + setUpdatingForm, +} from '@reducers/anomalyTrackerSlice'; +import { IEtaFormPayload, IRcaFormPayload } from './interfaces'; +import { toast } from '@rn-ui-lib/components/toast'; +import { ToastMessages } from '@screens/allCases/constants'; + +export const getAnomalyDetails = + (anomalyType: string, isLoading: boolean) => (dispatch: AppDispatch) => { + const url = getApiUrl(ApiKeys.GET_ANOMALY_DETAILS); + if (isLoading) dispatch(setAnomalyDetailsLoading(true)); + axiosInstance + .get(url, { + 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) + .then((response) => { + if (response?.status === API_STATUS_CODE.OK) { + dispatch(setActivityLogs(response?.data)); + } + }) + .catch((err) => { + toast({ + text1: ToastMessages.GENERIC_ERROR_TOAST, + type: 'error', + }); + }) + .finally(() => dispatch(setActivityLogsLoading(false))); +}; + +export const getRcaReasons = + ( + anomalyReferenceId: string, + setIsRcaReasonsLoading: React.Dispatch> + ) => + (dispatch: AppDispatch) => { + const url = getApiUrl(ApiKeys.GET_ANOMALY_RCA_QUESTION, { anomalyReferenceId }); + axiosInstance + .get(url) + .then((response) => { + if (response?.status === API_STATUS_CODE.OK) { + dispatch(setRcaReasons(response?.data)); + } + }) + .catch((err) => { + logError(err); + }) + .finally(() => setIsRcaReasonsLoading(false)); + }; + +export const updateAnomaly = + ( + anomalyReferenceId: string, + payload: IEtaFormPayload | IRcaFormPayload, + callbackFn: () => void + ) => + (dispatch: AppDispatch) => { + const url = getApiUrl(ApiKeys.UPDATE_ANOMALY_ACTION, { anomalyReferenceId }); + dispatch(setUpdatingForm(true)); + axiosInstance + .put(url, payload) + .then((response) => { + if (response.status === API_STATUS_CODE.OK) { + callbackFn?.(); + } + }) + .catch((err) => { + logError(err); + }) + .finally(() => dispatch(setUpdatingForm(false))); + }; diff --git a/src/screens/Dashboard/AnomalyTracker/AnomlayTracker.tsx b/src/screens/Dashboard/AnomalyTracker/AnomlayTracker.tsx index 8340986f..91968232 100644 --- a/src/screens/Dashboard/AnomalyTracker/AnomlayTracker.tsx +++ b/src/screens/Dashboard/AnomalyTracker/AnomlayTracker.tsx @@ -6,24 +6,22 @@ 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'; +import { getAnomalyDetails } from './AnomalyTrackerActions'; -interface IAnomalyTracker {} - -const AnomalyTracker: React.FC = (props: IAnomalyTracker) => { +const AnomalyTracker = () => { const [currentTab, setCurrentTab] = useState(AnomalyType.OPEN); const dispatch = useAppDispatch(); useEffect(() => { - dispatch(getAnomalyDetails(AnomalyType.OPEN, {}, true)); + dispatch(getAnomalyDetails(AnomalyType.OPEN, true)); }, []); const handleTabChange = (tab: string) => { if (tab === currentTab) return; if (tab === AnomalyType.OPEN) { - dispatch(getAnomalyDetails(AnomalyType.OPEN, {}, true)); + dispatch(getAnomalyDetails(AnomalyType.OPEN, true)); } else { - dispatch(getAnomalyDetails(AnomalyType.RESOLVED, {}, true)); + dispatch(getAnomalyDetails(AnomalyType.RESOLVED, true)); } setCurrentTab(tab); }; @@ -36,6 +34,7 @@ const AnomalyTracker: React.FC = (props: IAnomalyTracker) => { currentTab={currentTab} onTabChange={handleTabChange} containerStyle={[getShadowStyle(2), GenericStyles.pt12]} + tabContainerStyle={GenericStyles.ml16} /> diff --git a/src/screens/Dashboard/AnomalyTracker/BottomsheetHeader.tsx b/src/screens/Dashboard/AnomalyTracker/BottomsheetHeader.tsx index 969a5756..58a665f9 100644 --- a/src/screens/Dashboard/AnomalyTracker/BottomsheetHeader.tsx +++ b/src/screens/Dashboard/AnomalyTracker/BottomsheetHeader.tsx @@ -9,9 +9,11 @@ import UpdateIcon from '@assets/icons/UpdateIcon'; interface IBottomsheetHeaderProps { title: string; handleClose: () => void; + showUpdateButton?: boolean; + handleUpdateButtonClick: () => void; } const BottomsheetHeader = (props: IBottomsheetHeaderProps) => { - const { title, handleClose } = props; + const { title, handleClose, showUpdateButton = false, handleUpdateButtonClick } = props; return ( { View {title} - {}} //TODO - Redirect toRCA ETA form - style={[styles.title, styles.gap4]} - > - - - Update - - + {showUpdateButton ? ( + + + + Update + + + ) : null} diff --git a/src/screens/Dashboard/AnomalyTracker/DaysTillEscalationComponent.tsx b/src/screens/Dashboard/AnomalyTracker/DaysTillEscalationComponent.tsx index d063d3af..555e161f 100644 --- a/src/screens/Dashboard/AnomalyTracker/DaysTillEscalationComponent.tsx +++ b/src/screens/Dashboard/AnomalyTracker/DaysTillEscalationComponent.tsx @@ -3,6 +3,7 @@ import { View, StyleSheet } from 'react-native'; import Text from '@rn-ui-lib/components/Text'; import { COLORS } from '@rn-ui-lib/colors'; import AnimatedCircularLoaderIcon from '@assets/icons/AnimatedCircularLoaderIcon'; +import EscalatedAnomalyIcon from '@assets/icons/EscalatedAnomalyIcon'; const DaysTillEscalationComponent = (props: { dayTillEscalation: number; @@ -19,16 +20,13 @@ const DaysTillEscalationComponent = (props: { circumference={circumference} progressPercent={dayTillEscalation > 0 ? progressPercent : 0} /> - + {dayTillEscalation > 0 ? ( {dayTillEscalation} ) : ( - 0 + + + )} @@ -36,5 +34,12 @@ const DaysTillEscalationComponent = (props: { }; const styles = StyleSheet.create({ textContainer: { fontSize: 14, color: COLORS.TEXT.YELLOW, fontWeight: '700' }, + escalationTextContainer: { + position: 'absolute', + transform: [{ translateX: 82 }], + }, + iconContainer: { + transform: [{ translateY: 6 }, { translateX: 4 }], + }, }); export default DaysTillEscalationComponent; diff --git a/src/screens/Dashboard/AnomalyTracker/Forms.tsx/EtaForm.tsx b/src/screens/Dashboard/AnomalyTracker/Forms.tsx/EtaForm.tsx new file mode 100644 index 00000000..c12d33e2 --- /dev/null +++ b/src/screens/Dashboard/AnomalyTracker/Forms.tsx/EtaForm.tsx @@ -0,0 +1,151 @@ +import React from 'react'; +import { View, StyleSheet, SafeAreaView, ScrollView, ActivityIndicator } from 'react-native'; +import NavigationHeader, { Icon } from '@rn-ui-lib/components/NavigationHeader'; +import { GenericStyles } from '@rn-ui-lib/styles'; +import { goBack, navigateToScreen } from '@components/utlis/navigationUtlis'; +import Button from '@rn-ui-lib/components/Button'; +import { useForm } from 'react-hook-form'; +import FormInput from '@common/FormInput'; +import { AnswerType } from '@components/form/interface'; +import { useAppDispatch, useAppSelector } from '@hooks'; +import { getAnomalyDetails, updateAnomaly } from '../AnomalyTrackerActions'; +import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack'; +import { PageRouteEnum } from '@screens/auth/ProtectedRouter'; +import { toast } from '@rn-ui-lib/components/toast'; +import { dateFormat, ISO_DATE_FORMAT } from '@rn-ui-lib/utils/dates'; +import { COLORS } from '@rn-ui-lib/colors'; +import { AnomalyType, VALIDATION_ERROR_MESSAGES } from '../constants'; +import { IEtaFormData } from '../interfaces'; +import { getLastDateOfNextMonth } from './utils'; + +interface IEtaForm { + route: { + params: { + selectedAnomalyId: string; + }; + }; +} +const EtaForm = (props: IEtaForm) => { + const { selectedAnomalyId } = props.route.params || {}; + const showLoader = useAppSelector((state) => state?.anomalyTracker?.updatingForm); + const dispatch = useAppDispatch(); + const { + control, + reset, + formState: { isValid }, + handleSubmit, + } = useForm({ + defaultValues: { + at: '', + reason: '', + }, + mode: 'onBlur', + }); + const successCallback = () => { + reset(); + toast({ + text1: 'ETA submitted successfully', + type: 'success', + }); + dispatch(getAnomalyDetails(AnomalyType.OPEN, true)); + navigateToScreen(PageRouteEnum.CASE_DETAIL_STACK, { + screen: CaseDetailStackEnum.ANOMALY_TRACKER, + }); + }; + const submitETA = (data: IEtaFormData) => { + dispatch(updateAnomaly(selectedAnomalyId, { eta: data }, successCallback)); + }; + + const maxDate = dateFormat(getLastDateOfNextMonth(), ISO_DATE_FORMAT); + + return ( + + + {showLoader ? ( + + + + ) : ( + + + + + + + + +