feat: added changes for qa release on feedback flow (#114)
This commit is contained in:
committed by
GitHub Enterprise
parent
81457d12ec
commit
4f636e60bb
3
App.tsx
3
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 (
|
||||
|
||||
@@ -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!' })
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -14,14 +14,14 @@ const ErrorMessage: React.FC<IError> = props => {
|
||||
}
|
||||
return (
|
||||
<View style={GenericStyles.mt12}>
|
||||
<Text style={GenericStyles.alignCenter}>
|
||||
<InfoIcon color={'red'} />{' '}
|
||||
<Text style={[GenericStyles.ml10, GenericStyles.redText]}>
|
||||
<View style={[GenericStyles.flexRow, GenericStyles.alignCenter, GenericStyles.justifyStart]}>
|
||||
<InfoIcon color={'red'}/>
|
||||
<Text style={[GenericStyles.ml8, GenericStyles.redText]}>
|
||||
{show?.message || 'This is mandatory field'}
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorMessage;
|
||||
|
||||
@@ -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<IImageUpload> = props => {
|
||||
{!imageId ? (
|
||||
<Controller
|
||||
control={props.control}
|
||||
// rules={question.metadata.validators}
|
||||
rules={{validate: (data) => validateInput(data, question.metadata.validators)}}
|
||||
render={({field: {onChange}}) => (
|
||||
<CameraClickPicture
|
||||
onPictureClickSuccess={clickedImage => handleChange(clickedImage, onChange)}
|
||||
|
||||
@@ -55,7 +55,7 @@ const Rating: React.FC<IRating> = props => {
|
||||
</Text>
|
||||
<Controller
|
||||
control={props.control}
|
||||
// rules={{validate: (data) => validateInput(data, question.metadata.validators)}}
|
||||
rules={{validate: (data) => validateInput(data, question.metadata.validators)}}
|
||||
render={({field: {onChange, value}}) => (
|
||||
<StarRating
|
||||
minText={"Unhappy"}
|
||||
|
||||
@@ -57,6 +57,7 @@ const TextInput: React.FC<ITextInput> = props => {
|
||||
rules={{validate: (data) => validateInput(data, question.metadata.validators)}}
|
||||
render={({field: {onChange, value}}) => (
|
||||
<RNTextInput
|
||||
keyboardType={question.metadata.keyboardType || "default"}
|
||||
onChangeText={(text) => handleChange(text, onChange)}
|
||||
value={value?.answer}
|
||||
containerStyle={[GenericStyles.mt12]}
|
||||
|
||||
@@ -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<IWidget> = 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<IWidget> = 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<IWidget> = 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<IWidget> = props => {
|
||||
<>
|
||||
{sections?.map((section: any, index: number) => {
|
||||
return (
|
||||
<>
|
||||
{sectionMap[section]?.label && (
|
||||
<Heading
|
||||
style={
|
||||
index > 0
|
||||
? GenericStyles.mt16
|
||||
: null
|
||||
}
|
||||
key={index}
|
||||
type={'h3'}
|
||||
dark>
|
||||
{sectionMap[section]?.label}
|
||||
</Heading>
|
||||
)}
|
||||
<View
|
||||
key={section + index}
|
||||
style={[
|
||||
GenericStyles.p16,
|
||||
GenericStyles.pt12,
|
||||
GenericStyles.whiteBackground,
|
||||
getShadowStyle(5),
|
||||
styles.br8,
|
||||
@@ -296,7 +294,6 @@ const Widget: React.FC<IWidget> = props => {
|
||||
name={name}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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))) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user