From 4f636e60bb2a703ebc2525abb9da913c575e19af Mon Sep 17 00:00:00 2001 From: Ishan Srivastava Date: Tue, 28 Feb 2023 14:29:38 +0530 Subject: [PATCH] feat: added changes for qa release on feedback flow (#114) --- App.tsx | 3 ++ src/action/dataActions.ts | 2 + .../form/components/ErrorMessage.tsx | 10 ++-- .../form/components/ImageUpload.tsx | 4 +- src/components/form/components/Rating.tsx | 2 +- src/components/form/components/TextInput.tsx | 1 + src/components/form/index.tsx | 49 +++++++++---------- .../services/conditionEvaluation.service.ts | 8 +-- src/components/form/services/forms.service.ts | 2 +- .../form/services/validation.service.ts | 2 +- 10 files changed, 43 insertions(+), 40 deletions(-) diff --git a/App.tsx b/App.tsx index 6d354065..bf6c6fe9 100644 --- a/App.tsx +++ b/App.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { LogBox } from 'react-native'; import {Provider} from 'react-redux'; import store, {persistor} from './src/store/store'; import {PersistGate} from 'redux-persist/integration/react'; @@ -25,6 +26,8 @@ if (ENV !== 'prod') { mockApiServer(); } +LogBox.ignoreAllLogs(); + const App = () => { useNativeButtons(); return ( diff --git a/src/action/dataActions.ts b/src/action/dataActions.ts index 18cab7cf..22e7d3c0 100644 --- a/src/action/dataActions.ts +++ b/src/action/dataActions.ts @@ -21,6 +21,7 @@ import { AppDispatch } from '../store/store'; import { addClickstreamEvent } from '../services/clickstreamEventService'; import { CLICKSTREAM_EVENT_NAMES } from '../common/Constants'; import { logError } from '../components/utlis/errorUtils'; +import { toast } from "../../RN-UI-LIB/src/components/toast"; export const getAllCases = () => (dispatch: AppDispatch) => { const url = getApiUrl(ApiKeys.ALL_CASES); @@ -104,6 +105,7 @@ export const syncCaseDetail = (data: any) => (dispatch: AppDispatch) => { .then(res => { dispatch(updateSingleCase({ data: res.data, id: data.id })); OfflineImageDAO.deleteImages(offlineImageIdList); + toast({ type: 'success', text1: 'Feedback recorded successfully!' }) }); }; diff --git a/src/components/form/components/ErrorMessage.tsx b/src/components/form/components/ErrorMessage.tsx index 76bf43c2..8a2c442e 100644 --- a/src/components/form/components/ErrorMessage.tsx +++ b/src/components/form/components/ErrorMessage.tsx @@ -14,14 +14,14 @@ const ErrorMessage: React.FC = props => { } return ( - - {' '} - + + + {show?.message || 'This is mandatory field'} - - ); + + ); }; export default ErrorMessage; diff --git a/src/components/form/components/ImageUpload.tsx b/src/components/form/components/ImageUpload.tsx index c82d55c6..eedfb2e1 100644 --- a/src/components/form/components/ImageUpload.tsx +++ b/src/components/form/components/ImageUpload.tsx @@ -20,7 +20,7 @@ import withObservables from '@nozbe/with-observables'; import OfflineImageDAO from '../../../wmDB/dao/OfflineImageDAO'; import { CLICKSTREAM_EVENT_NAMES, PrefixJpegBase64Image } from '../../../common/Constants'; import { addClickstreamEvent } from '../../../services/clickstreamEventService'; -import { isQuestionMandatory } from "../services/validation.service"; +import { isQuestionMandatory, validateInput } from "../services/validation.service"; import { CaseAllocationType } from "../../../screens/allCases/interface"; interface IOfflineImage { @@ -90,7 +90,7 @@ const ImageUpload: React.FC = props => { {!imageId ? ( validateInput(data, question.metadata.validators)}} render={({field: {onChange}}) => ( handleChange(clickedImage, onChange)} diff --git a/src/components/form/components/Rating.tsx b/src/components/form/components/Rating.tsx index 5b64d4db..56460da5 100644 --- a/src/components/form/components/Rating.tsx +++ b/src/components/form/components/Rating.tsx @@ -55,7 +55,7 @@ const Rating: React.FC = props => { validateInput(data, question.metadata.validators)}} + rules={{validate: (data) => validateInput(data, question.metadata.validators)}} render={({field: {onChange, value}}) => ( = props => { rules={{validate: (data) => validateInput(data, question.metadata.validators)}} render={({field: {onChange, value}}) => ( handleChange(text, onChange)} value={value?.answer} containerStyle={[GenericStyles.mt12]} diff --git a/src/components/form/index.tsx b/src/components/form/index.tsx index 5e503e9d..c57c5aee 100644 --- a/src/components/form/index.tsx +++ b/src/components/form/index.tsx @@ -29,8 +29,6 @@ import { getNextJourneyActions, getNextWidget } from "./services/forms.service"; import { FormTemplateV1 } from "../../types/template.types"; import { CaseAllocationType } from "../../screens/allCases/interface"; -const CASE_DETAIL_SCREEN_NAME = 'caseDetail'; - interface IWidget { route: { name: string; @@ -42,12 +40,8 @@ interface IWidget { } const Widget: React.FC = props => { - const navigationRoutes = useNavigationState(state => state.routes); - const previousScreenRoute = navigationRoutes[navigationRoutes.length - 2]; - const [isJourneyFirstScreen] = useState( - previousScreenRoute.name === CASE_DETAIL_SCREEN_NAME, - ); + const [isJourneyFirstScreen, setIsJourneyFirstScreen] = useState(true); const { params } = props.route; @@ -62,19 +56,36 @@ const Widget: React.FC = props => { const sectionMap = templateData.sections; const [error, setError] = useState(); const dispatch = useDispatch(); - + useEffect(()=>{ + let isFirst = false + for(const journey of Object.values(templateData.journey)){ + if(journey.startWidget === name){ + isFirst = true; + break; + } + } + setIsJourneyFirstScreen(isFirst) + },[templateData, name]) const dataToBeValidated = useAppSelector( state => state.case.caseForm?.[caseId]?.[journey], ); - const { control, handleSubmit } = useForm({ + const { control, handleSubmit, trigger, watch, clearErrors, formState: {errors} } = useForm({ defaultValues: { ...dataToBeValidated, - allocationReferenceId: caseData?.currentAllocationReferenceId || 'asdfasd', + allocationReferenceId: caseData?.currentAllocationReferenceId || Date.now(), }, }); + React.useEffect(() => { + const subscription = watch((value, { name }) => { + clearErrors(name); + trigger(name) + }); + return () => subscription.unsubscribe(); + }, [watch]); + const onSubmit = (data: any) => { addClickstreamEvent( CLICKSTREAM_EVENT_NAMES.AV_FORM_NEXT_BUTTON_CLICKED, @@ -88,7 +99,7 @@ const Widget: React.FC = props => { answer: data, }), ); - navigateToScreen(getTemplateRoute(`${getNextWidget(widgetConditionActions, data, name)}`,caseType), { + navigateToScreen(getTemplateRoute(`${getNextWidget(widgetConditionActions, data)}`,caseType), { journey: journey, caseId, }); @@ -260,24 +271,11 @@ const Widget: React.FC = props => { <> {sections?.map((section: any, index: number) => { return ( - <> - {sectionMap[section]?.label && ( - 0 - ? GenericStyles.mt16 - : null - } - key={index} - type={'h3'} - dark> - {sectionMap[section]?.label} - - )} = props => { name={name} /> - ); })} diff --git a/src/components/form/services/conditionEvaluation.service.ts b/src/components/form/services/conditionEvaluation.service.ts index 96ec994e..9a3767b9 100644 --- a/src/components/form/services/conditionEvaluation.service.ts +++ b/src/components/form/services/conditionEvaluation.service.ts @@ -10,16 +10,16 @@ import { // TODO : technically widgetId should also be inside context, will do it when time allows. export function evaluateCondition(condition: Condition, context: any): boolean { if (condition.conditionType === ConditionTypes.LEAF) { - return evaluateLeaf(condition as unknown as LeafCondition, context , widgetId) - } else return evaluateComposite(condition as unknown as CompositeCondition, context, widgetId) + return evaluateLeaf(condition as unknown as LeafCondition, context ) + } else return evaluateComposite(condition as unknown as CompositeCondition, context) } -function evaluateLeaf (leaf: LeafCondition, context: any, widgetId: string):boolean { +function evaluateLeaf (leaf: LeafCondition, context: any):boolean { switch (leaf.operator) { case LeafOperator.VALUE_EQUAL_TO: return ( leaf.right === - context?.widgetContext?.[widgetId]?.sectionContext?.[leaf.section] + context?.widgetContext?.[leaf.widgetId]?.sectionContext?.[leaf.section] ?.questionContext?.[leaf.left]?.answer ); default: diff --git a/src/components/form/services/forms.service.ts b/src/components/form/services/forms.service.ts index 10fb1776..5f3efcd3 100644 --- a/src/components/form/services/forms.service.ts +++ b/src/components/form/services/forms.service.ts @@ -1,7 +1,7 @@ import { evaluateCondition } from "./conditionEvaluation.service"; import { Action, ActionType, ConditionAction } from "../../../types/template.types"; -export const getNextWidget = (conditionActions: ConditionAction[], context: any, currentWidgetId: string): string => { +export const getNextWidget = (conditionActions: ConditionAction[], context: any): string => { let nextScreenName = ''; for (const conditionAction of conditionActions) { if (conditionAction.isDefault || (conditionAction.condition && evaluateCondition(conditionAction.condition, context))) { diff --git a/src/components/form/services/validation.service.ts b/src/components/form/services/validation.service.ts index 7abf6342..503624da 100644 --- a/src/components/form/services/validation.service.ts +++ b/src/components/form/services/validation.service.ts @@ -10,7 +10,7 @@ export function validateInput(data: { answer: any, type: string }, allRules: any for (const ruleName of Object.keys(allRules)) { const rule = allRules[ruleName]; if (ruleName === 'required' && rule.value) { - if (!data?.answer?.length) { + if (data?.answer=== null || data?.answer===undefined || !`${data?.answer}`.trim().length) { result = rule.message || 'This is required field'; break; }