NTP-14132| restrict feedback post operative hours

This commit is contained in:
aishwarya.srivastava
2024-11-27 03:23:21 +05:30
parent a9a36c6772
commit 72cce0249f
10 changed files with 195 additions and 22 deletions

View File

@@ -106,6 +106,7 @@ export const syncCaseDetail =
type: 'success',
text1: ToastMessages.FEEDBACK_SUCCESSFUL,
});
//TODO: Aishwarya
if (callbacks?.onSuccessCB != null && typeof callbacks?.onSuccessCB === 'function') {
callbacks?.onSuccessCB(payload.data.answers, interactionId);
}

View File

@@ -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,9 @@ 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';
import { handleTimeSync } from '@screens/caseDetails/utils/postOperationalHourActions';
import { setPostOperationalHourRestrictions } from '@reducers/postOperationalHourRestrictionsSlice';
interface IGeolocationAddress {
address: IGeolocation;
@@ -104,9 +107,21 @@ const GeolocationAddress: React.FC<IGeolocationAddress> = ({
);
return { addressDate, addressTime, lastFeedbackTimestampDate };
}, [lastFeedbackForGeolocation, timestamp]);
const addingNewFeedbackDisabled = useAppSelector(
(state) => state.postOperationalHourRestrictionsSlice.postOperationalHourRestrictions
);
const handleCloseRouting = () => handlePageRouting?.(CaseDetailStackEnum.ADDRESS_GEO);
useEffect(() => {
const syncTime = async () => {
const getCurrentTime = await handleTimeSync();
if (getCurrentTime?.isPostOperational) {
dispatch(setPostOperationalHourRestrictions(getCurrentTime?.isPostOperational));
}
};
syncTime();
const intervalId = setInterval(syncTime, 180000);
return () => clearInterval(intervalId);
}, []);
const handleAddFeedback = () => {
if (!caseId) {
return;
@@ -297,10 +312,10 @@ const GeolocationAddress: React.FC<IGeolocationAddress> = ({
{showAddFeedback ? (
<TouchableOpacity
activeOpacity={0.7}
onPress={handleAddFeedback}
onPress={addingNewFeedbackDisabled? handleClickPostOperativeHours: handleAddFeedback}
style={GenericStyles.ml20}
>
<Text style={styles.openMapBtn} bold>
<Text style={addingNewFeedbackDisabled? styles.disabledButton : styles.openMapBtn} bold>
Add feedback
</Text>
</TouchableOpacity>
@@ -364,6 +379,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;

View File

@@ -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';
@@ -44,6 +44,8 @@ import { NUDGE_BOTTOM_SHEET_DEFAULT_STATE } from './constants';
import {useBackHandler} from "@hooks/useBackHandler";
import { CALLING_NUDGE } from '@screens/caseDetails/CallingFlow/constants';
import { isFunction } from '@components/utlis/commonFunctions';
import { handleTimeSync } from '@screens/caseDetails/utils/postOperationalHourActions';
import { setPostOperationalHourRestrictions } from '@reducers/postOperationalHourRestrictionsSlice';
interface IWidget {
route: {
@@ -98,9 +100,22 @@ const Widget: React.FC<IWidget> = (props) => {
}
setIsJourneyFirstScreen(isFirst);
}, [templateData, name]);
const addingNewFeedbackDisabled = useAppSelector((state) => state.postOperationalHourRestrictionsSlice.postOperationalHourRestrictions);
const [isSubmitButtonDiabled, setIsSubmitButtonDiabled] = useState(false);
useEffect(() => {
const syncTime = async () => {
const getCurrentTime = await handleTimeSync();
if (getCurrentTime?.isPostOperational) {
dispatch(setPostOperationalHourRestrictions(getCurrentTime?.isPostOperational));
}
};
syncTime();
const intervalId = setInterval(syncTime, 15000);
return () => clearInterval(intervalId);
}, []);
const handleBackPress = useCallback(() => {
if (isSubmitting) {
if (isSubmitting && ! addingNewFeedbackDisabled) {
toast({
type: 'info',
text1: ToastMessages.FEEDBACK_SUBMISSION_UNDER_PROCESS,
@@ -112,6 +127,13 @@ const Widget: React.FC<IWidget> = (props) => {
}, [isSubmitting]);
useBackHandler(handleBackPress);
const handleDisabledButtonClick = () => {
setIsSubmitButtonDiabled(true);
toast({
type: 'error',
text1: 'Submission failed! You can add feedback only during work hours (8 AM to 7 PM)',
})
};
const {
control,
@@ -366,7 +388,7 @@ const Widget: React.FC<IWidget> = (props) => {
}, []);
const handleCloseIconPress = () => {
if (isSubmitting) {
if (isSubmitting && !addingNewFeedbackDisabled) {
toast({
type: 'info',
text1: ToastMessages.FEEDBACK_SUBMISSION_UNDER_PROCESS,
@@ -464,11 +486,13 @@ const Widget: React.FC<IWidget> = (props) => {
<Button
style={[styles.autoFlex, styles.mH16]}
title={isLeaf ? 'Submit' : 'Next'}
showLoader={isLeaf && isSubmitting}
disabled={isLeaf && isSubmitting}
showLoader={isLeaf && isSubmitting && !addingNewFeedbackDisabled}
disabled={(isLeaf && isSubmitting) || isSubmitButtonDiabled}
onPress={
isLeaf
? handleSubmit(submitJourneyWithGeoLocation, onError)
? (addingNewFeedbackDisabled
? handleDisabledButtonClick
: handleSubmit(submitJourneyWithGeoLocation, onError))
: handleSubmit(onSubmit, onError)
}
rightIcon={
@@ -479,7 +503,7 @@ const Widget: React.FC<IWidget> = (props) => {
</View>
<NudgeSuspiciousFeedbackBottomSheet
caseId={caseId}
successBtnLoader={isSubmitting}
successBtnLoader={isSubmitting && !addingNewFeedbackDisabled}
nudgeBottomSheetDetails={nudgeBottomSheetDetails}
setNudgeBottomSheetDetails={setNudgeBottomSheetDetails}
successCallbackFn={handleSubmit(

View File

@@ -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;

View File

@@ -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,9 @@ 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 { setPostOperationalHourRestrictions } from '@reducers/postOperationalHourRestrictionsSlice';
import { handleTimeSync } from '@screens/caseDetails/utils/postOperationalHourActions';
import { handleClickPostOperativeHours } from './utils/operativeHourUtils';
interface IAddressItem {
addressItem: IAddress;
@@ -58,7 +61,9 @@ function AddressItem({
);
const dispatch = useAppDispatch();
const addingNewFeedbackDisabled = useAppSelector(
(state) => state.postOperationalHourRestrictionsSlice.postOperationalHourRestrictions
);
let relativeDistanceBwLatLong = 0;
const addressGeolocationCoordinated: IGeolocationCoordinate = {
@@ -69,6 +74,17 @@ function AddressItem({
currentGeolocationCoordinates,
addressGeolocationCoordinated
);
useEffect(() => {
const syncTime = async () => {
const getCurrentTime = await handleTimeSync();
if (getCurrentTime?.isPostOperational) {
dispatch(setPostOperationalHourRestrictions(getCurrentTime?.isPostOperational));
}
};
syncTime();
const intervalId = setInterval(syncTime, 180000);
return () => clearInterval(intervalId);
}, []);
const handleAddFeedback = () => {
if (prefilledAddressScreenTemplate != null) {
@@ -233,10 +249,10 @@ function AddressItem({
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
onPress={handleAddFeedback}
onPress={addingNewFeedbackDisabled? handleClickPostOperativeHours: handleAddFeedback}
hitSlop={{ top: 25, bottom: 25, left: 15, right: 15 }}
>
<Text style={styles.actionBtn}>Add Feedback</Text>
<Text style={addingNewFeedbackDisabled? styles.disabledButton : styles.actionBtn}>Add Feedback</Text>
</TouchableOpacity>
{lastFeedbackForAddress?.feedbackPresent ? (
<TouchableOpacity
@@ -294,6 +310,13 @@ const styles = StyleSheet.create({
fontSize: 11,
color: COLORS.TEXT.LIGHT,
},
disabledButton: {
fontSize: 13,
lineHeight: 20,
fontWeight: '500',
color: COLORS.TEXT.BLUE,
opacity: 0.5
}
});
export default AddressItem;

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import { View, StyleSheet, type ViewStyle, TouchableOpacity, Linking } from 'react-native';
import Text from '../../../RN-UI-LIB/src/components/Text';
import { type IAddress, type IGeolocationCoordinate } from '../../types/addressGeolocation.types';
@@ -24,6 +24,9 @@ import AddressSource from './AddressSource';
import relativeDistanceFormatter from './utils/relativeDistanceFormatter';
import CopyIcon from '@rn-ui-lib/icons/CopyIcon';
import { copyAddressToClipboard } from './utils/copyAddressText';
import { handleTimeSync } from '@screens/caseDetails/utils/postOperationalHourActions';
import { setPostOperationalHourRestrictions } from '@reducers/postOperationalHourRestrictionsSlice';
import { handleClickPostOperativeHours } from './utils/operativeHourUtils';
interface IAddressItem {
addressItem: IAddress;
@@ -118,6 +121,18 @@ function SimilarAddressItem({
}
}
};
const addingNewFeedbackDisabled = useAppSelector((state) => state.postOperationalHourRestrictionsSlice.postOperationalHourRestrictions);
useEffect(() => {
const syncTime = async () => {
const getCurrentTime = await handleTimeSync();
if (getCurrentTime?.isPostOperational) {
dispatch(setPostOperationalHourRestrictions(getCurrentTime?.isPostOperational));
}
};
syncTime();
const intervalId = setInterval(syncTime, 180000);
return () => clearInterval(intervalId);
}, []);
const copyAddress = () => {
copyAddressToClipboard(addressItem?.addressText, caseId);
@@ -193,11 +208,11 @@ function SimilarAddressItem({
{showAddFeedbackBtn ? (
<TouchableOpacity
activeOpacity={0.7}
onPress={handleAddFeedback}
onPress={addingNewFeedbackDisabled ? handleClickPostOperativeHours : handleAddFeedback}
hitSlop={{ top: 25, bottom: 25, left: 15, right: 15 }}
style={GenericStyles.mh8}
>
<Text style={styles.actionBtn}>Add Feedback</Text>
<Text style={addingNewFeedbackDisabled ? styles.disabledButton :styles.actionBtn}>Add Feedbackj</Text>
</TouchableOpacity>
) : null}
{showOldFeedbackBtn ? (
@@ -251,6 +266,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,

View File

@@ -0,0 +1,10 @@
import { toast } from "@rn-ui-lib/components/toast";
export const handleClickPostOperativeHours = () => {
toast({
type: 'error',
text1: 'You will be able to add feedback only during work hours (8 AM to 7 PM).',
});
return;
};

View File

@@ -14,6 +14,9 @@ import React, { useEffect } from 'react';
import { StyleSheet, View } from 'react-native';
import { CaseDetailStackEnum } from './CaseDetailStack';
import { captureLatestDeviceLocation } from '@components/form/services/geoLocation.service';
import { handleTimeSync } from './utils/postOperationalHourActions';
import { handleClickPostOperativeHours } from '@screens/addressGeolocation/utils/operativeHourUtils';
import { setPostOperationalHourRestrictions } from '@reducers/postOperationalHourRestrictionsSlice';
interface ICollectionCaseDetailFooter {
caseId: string;
@@ -29,7 +32,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,
@@ -77,6 +82,17 @@ const CollectionCaseDetailFooter = ({ caseId, notificationId }: ICollectionCaseD
}
);
};
useEffect(() => {
const syncTime = async () => {
const getCurrentTime = await handleTimeSync();
if (getCurrentTime?.isPostOperational) {
dispatch(setPostOperationalHourRestrictions(getCurrentTime?.isPostOperational));
}
};
syncTime();
const intervalId = setInterval(syncTime, 180000);
return () => clearInterval(intervalId);
}, []);
useEffect(() => {
// If landed via notification action
@@ -116,8 +132,9 @@ const CollectionCaseDetailFooter = ({ caseId, notificationId }: ICollectionCaseD
) : null
}
variant="primary"
onPress={handleAddFeedback}
onPress={addingNewFeedbackDisabled ? handleClickPostOperativeHours : handleAddFeedback}
testID={'test_add_feedback'}
isClickableButDisabled={addingNewFeedbackDisabled}
/>
</View>
);

View File

@@ -0,0 +1,28 @@
import { logError } from '@components/utlis/errorUtils';
import { getSyncTime } from '@hooks/capturingApi';
export const getTodaysDate = async () => {
const timestamp = await getSyncTime();
const date = new Date(timestamp);
return date;
};
export const handleTimeSync = async () => {
try {
const timestamp = await getSyncTime();
if (timestamp) {
const todaysDate = new Date(timestamp);
const currentHour = todaysDate.getTime();
const loginStartTime = todaysDate.setHours(8, 0, 0);
const loginEndTime = todaysDate.setHours(19, 0, 0);
return {
isPostOperational: currentHour < loginStartTime || currentHour > loginEndTime
};
}
} catch (e: any) {
logError(e, 'Error during fetching timestamp from server.');
}
};

View File

@@ -35,6 +35,7 @@ import activeCallSlice from '@reducers/activeCallSlice';
import documentsSlice from '@reducers/documentsSlice';
import topFeedbacksSlice from '@reducers/topFeedbacksSlice';
import escalationSlice from '@reducers/escalationSlice';
import postOperationalHourRestrictionsSlice from '@reducers/postOperationalHourRestrictionsSlice';
const rootReducer = combineReducers({
case: caseReducer,
@@ -70,6 +71,7 @@ const rootReducer = combineReducers({
documentsSlice: documentsSlice,
topFeedbacks: topFeedbacksSlice,
escalationSlice: escalationSlice,
postOperationalHourRestrictionsSlice: postOperationalHourRestrictionsSlice
});
const persistConfig = {