Merge branch 'rendering_engine' of github.cmd.navi-tech.in:medici/Address-Verification-App into TP-12803

# Conflicts:
#	src/data/templateData.json
This commit is contained in:
kunalsharma
2022-12-05 13:51:49 +05:30
9 changed files with 320 additions and 155 deletions

View File

@@ -21,6 +21,7 @@ import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack'; import {createNativeStackNavigator} from '@react-navigation/native-stack';
import Widget from './src/components/form'; import Widget from './src/components/form';
import {navigationRef} from './src/components/utlis/navigationUtlis'; import {navigationRef} from './src/components/utlis/navigationUtlis';
import {GenericStyles} from './RN-UI-LIB/src/styles';
function HomeScreen() { function HomeScreen() {
return ( return (

View File

@@ -1,14 +1,14 @@
import { StyleSheet, Text, View } from 'react-native' import {StyleSheet, Text, View} from 'react-native';
import React from 'react' import React from 'react';
const Checkbox = () => { const Checkbox = () => {
return ( return (
<View> <View>
<Text>Checkbox</Text> <Text>Checkbox</Text>
</View> </View>
) );
} };
export default Checkbox export default Checkbox;
const styles = StyleSheet.create({}) const styles = StyleSheet.create({});

View File

@@ -1,10 +1,88 @@
import {StyleSheet, Text, View} from 'react-native'; import {Image, StyleSheet, Text, View} from 'react-native';
import React from 'react'; import React, { useEffect } from 'react';
import {Control, Controller} from 'react-hook-form';
import CameraClickPicture from '../../../../RN-UI-LIB/src/components/camera_click_picture/CameraClickPicture';
const ImageUpload = () => { import template from '../../../data/templateData.json';
import {GenericStyles} from '../../../../RN-UI-LIB/src/styles';
import {useState} from 'react';
import {useSelector} from 'react-redux';
import {RootState} from '../../../store/store';
interface IImageUpload {
questionType: string;
questionId: string;
widgetId: string;
journeyId: string;
caseId: string;
sectionId: string;
control: Control<any, any>;
error: any;
}
const ImageUpload: React.FC<IImageUpload> = props => {
const {questionId, error, sectionId, caseId, journeyId, widgetId} = props;
const [image, setImage] = useState('');
const question = template.questions[questionId];
const dataFromRedux = useSelector(
(state: RootState) =>
state.case.caseForm?.[caseId]?.[journeyId]?.[widgetId]?.[
sectionId
]?.[questionId],
);
useEffect(() => {
if (dataFromRedux) {
setImage(dataFromRedux);
}
}, [dataFromRedux]);
if (!question) {
return null;
}
return ( return (
<View> <View style={[GenericStyles.mt12]}>
<Text>ImageUpload</Text> <Text>
{question.text}{' '}
{question.type === 'mandatory' && (
<Text style={{color: 'red'}}>*</Text>
)}
</Text>
{!image ? (
<Controller
control={props.control}
rules={{
required: question.type === 'mandatory',
}}
render={({field: {onChange}}) => (
<CameraClickPicture
onPictureClickSuccess={image => {
const data = `data:image/jpeg;base64,${image}`;
setImage(data);
onChange(data);
}}
/>
)}
name={`${props.sectionId}.${questionId}`}
/>
) : (
<View>
<Text onPress={() => setImage('')}>delete</Text>
<Image
style={{
width: 300,
height: 300,
resizeMode: 'contain',
borderWidth: 1,
borderColor: 'red',
}}
source={{uri: image}}
/>
</View>
)}
{error?.[sectionId]?.[questionId] ? (
<Text style={{color: 'red'}}>This is mendatory field</Text>
) : null}
</View> </View>
); );
}; };

View File

@@ -1,19 +1,17 @@
import React, {useEffect} from 'react';
import {StyleSheet, View} from 'react-native'; import {StyleSheet, View} from 'react-native';
import React from 'react';
import template from '../../../data/templateData.json'; import {useState} from 'react';
import {GenericStyles} from '../../../../RN-UI-LIB/src/styles'; import {Control, Controller} from 'react-hook-form';
import RadioGroup from '../../../../RN-UI-LIB/src/components/radio_button/RadioGroup'; import {useSelector} from 'react-redux';
import RNRadioButton from '../../../../RN-UI-LIB/src/components/radio_button/RadioButton'; import RNRadioButton from '../../../../RN-UI-LIB/src/components/radio_button/RadioButton';
import RadioChip from '../../../../RN-UI-LIB/src/components/radio_button/RadioChip'; import RadioChip from '../../../../RN-UI-LIB/src/components/radio_button/RadioChip';
import {useDispatch, useSelector} from 'react-redux'; import RadioGroup from '../../../../RN-UI-LIB/src/components/radio_button/RadioGroup';
import {decreaseByOne} from '../../../reducer/formData';
import {updateInteraction} from '../../../reducer/caseReducre';
import {RootState} from '../../../store/store';
import {Control, Controller} from 'react-hook-form';
import {useState} from 'react';
import QuestionRenderingEngine from '../QuestionRenderingEngine';
import Text from '../../../../RN-UI-LIB/src/components/Text'; import Text from '../../../../RN-UI-LIB/src/components/Text';
import {GenericStyles} from '../../../../RN-UI-LIB/src/styles';
import template from '../../../data/templateData.json';
import {RootState} from '../../../store/store';
import QuestionRenderingEngine from '../QuestionRenderingEngine';
interface IRadioButton { interface IRadioButton {
questionType: string; questionType: string;
@@ -31,6 +29,21 @@ const RadioButton: React.FC<IRadioButton> = props => {
const question = template.questions[questionId]; const question = template.questions[questionId];
const options = template.options; const options = template.options;
const [associatedQuestions, setAssociatedQuestions] = useState([]); const [associatedQuestions, setAssociatedQuestions] = useState([]);
const dataFromRedux = useSelector(
(state: RootState) =>
state.case.caseForm?.[caseId]?.[journeyId]?.[widgetId]?.[
sectionId
]?.[questionId],
);
useEffect(() => {
if (dataFromRedux) {
computeNextQuestion(dataFromRedux);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataFromRedux]);
// const dispatch = useDispatch(); // const dispatch = useDispatch();
// const answer = useSelector( // const answer = useSelector(
// (state: RootState) => // (state: RootState) =>

View File

@@ -1,41 +1,35 @@
import {ScrollView, StyleSheet, Text, View} from 'react-native'; import React, {useState} from 'react';
import React, {Component, useCallback} from 'react';
import {navigateToScreen} from '../utlis/navigationUtlis';
import template from '../../data/templateData.json';
import Heading from '../../../RN-UI-LIB/src/components/Heading';
import RenderQuestion from './RenderQuestion';
import {GenericStyles} from '../../../RN-UI-LIB/src/styles';
import {useSelector} from 'react-redux';
import {RootState} from '../../store/store';
import Button from '../../../RN-UI-LIB/src/components/Button';
import {Operator} from '../formRenderingEngine/types';
import {useForm} from 'react-hook-form'; import {useForm} from 'react-hook-form';
import {useState} from 'react'; import {ScrollView, StyleSheet, View} from 'react-native';
import {useEffect} from 'react'; import {SafeAreaView} from 'react-native-safe-area-context';
import {useDispatch, useSelector} from 'react-redux';
import Button from '../../../RN-UI-LIB/src/components/Button';
import Heading from '../../../RN-UI-LIB/src/components/Heading';
import {GenericStyles, getShadowStyle} from '../../../RN-UI-LIB/src/styles';
import {COLORS} from '../../../RN-UI-LIB/src/styles/colors';
import template from '../../data/templateData.json';
import {updateInteraction} from '../../reducer/caseReducre';
import {RootState} from '../../store/store';
import {goBack, navigateToScreen} from '../utlis/navigationUtlis';
import RenderQuestion from './RenderQuestion';
const Widget = (props: any) => { const Widget = (props: any) => {
const {name, params} = props.route; const {name, params} = props.route;
const {caseId, journey} = params; const {caseId, journey} = params;
const {sections, transitionRules} = template.widgets[name]; const {sections, transitionRules, isLeaf} = template.widgets[name];
const sectionMap = template.sections; const sectionMap = template.sections;
const {actions, conditions} = transitionRules; const {actions, conditions} = transitionRules;
const [error, setError] = useState(); const [error, setError] = useState();
const dispatch = useDispatch();
const {
control,
handleSubmit,
formState: {errors},
} = useForm({});
const dataToBeValidated = useSelector( const dataToBeValidated = useSelector(
(state: RootState) => (state: RootState) =>
state.counter.caseForm?.[caseId]?.[journey]?.[name], state.case.caseForm?.[caseId]?.[journey]?.[name],
); );
console.log('defaultValues', dataToBeValidated); const {control, handleSubmit} = useForm({
defaultValues: dataToBeValidated,
// useEffect(() => { });
// setValue('defaultValues', dataToBeValidated);
// }, [dataToBeValidated, setValue]);
const evaluateLeaf = (leaf: { const evaluateLeaf = (leaf: {
left: string; left: string;
@@ -75,62 +69,98 @@ const Widget = (props: any) => {
return composite.operator === 'AND' ? left && right : left || right; return composite.operator === 'AND' ? left && right : left || right;
}; };
const handleValidation = (transitionRules: any) => { const handleValidation = (rules: any) => {
if (transitionRules.condition_type === 'LEAF_NODE') { let nextScreenName = '';
// console.log(evaluateLeaf(transitionRules)); if (rules.condition_type === 'LEAF_NODE') {
const nextScreenName = actions[evaluateLeaf(transitionRules)]; nextScreenName = actions[evaluateLeaf(rules)];
} else { } else {
// console.log(evaluateComposite(transitionRules)); const answer = String(evaluateComposite(rules));
const answer = String(evaluateComposite(transitionRules)); nextScreenName = actions[answer];
const nextScreenName = actions[answer];
navigateToScreen(nextScreenName, {
journey: journey,
caseId,
});
console.log({nextScreenName, actions});
} }
navigateToScreen(nextScreenName, {
journey: journey,
caseId,
});
}; };
const onSubmit = data => { const onSubmit = (data: any) => {
console.log(data, 'data'); dispatch(
updateInteraction({
caseId,
journeyId: journey,
widgetId: name,
answer: data,
}),
);
handleValidation(conditions);
}; };
const onError = data => { const onError = (data: any) => {
console.log(data); console.log(data);
setError(data); setError(data);
}; };
return ( return (
<ScrollView> <SafeAreaView
{sections.map((section: string, index: number) => ( style={[GenericStyles.whiteBackground, GenericStyles.fill]}>
<View <ScrollView
style={[ contentContainerStyle={[
GenericStyles.p16, GenericStyles.p16,
GenericStyles.whiteBackground, GenericStyles.whiteBackground,
index > 0 ? GenericStyles.mt16 : null, ]}>
]}> {sections.map(
<Heading key={index} type={'h3'} dark> (section: keyof typeof sectionMap, index: number) => (
{sectionMap[section]?.label} <View
</Heading> key={section + index}
<RenderQuestion style={[
questionMap={sectionMap[section].questions} GenericStyles.p16,
journeyId={journey} GenericStyles.whiteBackground,
caseId={caseId} index > 0 ? GenericStyles.mt16 : null,
sectionId={section} getShadowStyle(5),
widgetId={name} styles.br8,
control={control} ]}>
error={error} <Heading key={index} type={'h3'} dark>
/> {sectionMap[section]?.label}
</View> </Heading>
))} <RenderQuestion
<Button questionMap={sectionMap[section].questions}
title="handle validation" journeyId={journey}
onPress={handleSubmit(onSubmit, onError)} caseId={caseId}
/> sectionId={section}
</ScrollView> widgetId={name}
control={control}
error={error}
name={name}
/>
</View>
),
)}
</ScrollView>
<View
style={[
GenericStyles.row,
GenericStyles.justifyContentSpaceBetween,
GenericStyles.p16,
styles.borderTop,
]}>
<Button title="Go Back" onPress={goBack} />
<Button
title={isLeaf ? 'Submit' : 'next'}
onPress={handleSubmit(onSubmit, onError)}
/>
</View>
</SafeAreaView>
); );
}; };
const styles = StyleSheet.create({}); const styles = StyleSheet.create({
br8: {
borderRadius: 8,
},
borderTop: {
borderTopColor: COLORS.BORDER.PRIMARY,
borderTopWidth: 1,
},
});
export default Widget; export default Widget;

View File

@@ -1,11 +1,12 @@
// @ts-nocheck // @ts-nocheck
import React, {useMemo} from 'react';
import {Button, Pressable, Text, View} from 'react-native'; import {Button, Pressable, Text, View} from 'react-native';
import React, {useCallback, useMemo} from 'react';
import {Adderss, EachJourney, Template} from './types';
import {useDispatch, useSelector} from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
import {RootState} from '../../store/store'; import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
import {increaseByOne} from '../../reducer/caseReducre'; import {increaseByOne} from '../../reducer/caseReducre';
import {RootState} from '../../store/store';
import {navigateToScreen} from '../utlis/navigationUtlis'; import {navigateToScreen} from '../utlis/navigationUtlis';
import {EachJourney, Template} from './types';
interface RenderingEngine { interface RenderingEngine {
data: Template; data: Template;
@@ -32,19 +33,22 @@ interface RenderingEngine {
const RenderingEngine: React.FC<RenderingEngine> = props => { const RenderingEngine: React.FC<RenderingEngine> = props => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const journeyOrder = props?.userData?.journeyOrder; const journeyOrder = props?.userData?.journeyOrder;
const data = useSelector((state: RootState) => state.counter.value); const data = useSelector((state: RootState) => state.case.value);
const handleNextContext = (journey: EachJourney) => { const handleNextContext = useMemo(
navigateToScreen(journey.startWidget, { () => (journey: EachJourney) => {
journey: journey.id, navigateToScreen(journey.startWidget, {
caseId: props.userData.id, journey: journey.id,
}); caseId: props.userData.id,
}; });
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
);
const JourneyRendering = useMemo( const JourneyRendering = useMemo(
() => () =>
journeyOrder.map((eachJourney, index) => { journeyOrder.map((eachJourney, index) => {
const journey = const journey = props.data.journeys[eachJourney];
props.data.journeys[eachJourney]
return ( return (
<Pressable <Pressable
@@ -54,7 +58,7 @@ const RenderingEngine: React.FC<RenderingEngine> = props => {
</Pressable> </Pressable>
); );
}), }),
[journeyOrder, handleNextContext, props.userData], [journeyOrder, handleNextContext, props],
); );
const handlePress = () => { const handlePress = () => {
@@ -62,7 +66,7 @@ const RenderingEngine: React.FC<RenderingEngine> = props => {
}; };
return ( return (
<View> <View style={GenericStyles.whiteBackground}>
<Text>Rendering Engine {data}</Text> <Text>Rendering Engine {data}</Text>
{JourneyRendering} {JourneyRendering}
<Button title="add todo" onPress={handlePress} /> <Button title="add todo" onPress={handlePress} />

View File

@@ -17,7 +17,7 @@
"false": "unverified" "false": "unverified"
} }
}, },
"is_leaf": false "isLeaf": false
}, },
"j2": { "j2": {
"id": "j2", "id": "j2",
@@ -36,7 +36,7 @@
"false": "unverified" "false": "unverified"
} }
}, },
"is_leaf": false "isLeaf": false
}, },
"j3": { "j3": {
"id": "j3", "id": "j3",
@@ -55,7 +55,7 @@
"false": "unverified" "false": "unverified"
} }
}, },
"is_leaf": false "isLeaf": false
}, },
"j4": { "j4": {
"id": "j4", "id": "j4",
@@ -74,7 +74,7 @@
"false": "unverified" "false": "unverified"
} }
}, },
"is_leaf": false "isLeaf": false
}, },
"j5": { "j5": {
"id": "j5", "id": "j5",
@@ -93,7 +93,7 @@
"false": "unverified" "false": "unverified"
} }
}, },
"is_leaf": false "isLeaf": false
} }
}, },
"widgets": { "widgets": {
@@ -138,7 +138,7 @@
"false": "w3" "false": "w3"
} }
}, },
"is_leaf": false "isLeaf": false
}, },
"w2": { "w2": {
"journey_id": "j1", "journey_id": "j1",
@@ -146,9 +146,20 @@
"s2" "s2"
], ],
"transitionRules": { "transitionRules": {
"conditions": {
"operator": "MATCHES",
"condition_type": "LEAF_NODE",
"left":"q3",
"right": "o3",
"section":"s2"
},
"actions": {
"true": "w4",
"false": "w3"
}
}, },
"is_leaf": true "isLeaf": false
}, },
"w3": { "w3": {
"journey_id": "j3", "journey_id": "j3",
@@ -172,7 +183,27 @@
} }
} }
}, },
"is_leaf": true "w4": {
"journey_id": "j1",
"sections": [
"s2"
],
"transitionRules": {
"conditions": {
"operator": "MATCHES",
"condition_type": "LEAF_NODE",
"left":"q3",
"right": "o3",
"section":"s2"
},
"actions": {
"true": "w2",
"false": "w3"
}
},
"isLeaf": true
}
}, },
"sections": { "sections": {
"s1": { "s1": {
@@ -180,7 +211,8 @@
"questions": [ "questions": [
"q1", "q1",
"q8", "q8",
"q9" "q9",
"q10"
] ]
}, },
"s2": { "s2": {
@@ -264,7 +296,7 @@
"type": "mandatory", "type": "mandatory",
"input_type": "TextInput", "input_type": "TextInput",
"options": [ "options": [
], ],
"metadata": { "metadata": {
"formatting_type": "textbox" "formatting_type": "textbox"
@@ -323,77 +355,87 @@
"metadata": { "metadata": {
"formatting_type": "Rating" "formatting_type": "Rating"
} }
},
"q10": {
"text": "New Address Verification question: some question?",
"type": "na",
"input_type": "ImageUpload",
"options": [],
"metadata": {
"formatting_type": "Rating"
}
} }
}, },
"options": { "options": {
"o1": { "o1": {
"type": "single_select", "type": "single_select",
"text": "Found", "text": "Found",
"associateQuestions": [ "associateQuestions": [
], ],
"metadata": { "metadata": {
} }
}, },
"o2": { "o2": {
"type": "single_select", "type": "single_select",
"text": "Not Found", "text": "Not Found",
"associateQuestions": [ "associateQuestions": [
], ],
"metadata": { "metadata": {
} }
}, },
"o3": { "o3": {
"type": "single_select", "type": "single_select",
"text": "Yes", "text": "Yes",
"associateQuestions": [ "associateQuestions": [
], ],
"metadata": { "metadata": {
} }
}, },
"o4": { "o4": {
"type": "single_select", "type": "single_select",
"text": "No", "text": "No",
"associateQuestions": [ "associateQuestions": [
], ],
"metadata": { "metadata": {
} }
}, },
"o5": { "o5": {
"type": "multi_select", "type": "multi_select",
"text": "Medical issue", "text": "Medical issue",
"associateQuestions": [ "associateQuestions": [
], ],
"metadata": { "metadata": {
} }
}, },
"o6": { "o6": {
"type": "multi_select", "type": "multi_select",
"text": "Covid", "text": "Covid",
"associateQuestions": [ "associateQuestions": [
], ],
"metadata": { "metadata": {
} }
}, },
"o7": { "o7": {
"type": "multi_select", "type": "multi_select",
"text": "Business purpose", "text": "Business purpose",
"associateQuestions": [ "associateQuestions": [
], ],
"metadata": { "metadata": {
} }
}, },
"o8": { "o8": {
@@ -403,48 +445,48 @@
"q4" "q4"
], ],
"metadata": { "metadata": {
} }
}, },
"o9": { "o9": {
"type": "multi_select", "type": "multi_select",
"text": "Flat", "text": "Flat",
"associateQuestions": [ "associateQuestions": [
], ],
"metadata": { "metadata": {
} }
}, },
"o10": { "o10": {
"type": "multi_select", "type": "multi_select",
"text": "city", "text": "city",
"associateQuestions": [ "associateQuestions": [
], ],
"metadata": { "metadata": {
} }
}, },
"o11": { "o11": {
"type": "single_select", "type": "single_select",
"text": "PTP", "text": "PTP",
"associateQuestions": [ "associateQuestions": [
], ],
"metadata": { "metadata": {
} }
}, },
"o22": { "o22": {
"type": "single_select", "type": "single_select",
"text": "RNR", "text": "RNR",
"associateQuestions": [ "associateQuestions": [
], ],
"metadata": { "metadata": {
} }
} }
}, }
} }

View File

@@ -1,7 +1,8 @@
// @ts-nocheck
import {createSlice} from '@reduxjs/toolkit'; import {createSlice} from '@reduxjs/toolkit';
export const counterSlice = createSlice({ export const counterSlice = createSlice({
name: 'counter', name: 'case',
initialState: { initialState: {
value: 0, value: 0,
caseForm: {}, caseForm: {},
@@ -14,8 +15,7 @@ export const counterSlice = createSlice({
state.value--; state.value--;
}, },
updateInteraction: (state, action) => { updateInteraction: (state, action) => {
const {caseId, journeyId, widgetId, questionId, answer, sectionId} = const {caseId, journeyId, widgetId, answer} = action.payload;
action.payload;
const data = state.caseForm || {}; const data = state.caseForm || {};
if (!data[caseId]) { if (!data[caseId]) {
data[caseId] = {}; data[caseId] = {};
@@ -26,10 +26,7 @@ export const counterSlice = createSlice({
if (!data[caseId][journeyId][widgetId]) { if (!data[caseId][journeyId][widgetId]) {
data[caseId][journeyId][widgetId] = {}; data[caseId][journeyId][widgetId] = {};
} }
if (!data[caseId][journeyId][widgetId][sectionId]) { data[caseId][journeyId][widgetId] = answer;
data[caseId][journeyId][widgetId][sectionId] = {};
}
data[caseId][journeyId][widgetId][sectionId][questionId] = answer;
state.caseForm = data; state.caseForm = data;
}, },
}, },

View File

@@ -12,17 +12,17 @@ import {
} from 'redux-persist'; } from 'redux-persist';
import AsyncStorage from '@react-native-async-storage/async-storage'; import AsyncStorage from '@react-native-async-storage/async-storage';
import counterReducer from '../reducer/caseReducre'; import caseReducer from '../reducer/caseReducre';
const rootReducer = combineReducers({ const rootReducer = combineReducers({
counter: counterReducer, case: caseReducer,
}); });
const persistConfig = { const persistConfig = {
key: 'root', key: 'root',
version: 1, version: 1,
storage: AsyncStorage, storage: AsyncStorage,
whitelist: ['counter'], whitelist: ['case'],
}; };
const persistedReducer = persistReducer(persistConfig, rootReducer); const persistedReducer = persistReducer(persistConfig, rootReducer);