From 04408aeb3fe4eaf32e911b45245df74726bbef55 Mon Sep 17 00:00:00 2001 From: "aman.singh" Date: Sat, 3 Dec 2022 16:43:36 +0530 Subject: [PATCH] initial commit --- .gitmodules | 3 + App.tsx | 37 ++- RN-UI-LIB | 1 + metro.config.js | 2 +- package.json | 5 + .../form/QuestionRenderingEngine.tsx | 46 +++ src/components/form/RenderQuestion.tsx | 47 +++ src/components/form/components/Checkbox.tsx | 14 + .../form/components/ImageUpload.tsx | 14 + .../form/components/RadioButton.tsx | 135 +++++++++ src/components/form/components/Rating.tsx | 14 + src/components/form/components/TextArea.tsx | 14 + src/components/form/components/TextInput.tsx | 59 ++++ src/components/form/index.tsx | 136 +++++++++ src/components/formRenderingEngine/index.tsx | 55 +++- src/components/formRenderingEngine/types.ts | 51 +++- src/components/utlis/navigationUtlis.ts | 31 ++ src/data/{data.json => templateData.json} | 273 ++++++++---------- src/data/userData.json | 61 ++++ src/reducer/caseReducre.ts | 23 +- src/reducer/formData.ts | 17 ++ src/reducer/index.ts | 2 + tsconfig.json | 7 +- yarn.lock | 107 ++++++- 24 files changed, 982 insertions(+), 172 deletions(-) create mode 100644 .gitmodules create mode 160000 RN-UI-LIB create mode 100644 src/components/form/QuestionRenderingEngine.tsx create mode 100644 src/components/form/RenderQuestion.tsx create mode 100644 src/components/form/components/Checkbox.tsx create mode 100644 src/components/form/components/ImageUpload.tsx create mode 100644 src/components/form/components/RadioButton.tsx create mode 100644 src/components/form/components/Rating.tsx create mode 100644 src/components/form/components/TextArea.tsx create mode 100644 src/components/form/components/TextInput.tsx create mode 100644 src/components/form/index.tsx create mode 100644 src/components/utlis/navigationUtlis.ts rename src/data/{data.json => templateData.json} (61%) create mode 100644 src/data/userData.json create mode 100644 src/reducer/formData.ts diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..1d027a4e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "RN-UI-LIB"] + path = RN-UI-LIB + url = git@github.cmd.navi-tech.in:medici/RN-UI-LIB.git diff --git a/App.tsx b/App.tsx index 6ad02009..23ce4f9e 100644 --- a/App.tsx +++ b/App.tsx @@ -9,13 +9,31 @@ */ import React from 'react'; -import {SafeAreaView, Text} from 'react-native'; -import data from './src/data/data.json'; +import {SafeAreaView, Text, View} from 'react-native'; +import data from './src/data/templateData.json'; +import userData from './src/data/userData.json'; import RenderingEngine from './src/components/formRenderingEngine'; import {Provider} from 'react-redux'; import store, {persistor} from './src/store/store'; import {PersistGate} from 'redux-persist/integration/react'; +import {NavigationContainer} from '@react-navigation/native'; +import {createNativeStackNavigator} from '@react-navigation/native-stack'; +import Widget from './src/components/form'; +import {navigationRef} from './src/components/utlis/navigationUtlis'; + +function HomeScreen() { + return ( + + + + + + ); +} + +const Stack = createNativeStackNavigator(); + const App = () => { // const [hasBeenManuplated, setHasBeenManuplated] = useState(false); return ( @@ -23,9 +41,18 @@ const App = () => { Loading...} persistor={persistor}> - - - + + + + {Object.keys(data.widgets).map(key => ( + + ))} + + ); diff --git a/RN-UI-LIB b/RN-UI-LIB new file mode 160000 index 00000000..f77ac9a6 --- /dev/null +++ b/RN-UI-LIB @@ -0,0 +1 @@ +Subproject commit f77ac9a69232c5d79b68db61bdc7740285b280ac diff --git a/metro.config.js b/metro.config.js index 715c7bca..783f3499 100644 --- a/metro.config.js +++ b/metro.config.js @@ -10,7 +10,7 @@ module.exports = { getTransformOptions: async () => ({ transform: { experimentalImportSupport: false, - inlineRequires: true, + inlineRequires: false, }, }), }, diff --git a/package.json b/package.json index f20409f5..e372c72d 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,14 @@ "@react-native-async-storage/async-storage": "1.17.11", "@react-native-firebase/app": "^16.4.6", "@react-native-firebase/database": "^16.4.6", + "@react-navigation/native": "^6.0.16", + "@react-navigation/native-stack": "^6.9.4", "@reduxjs/toolkit": "1.9.1", "react": "18.1.0", + "react-hook-form": "7.40.0", "react-native": "0.70.6", + "react-native-safe-area-context": "^4.4.1", + "react-native-screens": "^3.18.2", "react-redux": "8.0.5", "redux": "4.2.0", "redux-persist": "6.0.0" diff --git a/src/components/form/QuestionRenderingEngine.tsx b/src/components/form/QuestionRenderingEngine.tsx new file mode 100644 index 00000000..7eaddec3 --- /dev/null +++ b/src/components/form/QuestionRenderingEngine.tsx @@ -0,0 +1,46 @@ +import {StyleSheet, Text, View} from 'react-native'; +import React from 'react'; +import TextInput from './components/TextInput'; +import RadioButton from './components/RadioButton'; +import TextArea from './components/TextArea'; +import ImageUpload from './components/ImageUpload'; +import Checkbox from './components/Checkbox'; +import Rating from './components/Rating'; +import { Control } from 'react-hook-form'; + +const Component = { + TextInput: TextInput, + TextArea: TextArea, + RadioButton: RadioButton, + ImageUpload: ImageUpload, + Checkbox: Checkbox, + Rating: Rating, +}; + +interface IQuestionRenderingEngine { + questionType: string; + questionId: string; + name: string; + journeyId: string; + caseId: string; + widgitId: string; + control: Control; + error: any; + sectionId: string; +} + +const QuestionRenderingEngine: React.FC = props => { + const Comp = Component[props.questionType]; + if (!Comp) { + return null; + } + return ( + + + + ); +}; + +export default QuestionRenderingEngine; + +const styles = StyleSheet.create({}); diff --git a/src/components/form/RenderQuestion.tsx b/src/components/form/RenderQuestion.tsx new file mode 100644 index 00000000..3e4da83f --- /dev/null +++ b/src/components/form/RenderQuestion.tsx @@ -0,0 +1,47 @@ +import {StyleSheet, Text, View} from 'react-native'; +import React from 'react'; +import template from '../../data/templateData.json'; +import QuestionRenderingEngine from './QuestionRenderingEngine'; +import { Control } from 'react-hook-form'; + +interface IRenderQuestion { + questionMap: Array; + journeyId: string; + name: string; + caseId: string; + sectionId: string; + widgetId: string; + control: Control; + error: any; +} + +const RenderQuestion: React.FC = props => { + const {name, questionMap, journeyId, caseId, sectionId} = props; + const questionHashMap = template.questions; + return ( + + {questionMap.map((question, index) => { + if (questionHashMap[question]) { + return ( + + ); + } + })} + + ); +}; + +export default RenderQuestion; + +const styles = StyleSheet.create({}); diff --git a/src/components/form/components/Checkbox.tsx b/src/components/form/components/Checkbox.tsx new file mode 100644 index 00000000..8ae5e9d1 --- /dev/null +++ b/src/components/form/components/Checkbox.tsx @@ -0,0 +1,14 @@ +import { StyleSheet, Text, View } from 'react-native' +import React from 'react' + +const Checkbox = () => { + return ( + + Checkbox + + ) +} + +export default Checkbox + +const styles = StyleSheet.create({}) \ No newline at end of file diff --git a/src/components/form/components/ImageUpload.tsx b/src/components/form/components/ImageUpload.tsx new file mode 100644 index 00000000..0c92a1fd --- /dev/null +++ b/src/components/form/components/ImageUpload.tsx @@ -0,0 +1,14 @@ +import { StyleSheet, Text, View } from 'react-native' +import React from 'react' + +const ImageUpload = () => { + return ( + + ImageUpload + + ) +} + +export default ImageUpload + +const styles = StyleSheet.create({}) \ No newline at end of file diff --git a/src/components/form/components/RadioButton.tsx b/src/components/form/components/RadioButton.tsx new file mode 100644 index 00000000..84577c59 --- /dev/null +++ b/src/components/form/components/RadioButton.tsx @@ -0,0 +1,135 @@ +import {StyleSheet, View} from 'react-native'; +import React from 'react'; + +import template from '../../../data/templateData.json'; +import {GenericStyles} from '../../../../RN-UI-LIB/src/styles'; +import RadioGroup from '../../../../RN-UI-LIB/src/components/radio_button/RadioGroup'; +import RNRadioButton from '../../../../RN-UI-LIB/src/components/radio_button/RadioButton'; +import RadioChip from '../../../../RN-UI-LIB/src/components/radio_button/RadioChip'; +import {useDispatch, useSelector} from 'react-redux'; +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'; + +interface IRadioButton { + questionType: string; + questionId: string; + widgetId: string; + journeyId: string; + caseId: string; + sectionId: string; + control: Control; + error: any; +} + +const RadioButton: React.FC = props => { + const {questionId, widgetId, journeyId, caseId, sectionId, error} = props; + const question = template.questions[questionId]; + const options = template.options; + const [associatedQuestions, setAssociatedQuestions] = useState([]); + // const dispatch = useDispatch(); + // const answer = useSelector( + // (state: RootState) => + // state.counter.caseForm?.[caseId]?.[journeyId]?.[widgetId]?.[ + // sectionId + // ]?.[questionId] || 'not_found', + // ); + + // const registerValue = (id: string) => { + // dispatch( + // updateInteraction({ + // caseId, + // journeyId, + // widgetId, + // questionId, + // answer: id, + // sectionId, + // }), + // ); + // }; + + const computeNextQuestion = (optionId: string) => { + if (options[optionId]?.associateQuestions.length < 1) { + setAssociatedQuestions([]); + return; + } + setAssociatedQuestions(options[optionId]?.associateQuestions); + }; + + return ( + + + {question.text}{' '} + {question.type === 'mandatory' && ( + * + )} + + ( + { + onChange(change); + computeNextQuestion(change); + // registerValue(change); + }} + variant={'button'} + orientation={ + question.metadata.orientation || 'verticle' + }> + {question?.metadata.buttonType === 'button' + ? question.options.map(option => ( + + )) + : question.options.map(option => ( + + ))} + + )} + name={`${sectionId}.${questionId}`} + /> + {error?.[sectionId]?.[questionId] ? ( + This is mendatory field + ) : null} + + {associatedQuestions.map((question, index) => { + if (template.questions[question]) { + return ( + + ); + } + })} + + ); +}; + +const styles = StyleSheet.create({}); + +export default RadioButton; diff --git a/src/components/form/components/Rating.tsx b/src/components/form/components/Rating.tsx new file mode 100644 index 00000000..9a8973c3 --- /dev/null +++ b/src/components/form/components/Rating.tsx @@ -0,0 +1,14 @@ +import { StyleSheet, Text, View } from 'react-native' +import React from 'react' + +const Rating = () => { + return ( + + Rating + + ) +} + +export default Rating + +const styles = StyleSheet.create({}) \ No newline at end of file diff --git a/src/components/form/components/TextArea.tsx b/src/components/form/components/TextArea.tsx new file mode 100644 index 00000000..95c4e0c3 --- /dev/null +++ b/src/components/form/components/TextArea.tsx @@ -0,0 +1,14 @@ +import { StyleSheet, Text, View } from 'react-native' +import React from 'react' + +const TextArea = () => { + return ( + + TextArea + + ) +} + +export default TextArea + +const styles = StyleSheet.create({}) \ No newline at end of file diff --git a/src/components/form/components/TextInput.tsx b/src/components/form/components/TextInput.tsx new file mode 100644 index 00000000..0d652f06 --- /dev/null +++ b/src/components/form/components/TextInput.tsx @@ -0,0 +1,59 @@ +import {StyleSheet, View} from 'react-native'; +import React from 'react'; +import RNTextInput from '../../../../RN-UI-LIB/src/components/TextInput'; +import {GenericStyles} from '../../../../RN-UI-LIB/src/styles'; +import {Control, Controller} from 'react-hook-form'; + +import template from '../../../data/templateData.json'; +import Text from '../../../../RN-UI-LIB/src/components/Text'; + +interface ITextInput { + questionType: string; + questionId: string; + widgetId: string; + journeyId: string; + caseId: string; + sectionId: string; + control: Control; + error: any; +} + +const TextInput: React.FC = props => { + const {questionId, error, sectionId} = props; + + const question = template.questions[questionId]; + if (!question) { + return null; + } + return ( + + + {question.text}{' '} + {question.type === 'mandatory' && ( + * + )} + + ( + + )} + name={`${props.sectionId}.${questionId}`} + /> + {error?.[sectionId]?.[questionId] ? ( + This is mendatory field + ) : null} + + ); +}; + +export default TextInput; + +const styles = StyleSheet.create({}); diff --git a/src/components/form/index.tsx b/src/components/form/index.tsx new file mode 100644 index 00000000..27958911 --- /dev/null +++ b/src/components/form/index.tsx @@ -0,0 +1,136 @@ +import {ScrollView, StyleSheet, Text, View} from 'react-native'; +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 {useState} from 'react'; +import {useEffect} from 'react'; +const Widget = (props: any) => { + const {name, params} = props.route; + const {caseId, journey} = params; + const {sections, transitionRules} = template.widgets[name]; + const sectionMap = template.sections; + const {actions, conditions} = transitionRules; + const [error, setError] = useState(); + + const { + control, + handleSubmit, + formState: {errors}, + } = useForm({}); + + const dataToBeValidated = useSelector( + (state: RootState) => + state.counter.caseForm?.[caseId]?.[journey]?.[name], + ); + + console.log('defaultValues', dataToBeValidated); + + // useEffect(() => { + // setValue('defaultValues', dataToBeValidated); + // }, [dataToBeValidated, setValue]); + + const evaluateLeaf = (leaf: { + left: string; + right: string; + operator: string; + section: string; + }): boolean => { + switch (leaf.operator) { + case 'MATCHES': + return ( + leaf.right === dataToBeValidated[leaf.section][leaf.left] + ); + default: + return false; + } + }; + + const evaluateComposite = (composite: { + left: any; + right: any; + operator: string; + section: string; + }): boolean => { + let left = false, + right = false; + if (composite.left.condition_type === 'COMPOSITE') { + left = evaluateComposite(composite.left); + } else if (composite.left.condition_type === 'LEAF_NODE') { + return evaluateLeaf(composite.left); + } + if (composite.right.condition_type === 'COMPOSITE') { + left = evaluateComposite(composite.right); + } else if (composite.right.condition_type === 'LEAF_NODE') { + return evaluateLeaf(composite.right); + } + + return composite.operator === 'AND' ? left && right : left || right; + }; + + const handleValidation = (transitionRules: any) => { + if (transitionRules.condition_type === 'LEAF_NODE') { + // console.log(evaluateLeaf(transitionRules)); + const nextScreenName = actions[evaluateLeaf(transitionRules)]; + } else { + // console.log(evaluateComposite(transitionRules)); + const answer = String(evaluateComposite(transitionRules)); + const nextScreenName = actions[answer]; + navigateToScreen(nextScreenName, { + journey: journey, + caseId, + }); + console.log({nextScreenName, actions}); + } + }; + + const onSubmit = data => { + console.log(data, 'data'); + }; + + const onError = data => { + console.log(data); + setError(data); + }; + + return ( + + {sections.map((section: string, index: number) => ( + 0 ? GenericStyles.mt16 : null, + ]}> + + {sectionMap[section]?.label} + + + + ))} +