diff --git a/RN-UI-LIB b/RN-UI-LIB index 348345f1..810ff6ea 160000 --- a/RN-UI-LIB +++ b/RN-UI-LIB @@ -1 +1 @@ -Subproject commit 348345f1bb3af8b78a8246231dbc440ddc02de32 +Subproject commit 810ff6ea12df6cbfcec37c82b01e6909f620fef0 diff --git a/android/app/build.gradle b/android/app/build.gradle index 09ada5f8..b70bda37 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -113,8 +113,8 @@ def jscFlavor = 'org.webkit:android-jsc:+' def enableHermes = project.ext.react.get("enableHermes", false); -def VERSION_CODE = 218 -def VERSION_NAME = "2.15.4" +def VERSION_CODE = 219 +def VERSION_NAME = "2.15.5" android { namespace "com.avapp" diff --git a/package.json b/package.json index 556a9301..9bb29820 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "AV_APP", - "version": "2.15.4", - "buildNumber": "218", + "version": "2.15.5", + "buildNumber": "219", "private": true, "scripts": { "android:dev": "yarn move:dev && react-native run-android", diff --git a/src/common/TrackingComponent.tsx b/src/common/TrackingComponent.tsx index dd01ba5e..b8c236a0 100644 --- a/src/common/TrackingComponent.tsx +++ b/src/common/TrackingComponent.tsx @@ -57,6 +57,8 @@ import { getSyncUrl } from '@services/syncJsonDataToBe'; import { handleCheckAndUpdatePullToRefreshStateForNearbyCases } from '@screens/allCases/utils'; import { getWifiDetailsSyncUrl } from '@components/utlis/WifiDetails'; import useFirestoreUpdates from '@hooks/useFirestoreUpdates'; +import { handlePostOperativeHourActivity } from '@screens/caseDetails/utils/postOperationalHourActions'; +import { setPostOperationalHourRestrictions } from '@reducers/postOperationalHourRestrictionsSlice'; export enum FOREGROUND_TASKS { GEOLOCATION = 'GEOLOCATION', @@ -104,6 +106,7 @@ const TrackingComponent: React.FC = ({ children }) => { if (timestamp) { const isTimeDifferenceLess = isTimeDifferenceWithinRange(timestamp, 5); dispatch(setIsTimeSynced(isTimeDifferenceLess)); + dispatch(setPostOperationalHourRestrictions(handlePostOperativeHourActivity(timestamp))); } } catch (e: any) { logError(e, 'Error during fetching timestamp from server.'); diff --git a/src/components/form/components/GeolocationAddress.tsx b/src/components/form/components/GeolocationAddress.tsx index 5437a3de..3bc22a6f 100644 --- a/src/components/form/components/GeolocationAddress.tsx +++ b/src/components/form/components/GeolocationAddress.tsx @@ -1,5 +1,5 @@ import { Linking, StyleSheet, TouchableOpacity, View } from 'react-native'; -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { GeolocationSource, GeolocationSourceMap, @@ -31,6 +31,7 @@ import { IAddressFeedback } from '../../../reducer/addressSlice'; import Tag, { TagVariant } from '@rn-ui-lib/components/Tag'; import ArrowSolidIcon from '@rn-ui-lib/icons/ArrowSolidIcon'; import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack'; +import { handleClickPostOperativeHours } from '@screens/addressGeolocation/utils/operativeHourUtils'; interface IGeolocationAddress { address: IGeolocation; @@ -104,7 +105,9 @@ const GeolocationAddress: React.FC = ({ ); return { addressDate, addressTime, lastFeedbackTimestampDate }; }, [lastFeedbackForGeolocation, timestamp]); - + const addingNewFeedbackDisabled = useAppSelector( + (state) => state?.postOperationalHourRestrictionsSlice?.postOperationalHourRestrictions + ); const handleCloseRouting = () => handlePageRouting?.(CaseDetailStackEnum.ADDRESS_GEO); const handleAddFeedback = () => { @@ -297,10 +300,10 @@ const GeolocationAddress: React.FC = ({ {showAddFeedback ? ( - + Add feedback @@ -364,6 +367,13 @@ const styles = StyleSheet.create({ tagText: { lineHeight: 18, }, + disabledButton: { + fontSize: 13, + lineHeight: 20, + color: COLORS.TEXT.BLUE, + paddingVertical: 4, + opacity: 0.5 + }, }); export default GeolocationAddress; diff --git a/src/components/form/index.tsx b/src/components/form/index.tsx index a8e46e27..d3b783ea 100644 --- a/src/components/form/index.tsx +++ b/src/components/form/index.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useRef, useState} from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useForm } from 'react-hook-form'; import { ScrollView, StyleSheet, View } from 'react-native'; import Geolocation from 'react-native-geolocation-service'; @@ -41,9 +41,11 @@ import NavigationHeader, { Icon } from '../../../RN-UI-LIB/src/components/Naviga import { CaseDetailStackEnum } from '@screens/caseDetails/CaseDetailStack'; import { useNavigation, useRoute } from '@react-navigation/native'; import { NUDGE_BOTTOM_SHEET_DEFAULT_STATE } from './constants'; -import {useBackHandler} from "@hooks/useBackHandler"; +import { useBackHandler } from '@hooks/useBackHandler'; import { CALLING_NUDGE } from '@screens/caseDetails/CallingFlow/constants'; import { isFunction } from '@components/utlis/commonFunctions'; +import { setPostOperationalHourRestrictions } from '@reducers/postOperationalHourRestrictionsSlice'; +import { getSyncTime } from '@hooks/capturingApi'; interface IWidget { route: { @@ -197,7 +199,7 @@ const Widget: React.FC = (props) => { journey: journey, caseId, handleCloseRouting, - from + from, }); }; @@ -257,11 +259,13 @@ const Widget: React.FC = (props) => { widgetId: name, } ); - fetchLocation().then((location) => { + fetchLocation() + .then((location) => { if (location) { return handleSubmitJourney(data, location); } - }).catch((err) => { + }) + .catch((err) => { setIsSubmitting(false); }); }; @@ -279,6 +283,13 @@ const Widget: React.FC = (props) => { suspiciousFeedbackMessage: errObj?.errorCode, }); } + if (errObj?.statusCode === API_STATUS_CODE.POST_OPERATIVE_HOURS_ACTIVITY) { + toast({ + type: 'error', + text1: ToastMessages.POST_OPERATIVE_HOURS_ACTIVITY, + }); + navigateToScreen(CaseDetailStackEnum.COLLECTION_CASE_DETAIL, { caseId }); + } }; const handleSubmitJourney = async (data: any, coords: Geolocation.GeoCoordinates) => { @@ -307,12 +318,12 @@ const Widget: React.FC = (props) => { unSyncedCase, nudgeBottomSheetDetails?.showNudgeBottomSheet ); - if(!transformedPayload?.data?.answers) { + if (!transformedPayload?.data?.answers) { toast({ type: 'error', text1: ToastMessages.FEEDBACK_IMAGE_NOT_FOUND, }); - onErrorSubmit({}, transformedPayload) + onErrorSubmit({}, transformedPayload); return; } dispatch( @@ -465,7 +476,7 @@ const Widget: React.FC = (props) => { style={[styles.autoFlex, styles.mH16]} title={isLeaf ? 'Submit' : 'Next'} showLoader={isLeaf && isSubmitting} - disabled={isLeaf && isSubmitting} + disabled={(isLeaf && isSubmitting)} onPress={ isLeaf ? handleSubmit(submitJourneyWithGeoLocation, onError) diff --git a/src/components/utlis/apiHelper.ts b/src/components/utlis/apiHelper.ts index 060b9d5a..e0145515 100644 --- a/src/components/utlis/apiHelper.ts +++ b/src/components/utlis/apiHelper.ts @@ -227,6 +227,7 @@ export const API_STATUS_CODE = { INTERNAL_SERVER_ERROR: 500, TOO_MANY_REQUESTS: 429, GONE: 410, + POST_OPERATIVE_HOURS_ACTIVITY: 451 }; export const UNAUTHORIZED_VALUES = [API_STATUS_CODE.UNAUTHORIZED, API_STATUS_CODE.FORBIDDEN]; diff --git a/src/reducer/postOperationalHourRestrictionsSlice.ts b/src/reducer/postOperationalHourRestrictionsSlice.ts new file mode 100644 index 00000000..33b3f1ec --- /dev/null +++ b/src/reducer/postOperationalHourRestrictionsSlice.ts @@ -0,0 +1,24 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; + +const initialState = { + postOperationalHourRestrictions: false +}; + +const postOperationalHourRestrictionsSlice = createSlice({ + name: 'postOperationalHourRestrictions', + initialState, + reducers: { + setPostOperationalHourRestrictions: (state, action) => { + state.postOperationalHourRestrictions = action.payload; + } + }, +}); + +export const { + setPostOperationalHourRestrictions +} = postOperationalHourRestrictionsSlice.actions; + +export default postOperationalHourRestrictionsSlice.reducer; + + + diff --git a/src/screens/addressGeolocation/AddressItem.tsx b/src/screens/addressGeolocation/AddressItem.tsx index 06f055d3..edf18aba 100644 --- a/src/screens/addressGeolocation/AddressItem.tsx +++ b/src/screens/addressGeolocation/AddressItem.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { View, StyleSheet, type ViewStyle, TouchableOpacity } from 'react-native'; import dayjs from 'dayjs'; import Text from '../../../RN-UI-LIB/src/components/Text'; @@ -19,6 +19,7 @@ import { addClickstreamEvent } from '@services/clickstreamEventService'; import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants'; import CopyIcon from '@rn-ui-lib/icons/CopyIcon'; import { copyAddressToClipboard } from './utils/copyAddressText'; +import { handleClickPostOperativeHours } from './utils/operativeHourUtils'; interface IAddressItem { addressItem: IAddress; @@ -58,7 +59,9 @@ function AddressItem({ ); const dispatch = useAppDispatch(); - + const addingNewFeedbackDisabled = useAppSelector( + (state) => state?.postOperationalHourRestrictionsSlice?.postOperationalHourRestrictions + ); let relativeDistanceBwLatLong = 0; const addressGeolocationCoordinated: IGeolocationCoordinate = { @@ -233,10 +236,10 @@ function AddressItem({ - Add Feedback + Add Feedback {lastFeedbackForAddress?.feedbackPresent ? ( state?.postOperationalHourRestrictionsSlice?.postOperationalHourRestrictions); + const copyAddress = () => { copyAddressToClipboard(addressItem?.addressText, caseId); }; @@ -193,11 +195,11 @@ function SimilarAddressItem({ {showAddFeedbackBtn ? ( - Add Feedback + Add Feedback ) : null} {showOldFeedbackBtn ? ( @@ -251,6 +253,13 @@ const styles = StyleSheet.create({ fontWeight: '500', color: COLORS.TEXT.BLUE, }, + disabledButton: { + fontSize: 13, + lineHeight: 20, + fontWeight: '500', + color: COLORS.TEXT.BLUE, + opacity: 0.5 + }, dotStyle: { fontSize: 11, color: COLORS.TEXT.LIGHT, diff --git a/src/screens/addressGeolocation/utils/operativeHourUtils.tsx b/src/screens/addressGeolocation/utils/operativeHourUtils.tsx new file mode 100644 index 00000000..20cdf712 --- /dev/null +++ b/src/screens/addressGeolocation/utils/operativeHourUtils.tsx @@ -0,0 +1,16 @@ +import { COLORS } from '@rn-ui-lib/colors'; +import { toast } from '@rn-ui-lib/components/toast'; +import InfoIcon from '@rn-ui-lib/icons/InfoIcon'; +import { ToastMessages } from '@screens/allCases/constants'; + +export const handleClickPostOperativeHours = () => { + toast({ + type: 'custom', + text1: ToastMessages.DISABLE_ADD_FEEDBACK_AFTER_POST_OPERATIVE_HOURS, + props: { + customBackgroundColor: COLORS.BACKGROUND.ORANGE_LIGHT, + customIcon: + }, + }); + return; +}; diff --git a/src/screens/allCases/constants.ts b/src/screens/allCases/constants.ts index be787421..9631823b 100644 --- a/src/screens/allCases/constants.ts +++ b/src/screens/allCases/constants.ts @@ -95,6 +95,8 @@ export const ToastMessages = { WHATSAPP_SHARE_SUCCESS: 'Document shared successfully via WhatsApp', WHATSAPP_SHARE_FAILURE: 'Document sharing failed via WhatsApp', ERROR_FETCHING_MULTILINGUAL_DOC : 'Error fetching multilingual document', + POST_OPERATIVE_HOURS_ACTIVITY: 'Submission failed! You can add feedback only during work hours (8 AM to 6:55 PM)', + DISABLE_ADD_FEEDBACK_AFTER_POST_OPERATIVE_HOURS: 'You will be able to add feedback only during work hours (8 AM to 6:55 PM)' }; export enum BOTTOM_TAB_ROUTES { diff --git a/src/screens/auth/AuthRouter.tsx b/src/screens/auth/AuthRouter.tsx index 8e8dd9af..41fa4ec1 100644 --- a/src/screens/auth/AuthRouter.tsx +++ b/src/screens/auth/AuthRouter.tsx @@ -26,6 +26,9 @@ import { getSyncTime } from '@hooks/capturingApi'; import ModalWrapperForAlfredV2 from '@common/ModalWrapperForAlfredV2'; import IdCardApproved from '@screens/AgentIdCard/IdCardStatus/IdCardApproved'; import CallingFeedbackNudgeBottomSheet from '@screens/caseDetails/CallingFlow/BottomSheets/CallingFeedbackNudgeBottomSheet'; +import { handlePostOperativeHourActivity } from '@screens/caseDetails/utils/postOperationalHourActions'; +import { setPostOperationalHourRestrictions } from '@reducers/postOperationalHourRestrictionsSlice'; +import { logError } from '@components/utlis/errorUtils'; function AuthRouter() { const dispatch = useAppDispatch(); @@ -67,7 +70,15 @@ function AuthRouter() { } }; const CHECK_ATTENDANCE_TIME = 10000; - + const syncTimeToCheckPostOperativeHours = async () => { + try { + const timestamp = await getSyncTime(); + const getActivityStatus = handlePostOperativeHourActivity(timestamp); + dispatch(setPostOperationalHourRestrictions(getActivityStatus)); + } catch (error) { + logError(error as Error); + } + }; useEffect(() => { const appStateChange = AppState.addEventListener('change', async (change) => { if (change !== 'active') return; @@ -82,6 +93,7 @@ function AuthRouter() { } alfredSetCodePushVersion(getAppVersion()); }); + syncTimeToCheckPostOperativeHours(); return () => { appStateChange.remove(); }; diff --git a/src/screens/caseDetails/CollectionCaseDetailFooter.tsx b/src/screens/caseDetails/CollectionCaseDetailFooter.tsx index d9c9e310..d13cf3da 100644 --- a/src/screens/caseDetails/CollectionCaseDetailFooter.tsx +++ b/src/screens/caseDetails/CollectionCaseDetailFooter.tsx @@ -14,6 +14,7 @@ import React, { useEffect } from 'react'; import { StyleSheet, View } from 'react-native'; import { CaseDetailStackEnum } from './CaseDetailStack'; import { captureLatestDeviceLocation } from '@components/form/services/geoLocation.service'; +import { handleClickPostOperativeHours } from '@screens/addressGeolocation/utils/operativeHourUtils'; interface ICollectionCaseDetailFooter { caseId: string; @@ -29,7 +30,9 @@ const CollectionCaseDetailFooter = ({ caseId, notificationId }: ICollectionCaseD (state: RootState) => state.case.caseForm?.[caseId]?.[TaskTitleUIMapping.COLLECTION_FEEDBACK] ); const dispatch = useAppDispatch(); - + const addingNewFeedbackDisabled = useAppSelector( + (state) => state?.postOperationalHourRestrictionsSlice?.postOperationalHourRestrictions + ); const handleCustomerCall = () => { addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_CALL_CUSTOMER_CLICKED, { caseId: caseId, @@ -106,7 +109,7 @@ const CollectionCaseDetailFooter = ({ caseId, notificationId }: ICollectionCaseD testID={'test_call_customer'} />