merged todo list and feedback form working state

This commit is contained in:
aman.singh
2022-12-23 13:24:33 +05:30
parent d7ea581384
commit 36c9f0112a
22 changed files with 554 additions and 243 deletions

View File

@@ -16,6 +16,7 @@ import OtpInput from './src/screens/login/OtpInput';
import TodoList from './src/screens/todoList/TodoList';
import { RootState } from './src/store/store';
import Profile from './src/screens/Profile';
import interactionsHandler from './src/screens/caseDetails/interactionsHandler';
const Stack = createNativeStackNavigator();
@@ -28,7 +29,7 @@ const ProtectedRouter = () => {
// for setting user token in global.ts for api calling's
setGlobalUserData(sessionDetails?.sessionToken);
const d = interactionsHandler()
const dispatch = useAppDispatch();
if (!deviceId) {
@@ -68,6 +69,7 @@ const ProtectedRouter = () => {
component={Widget}
options={{
header: () => null,
animation: 'slide_from_right'
}}
/>
))}

View File

@@ -1,5 +1,5 @@
import axiosInstance, {ApiKeys, getApiUrl} from '../components/utlis/apiHelper';
import { navigateToScreen } from '../components/utlis/navigationUtlis';
import {navigateToScreen} from '../components/utlis/navigationUtlis';
import {
resetTodoList,
setCasesListData,
@@ -52,3 +52,17 @@ export const postPinnedList =
dispatch(setTodoListOffline(updatedCaseList));
});
};
export const syncCaseDetail = (data: any) => (dispatch: AppDispatch) => {
const url = getApiUrl(ApiKeys.FEEDBACK);
console.log(data);
axiosInstance
.post(url, {
id: data.id,
...data,
})
.then(res => console.log(res.data))
.catch(err => console.log(err, 'error'));
console.log(data);
};

View File

@@ -24,7 +24,7 @@ const IconLabel: React.FC<IconLabelProps> = props => {
<View style={[styles.icon, iconStyle]}>
{icon ? icon : <BulletIcon />}
</View>
<Text style={textStyle}>{text}</Text>
<Text light style={textStyle}>{text}</Text>
</View>
);
};

View File

@@ -1,20 +1,18 @@
import React, { useEffect } from 'react';
import { View } from 'react-native';
import React, {useEffect} from 'react';
import {View} from 'react-native';
import { useState } from 'react';
import { Control, Controller } from 'react-hook-form';
import { useSelector } from 'react-redux';
import RNDropDown from '../../../../RN-UI-LIB/src/components/dropdown/Dropdown';
import RNOptions from '../../../../RN-UI-LIB/src/components/dropdown/Options';
import {useState} from 'react';
import {Control, Controller} from 'react-hook-form';
import {useSelector} from 'react-redux';
import RNCheckboxGroup from '../../../../RN-UI-LIB/src/components/chechbox/CheckboxGroup';
import {ICheckboxOption} from '../../../../RN-UI-LIB/src/components/chechbox/types';
import Text from '../../../../RN-UI-LIB/src/components/Text';
import { GenericStyles } from '../../../../RN-UI-LIB/src/styles';
import template from '../../../data/RealTemplateData.json';
import { RootState } from '../../../store/store';
import {GenericStyles} from '../../../../RN-UI-LIB/src/styles';
import {RootState} from '../../../store/store';
import {AnswerType} from '../interface';
import QuestionRenderingEngine from '../QuestionRenderingEngine';
import ErrorMessage from './ErrorMessage';
import RNCheckboxGroup from '../../../../RN-UI-LIB/src/components/chechbox/CheckboxGroup';
import { ICheckboxOption } from '../../../../RN-UI-LIB/src/components/chechbox/types';
import { AnswerType } from '../interface';
import {useAppSelector } from '../../../hooks';
interface ICheckBoxGroup {
questionType: string;
@@ -29,6 +27,7 @@ interface ICheckBoxGroup {
const CheckBoxGroup: React.FC<ICheckBoxGroup> = props => {
const {questionId, widgetId, journeyId, caseId, sectionId, error} = props;
const template = useAppSelector(state => state.case.templateData)
const question =
template.questions[questionId as keyof typeof template.questions];
const options = template.options;
@@ -38,9 +37,7 @@ const CheckBoxGroup: React.FC<ICheckBoxGroup> = props => {
const dataFromRedux = useSelector(
(state: RootState) =>
state.case.caseForm?.[caseId]?.[journeyId]?.[widgetId]?.[
sectionId
]?.[questionId],
state.case.caseForm?.[caseId]?.[journeyId]?.widgetContext?.[widgetId]?.sectionContext?.[sectionId]?.questionContext?.[questionId]
);
useEffect(() => {
@@ -50,7 +47,6 @@ const CheckBoxGroup: React.FC<ICheckBoxGroup> = props => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataFromRedux]);
const computeNextQuestion = (optionId: keyof typeof options) => {
if (options[optionId]?.associateQuestions.length < 1) {
setAssociatedQuestions([]);
@@ -59,38 +55,52 @@ const CheckBoxGroup: React.FC<ICheckBoxGroup> = props => {
setAssociatedQuestions(options[optionId]?.associateQuestions);
};
const optiosList = question.options.map(option =>({label: options[option].text, value: option}));
const optiosList = question.options.map((option: any) => ({
label: options[option].text,
value: option,
}));
const handleChange = (change : Array<ICheckboxOption> | null, onChange : (...event: any[]) => void) => {
const values = change?.map(item=>item.value)
const handleChange = (
change: Array<ICheckboxOption> | null,
onChange: (...event: any[]) => void,
) => {
const values = change?.map(item => item.value);
onChange({
answer: values,
type: AnswerType.array
})
}
type: AnswerType.array,
});
};
return (
<View style={GenericStyles.mt12}>
{question.text ?<Text dark bold>
{question.text}{' '}
{question.type === 'mandatory' && (
<Text style={GenericStyles.redText}>*</Text>
)}
</Text>: null}
{question.text ? (
<Text dark bold>
{question.text}{' '}
{question.type === 'mandatory' && (
<Text style={GenericStyles.redText}>*</Text>
)}
</Text>
) : null}
<Controller
control={props.control}
rules={{
required: question.type === 'mandatory',
}}
render={({field: {onChange, value}}) => {
console.log(value)
return(
<RNCheckboxGroup onSelectionChange={(change => handleChange(change, onChange))} defaultValue={value?.answer} options={optiosList} />
)
console.log(value);
return (
<RNCheckboxGroup
onSelectionChange={change =>
handleChange(change, onChange)
}
defaultValue={value?.answer}
options={optiosList}
/>
);
}}
name={`${sectionId}.${questionId}`}
name={`widgetContext.${widgetId}.sectionContext.${props.sectionId}.questionContext.${questionId}`}
/>
<ErrorMessage show={error?.[sectionId]?.[questionId]} />
<ErrorMessage show={error?.sectionContext?.[sectionId]?.questionContext?.[questionId]} />
{associatedQuestions?.map((nextQuestion: string, index) => {
if (
template.questions[

View File

@@ -8,11 +8,11 @@ import RNDropDown from '../../../../RN-UI-LIB/src/components/dropdown/Dropdown';
import RNOptions from '../../../../RN-UI-LIB/src/components/dropdown/Options';
import Text from '../../../../RN-UI-LIB/src/components/Text';
import {GenericStyles} from '../../../../RN-UI-LIB/src/styles';
import template from '../../../data/RealTemplateData.json';
import {RootState} from '../../../store/store';
import QuestionRenderingEngine from '../QuestionRenderingEngine';
import ErrorMessage from './ErrorMessage';
import {AnswerType} from '../interface';
import {AnswerType, Options} from '../interface';
import { useAppSelector } from '../../../hooks';
interface IDropDown {
questionType: string;
@@ -27,6 +27,7 @@ interface IDropDown {
const DropDown: React.FC<IDropDown> = props => {
const {questionId, widgetId, journeyId, caseId, sectionId, error} = props;
const template = useAppSelector(state => state.case.templateData);
const question =
template.questions[questionId as keyof typeof template.questions];
const options = template.options;
@@ -36,9 +37,7 @@ const DropDown: React.FC<IDropDown> = props => {
const dataFromRedux = useSelector(
(state: RootState) =>
state.case.caseForm?.[caseId]?.[journeyId]?.[widgetId]?.[
sectionId
]?.[questionId],
state.case.caseForm?.[caseId]?.[journeyId]?.widgetContext?.[widgetId]?.sectionContext?.[sectionId]?.questionContext?.[questionId]
);
useEffect(() => {
@@ -48,27 +47,6 @@ const DropDown: React.FC<IDropDown> = props => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataFromRedux]);
// 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: keyof typeof options) => {
if (options[optionId]?.associateQuestions.length < 1) {
setAssociatedQuestions([]);
@@ -105,22 +83,21 @@ const DropDown: React.FC<IDropDown> = props => {
bottomSheetHeight={question.options.length - 1 * 10}
onValueChange={change => handleChange(change, onChange)}
value={value?.answer}>
{question.options.map(option => {
{question.options.map((option: keyof typeof options) => {
return (
<RNOptions
id={option}
id={option as string}
label={
options[option as keyof typeof options]
?.text
options[option]?.text
}
/>
);
})}
</RNDropDown>
)}
name={`${sectionId}.${questionId}`}
name={`widgetContext.${widgetId}.sectionContext.${props.sectionId}.questionContext.${questionId}`}
/>
<ErrorMessage show={error?.[sectionId]?.[questionId]} />
<ErrorMessage show={error?.sectionContext?.[sectionId]?.questionContext?.[questionId]} />
{associatedQuestions?.map((nextQuestion: string, index) => {
if (
template.questions[

View File

@@ -1,24 +1,23 @@
import React, { useEffect } from 'react';
import { Control, Controller } from 'react-hook-form';
import {
Image,
ImageBackground,
Pressable,
StyleSheet,
View,
View
} from 'react-native';
import React, {useEffect} from 'react';
import {Control, Controller} from 'react-hook-form';
import CameraClickPicture from '../../../../RN-UI-LIB/src/components/camera_click_picture/CameraClickPicture';
import template from '../../../data/RealTemplateData.json';
import {GenericStyles} from '../../../../RN-UI-LIB/src/styles';
import {useState} from 'react';
import {useSelector} from 'react-redux';
import {RootState} from '../../../store/store';
import ErrorMessage from './ErrorMessage';
import DeleteIcon from '../../../../RN-UI-LIB/src/Icons/DeleteIcon';
import {COLORS} from '../../../../RN-UI-LIB/src/styles/colors';
import { useState } from 'react';
import { useSelector } from 'react-redux';
import Text from '../../../../RN-UI-LIB/src/components/Text';
import DeleteIcon from '../../../../RN-UI-LIB/src/Icons/DeleteIcon';
import { GenericStyles } from '../../../../RN-UI-LIB/src/styles';
import { COLORS } from '../../../../RN-UI-LIB/src/styles/colors';
import { useAppSelector } from '../../../hooks';
import { RootState } from '../../../store/store';
import { AnswerType } from '../interface';
import ErrorMessage from './ErrorMessage';
interface IImageUpload {
questionType: string;
@@ -34,13 +33,12 @@ interface IImageUpload {
const ImageUpload: React.FC<IImageUpload> = props => {
const {questionId, error, sectionId, caseId, journeyId, widgetId} = props;
const [image, setImage] = useState('');
const template = useAppSelector(state => state.case.templateData);
const question =
template.questions[questionId as keyof typeof template.questions];
const dataFromRedux = useSelector(
(state: RootState) =>
state.case.caseForm?.[caseId]?.[journeyId]?.[widgetId]?.[
sectionId
]?.[questionId],
state.case.caseForm?.[caseId]?.[journeyId]?.widgetContext?.[widgetId]?.sectionContext?.[sectionId]?.questionContext?.[questionId]
);
useEffect(() => {
@@ -84,7 +82,7 @@ const ImageUpload: React.FC<IImageUpload> = props => {
onPictureClickSuccess={clickedImage => handleChange(clickedImage, onChange)}
/>
)}
name={`${props.sectionId}.${questionId}`}
name={`widgetContext.${widgetId}.sectionContext.${props.sectionId}.questionContext.${questionId}`}
/>
) : (
<View>
@@ -97,7 +95,7 @@ const ImageUpload: React.FC<IImageUpload> = props => {
</ImageBackground>
</View>
)}
<ErrorMessage show={error?.[sectionId]?.[questionId]} />
<ErrorMessage show={error?.sectionContext?.[sectionId]?.questionContext?.[questionId]} />
</View>
);
};

View File

@@ -9,11 +9,11 @@ import RadioChip from '../../../../RN-UI-LIB/src/components/radio_button/RadioCh
import RadioGroup from '../../../../RN-UI-LIB/src/components/radio_button/RadioGroup';
import Text from '../../../../RN-UI-LIB/src/components/Text';
import {GenericStyles} from '../../../../RN-UI-LIB/src/styles';
import template from '../../../data/RealTemplateData.json';
import {RootState} from '../../../store/store';
import QuestionRenderingEngine from '../QuestionRenderingEngine';
import ErrorMessage from './ErrorMessage';
import { AnswerType } from '../interface';
import {AnswerType} from '../interface';
import {useAppSelector} from '../../../hooks';
interface IRadioButton {
questionType: string;
@@ -28,8 +28,8 @@ interface IRadioButton {
const RadioButton: React.FC<IRadioButton> = props => {
const {questionId, widgetId, journeyId, caseId, sectionId, error} = props;
const question =
template.questions[questionId as keyof typeof template.questions];
const template = useAppSelector(state => state.case.templateData);
const question = template.questions[questionId];
const options = template.options;
const [associatedQuestions, setAssociatedQuestions] = useState<
Array<string>
@@ -37,9 +37,9 @@ const RadioButton: React.FC<IRadioButton> = props => {
const dataFromRedux = useSelector(
(state: RootState) =>
state.case.caseForm?.[caseId]?.[journeyId]?.[widgetId]?.[
sectionId
]?.[questionId],
state.case.caseForm?.[caseId]?.[journeyId]?.widgetContext?.[
widgetId
]?.sectionContext?.[sectionId]?.questionContext?.[questionId],
);
useEffect(() => {
@@ -49,27 +49,6 @@ const RadioButton: React.FC<IRadioButton> = props => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataFromRedux]);
// 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: keyof typeof options) => {
if (options[optionId]?.associateQuestions.length < 1) {
setAssociatedQuestions([]);
@@ -78,16 +57,16 @@ const RadioButton: React.FC<IRadioButton> = props => {
setAssociatedQuestions(options[optionId]?.associateQuestions);
};
const handleChange = (change : string | null, onChange : (...event: any[]) => void) => {
const handleChange = (
change: string | null,
onChange: (...event: any[]) => void,
) => {
computeNextQuestion(change as keyof typeof options);
onChange({
answer: change,
type: AnswerType.option
})
}
type: AnswerType.option,
});
};
return (
<View style={GenericStyles.mt12}>
@@ -110,32 +89,35 @@ const RadioButton: React.FC<IRadioButton> = props => {
question.metadata.orientation || 'verticle'
}>
{question?.metadata.buttonType === 'button'
? question.options.map(option => {
return(
<RNRadioButton
id={option}
value={
options[
option as keyof typeof options
]?.text
}
/>
)})
: question.options.map(option => (
? question.options.map((option: keyof typeof options) => {
return (
<RNRadioButton
id={option as string}
value={
options[option]?.text
}
/>
);
})
: question.options.map((option: keyof typeof options) => (
<RadioChip
id={option}
id={option as string}
value={
options[
option as keyof typeof options
]?.text
options[option]?.text
}
/>
))}
</RadioGroup>
)}
name={`${sectionId}.${questionId}`}
name={`widgetContext.${widgetId}.sectionContext.${props.sectionId}.questionContext.${questionId}`}
/>
<ErrorMessage
show={
error?.widgetContext?.[widgetId]?.sectionContext?.[
sectionId
]?.questionContext?.[questionId]
}
/>
<ErrorMessage show={error?.[sectionId]?.[questionId]} />
{associatedQuestions?.map((nextQuestion: string, index) => {
if (
template.questions[
@@ -148,7 +130,7 @@ const RadioButton: React.FC<IRadioButton> = props => {
questionType={
template.questions[
nextQuestion as keyof typeof template.questions
].input_type
].inputType
}
questionId={nextQuestion}
name={widgetId}

View File

@@ -3,10 +3,10 @@ import React from 'react';
import {Control, Controller} from 'react-hook-form';
import {GenericStyles} from '../../../../RN-UI-LIB/src/styles';
import StarRating from '../../../../RN-UI-LIB/src/components/star_rating/StarRating';
import template from '../../../data/RealTemplateData.json';
import ErrorMessage from './ErrorMessage';
import Text from '../../../../RN-UI-LIB/src/components/Text';
import { AnswerType } from '../interface';
import { useAppSelector } from '../../../hooks';
interface IRating {
questionType: string;
questionId: string;
@@ -19,8 +19,8 @@ interface IRating {
}
const Rating: React.FC<IRating> = props => {
const {questionId, error, sectionId} = props;
const {questionId, error, sectionId, widgetId} = props;
const template = useAppSelector(state => state.case.templateData);
const question =
template.questions[questionId as keyof typeof template.questions];
if (!question) {
@@ -55,9 +55,9 @@ const Rating: React.FC<IRating> = props => {
maxRating={5}
/>
)}
name={`${props.sectionId}.${questionId}`}
name={`widgetContext.${widgetId}.sectionContext.${props.sectionId}.questionContext.${questionId}`}
/>
<ErrorMessage show={error?.[sectionId]?.[questionId]} />
<ErrorMessage show={error?.sectionContext?.[sectionId]?.questionContext?.[questionId]} />
</View>
);
};

View File

@@ -2,12 +2,12 @@ import {View} from 'react-native';
import React from 'react';
import {Control, Controller} from 'react-hook-form';
import template from '../../../data/RealTemplateData.json';
import RNTextArea from '../../../../RN-UI-LIB/src/components/TextArea';
import Text from '../../../../RN-UI-LIB/src/components/Text';
import {GenericStyles} from '../../../../RN-UI-LIB/src/styles';
import ErrorMessage from './ErrorMessage';
import { AnswerType } from '../interface';
import { useAppSelector } from '../../../hooks';
interface ITextArea {
questionType: string;
@@ -21,8 +21,8 @@ interface ITextArea {
}
const TextArea: React.FC<ITextArea> = props => {
const {questionId, error, sectionId} = props;
const {questionId, error, sectionId, widgetId} = props;
const template = useAppSelector(state => state.case.templateData);
const question =
template.questions[questionId as keyof typeof template.questions];
if (!question) {
@@ -58,9 +58,9 @@ const TextArea: React.FC<ITextArea> = props => {
title=""
/>
)}
name={`${props.sectionId}.${questionId}`}
name={`widgetContext.${widgetId}.sectionContext.${props.sectionId}.questionContext.${questionId}`}
/>
<ErrorMessage show={error?.[sectionId]?.[questionId]} />
<ErrorMessage show={error?.sectionContext?.[sectionId]?.questionContext?.[questionId]} />
</View>
);
};

View File

@@ -4,10 +4,10 @@ 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/RealTemplateData.json';
import Text from '../../../../RN-UI-LIB/src/components/Text';
import ErrorMessage from './ErrorMessage';
import { AnswerType } from '../interface';
import { useAppSelector } from '../../../hooks';
interface ITextInput {
questionType: string;
@@ -21,8 +21,8 @@ interface ITextInput {
}
const TextInput: React.FC<ITextInput> = props => {
const {questionId, error, sectionId} = props;
const {questionId, error, sectionId, widgetId} = props;
const template = useAppSelector(state => state.case.templateData);
const question =
template.questions[questionId as keyof typeof template.questions];
if (!question) {
@@ -56,9 +56,9 @@ const TextInput: React.FC<ITextInput> = props => {
containerStyle={[GenericStyles.mt12]}
/>
)}
name={`${props.sectionId}.${questionId}`}
name={`widgetContext.${widgetId}.sectionContext.${props.sectionId}.questionContext.${questionId}`}
/>
<ErrorMessage show={error?.[sectionId]?.[questionId]} />
<ErrorMessage show={error?.widgetContext?.[widgetId]?.sectionContext?.[sectionId]?.questionContext?.[questionId]} />
</View>
);
};

View File

@@ -1,22 +1,21 @@
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import { Pressable, ScrollView, StyleSheet, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useDispatch, useSelector } from 'react-redux';
import React, {useState} from 'react';
import {useForm} from 'react-hook-form';
import {Pressable, ScrollView, StyleSheet, View} from 'react-native';
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 Text from '../../../RN-UI-LIB/src/components/Text';
import BackArrowIcon from '../../../RN-UI-LIB/src/Icons/BackArrowIcon';
import CloseIcon from '../../../RN-UI-LIB/src/Icons/CloseIcon';
import { GenericStyles, getShadowStyle } from '../../../RN-UI-LIB/src/styles';
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
import Realjson from '../../data/RealTemplateData.json';
import { updateInteraction } from '../../reducer/caseReducre';
import { RootState } from '../../store/store';
import { goBack, navigateToScreen } from '../utlis/navigationUtlis';
import {
ConditionType,
IDecision, ILeaf
} from './interface';
import {GenericStyles, getShadowStyle} from '../../../RN-UI-LIB/src/styles';
import {COLORS} from '../../../RN-UI-LIB/src/styles/colors';
import {useAppSelector} from '../../hooks';
import { updateCaseDetail } from '../../reducer/allCasesSlice';
import {deleteInteraction, updateInteraction} from '../../reducer/caseReducre';
import {RootState} from '../../store/store';
import {goBack, navigateToScreen} from '../utlis/navigationUtlis';
import {ConditionType, IDecision, ILeaf} from './interface';
import RenderQuestion from './RenderQuestion';
interface IWidget {
@@ -32,21 +31,25 @@ interface IWidget {
const Widget: React.FC<IWidget> = props => {
const {name, params} = props.route;
const {caseId, journey} = params;
const {sections, transitionRules, isLeaf} =
Realjson.widget[name as keyof typeof Realjson.widget];
const sectionMap = Realjson.sections;
const templateData = useAppSelector(state => state.case.templateData);
const caseData = useAppSelector(state => state.allCases.caseDetails[caseId]);
const {sections, transitionRules, isLeaf} = templateData.widget[name];
const sectionMap = templateData.sections;
const {actions, conditions} = transitionRules;
const [error, setError] = useState();
const dispatch = useDispatch();
const dataToBeValidated = useSelector(
(state: RootState) => state.case.caseForm?.[caseId]?.[journey]?.[name],
const dataToBeValidated = useAppSelector(
state => state.case.caseForm?.[caseId]?.[journey],
);
console.log(dataToBeValidated);
const journeyData = useSelector(
(state: RootState) => state.case.caseForm?.[caseId]?.[journey],
);
console.log(journeyData);
const {control, handleSubmit} = useForm({
defaultValues: dataToBeValidated,
@@ -55,7 +58,11 @@ const Widget: React.FC<IWidget> = props => {
const evaluateLeaf = (leaf: ILeaf, data: any): boolean => {
switch (leaf.operator) {
case 'MATCHES':
return leaf.right === data[leaf.section][leaf.left]?.answer;
return (
leaf.right ===
data?.widgetContext?.[name]?.sectionContext?.[leaf.section]
?.questionContext?.[leaf.left]?.answer
);
default:
return false;
}
@@ -66,7 +73,9 @@ const Widget: React.FC<IWidget> = props => {
case 'MATCHES':
return (
leaf.right ===
journeyData[leaf.widgetId][leaf.section][leaf.left]
journeyData.widgetContext[leaf.widgetId]?.sectionContext?.[
leaf.section
]?.questionContext?.[leaf.left]
);
default:
return false;
@@ -114,7 +123,7 @@ const Widget: React.FC<IWidget> = props => {
};
const onSubmit = (data: any) => {
console.log(data)
console.log(data, 'submit');
dispatch(
updateInteraction({
caseId,
@@ -139,16 +148,31 @@ const Widget: React.FC<IWidget> = props => {
answer: data,
}),
);
const rules = Realjson.journey[journey].transitionRules.conditions;
const screeenAction = Realjson.journey[journey].transitionRules.actions;
let nextScreenName = '';
const rules =
templateData.journey[journey as keyof typeof templateData.journey]
.transitionRules.conditions;
const screeenAction =
templateData.journey[journey as keyof typeof templateData.journey]
.transitionRules.actions;
let nextActions = '';
if (rules.condition_type === 'LEAF_NODE') {
nextScreenName = screeenAction[evaluateLeafJourney(rules)];
nextActions = screeenAction[String(evaluateLeafJourney(rules as ILeaf))];
} else {
const answer = String(evaluateComposite(rules));
nextScreenName = screeenAction[answer];
const answer = String(evaluateComposite(rules as IDecision, data));
nextActions = screeenAction[answer];
}
console.log(nextActions);
// dispatch some actions to redux store
dispatch(updateCaseDetail({
caseId,
journeyId: journey,
widgetId: name,
answer: data,
caseData: caseData,
nextActions
}))
navigateToScreen('Home', {
journey: journey,
caseId,
@@ -185,7 +209,7 @@ const Widget: React.FC<IWidget> = props => {
GenericStyles.p16,
GenericStyles.whiteBackground,
]}>
{sections.map((section, index: number) => (
{sections.map((section: any, index: number) => (
<View
key={section + index}
style={[
@@ -224,7 +248,17 @@ const Widget: React.FC<IWidget> = props => {
variant={'secondary'}
style={styles.fb45}
title={'Back'}
onPress={goBack}
onPress={() => {
dispatch(
deleteInteraction({
caseId,
journeyId: journey,
widgetId: name,
}),
);
goBack();
}}
leftIcon={<BackArrowIcon size={10} />}
/>
<Button
style={styles.fb45}

View File

@@ -54,7 +54,13 @@ export interface ILeaf {
export enum AnswerType {
text = 'text',
option = 'option',
number = 'number',
array = 'array'
option = 'text',
number = 'text',
array = 'option'
}
export interface Options {
text: string;
associatedQuestions: Array<string>;
metadata: any;
}

View File

@@ -19,7 +19,8 @@ export enum ApiKeys {
ALL_CASES,
CASE_DETAIL,
PINNED_CASES,
LOGOUT
LOGOUT,
FEEDBACK
}
const API_URLS: Record<ApiKeys, string> = {} as Record<ApiKeys, string>;
@@ -29,6 +30,7 @@ API_URLS[ApiKeys.ALL_CASES] = '/cases/all-cases';
API_URLS[ApiKeys.CASE_DETAIL] = '/cases/get-cases';
API_URLS[ApiKeys.PINNED_CASES] = '/cases/pin';
API_URLS[ApiKeys.LOGOUT] = '/auth/logout';
API_URLS[ApiKeys.FEEDBACK] = '/cases/feedback';
const MOCK_API_URLS: Record<ApiKeys, string> = {} as Record<ApiKeys, string>;

View File

@@ -39,8 +39,14 @@
}
},
"actions": {
"true": "verified",
"false": "not verified"
"true": {
"status": "VERIFIED_SUCCESS",
"nextJourney": ""
},
"false":{
"status": "VERIFICATION_FAILED",
"nextJourney": "PERMANENT_ADDRESS_VERIFICATION_TASK"
}
}
},
"isLeaf": false

View File

@@ -68,7 +68,11 @@ const allCasesSlice = createSlice({
if (details?.length) {
const initialValue = {...state.caseDetails};
const detailsData = details.reduce((prev: any, item: any) => {
prev[item.id] = item;
prev[item.id] = {
...item,
isSynced: true,
};
return prev;
}, initialValue);
state.caseDetails = detailsData;
@@ -79,6 +83,58 @@ const allCasesSlice = createSlice({
state.pinnedRankCount = maxPinnedRank;
state.loading = false;
},
updateCaseDetail: (state, action) => {
const {caseId, journeyId, answer, caseData, nextActions} =
action.payload;
console.log(action.payload);
const updatedValue = {...caseData};
updatedValue.isSynced = false;
if (!updatedValue.context) {
updatedValue.context = {
taskSequence: updatedValue.tasks.map(task => task.taskType),
currentTask: nextActions.nextJourney,
taskContext: {
[journeyId]: [
{
taskStatus: nextActions.status,
...answer,
},
],
},
};
}
if (updatedValue.context.taskContext?.[journeyId]) {
const journey = [
...updatedValue.context.taskContext?.[journeyId],
{
taskStatus: nextActions.status,
...answer,
},
];
updatedValue.context = {
currentTask: nextActions.nextJourney,
taskContext: {
[journeyId]: journey,
},
};
} else {
const taskContext = updatedValue.taskContext;
(taskContext[journeyId] = [
{
taskStatus: nextActions.status,
...answer,
},
]),
(updatedValue.context = {
currentTask: nextActions.nextJourney,
taskContext,
});
}
state.caseDetails[caseId] = updatedValue;
},
setPinnedRank: (state, action) => {
const caseId = action.payload.caseReferenceId;
const isCasePresent = state.intermediateTodoListMap[caseId];
@@ -157,7 +213,8 @@ export const {
proceedToTodoList,
deleteIntermediateTodoListItem,
setSelectedTodoListMap,
resetSelectedTodoList
resetSelectedTodoList,
updateCaseDetail,
} = allCasesSlice.actions;
export default allCasesSlice.reducer;

View File

@@ -7,26 +7,23 @@ interface ICaseReducer {
caseForm: {
[caseId: string]: {
[journeyId: string]: {
[sectionId: string]: {
"widgetContext":{
[widgetId: string]: {
[questionId: string]: string;
"sectionContext":{
[sectionId: string]: {
"questionContext":{
[questionId: string]: string;
}
};
}
};
};
};
};
};
toBeSynced: {
[caseId: string]: {
[journeyId: string]: {
[sectionId: string]: {
[widgetId: string]: {
[questionId: string]: string;
};
};
}
};
};
};
toBeSynced: any;
allCases: Array<Data>;
templateData: any;
}
const initialState = {
@@ -148,7 +145,8 @@ const initialState = {
GEOLOCATION: [],
},
},
};
templateData: {}
} as ICaseReducer;
export const caseSlice = createSlice({
name: 'case',
@@ -167,14 +165,24 @@ export const caseSlice = createSlice({
data[caseId] = {};
}
if (!data[caseId][journeyId]) {
// @ts-ignore
data[caseId][journeyId] = {};
}
if (!data[caseId][journeyId][widgetId]) {
data[caseId][journeyId][widgetId] = {};
}
data[caseId][journeyId][widgetId] = answer;
data[caseId][journeyId] = answer;
state.caseForm = data;
},
deleteInteraction: (state, action) => {
const {caseId, journeyId, widgetId, answer} = action.payload;
console.log({caseId, journeyId, widgetId, answer})
const data = state.caseForm;
delete data[caseId][journeyId].widgetContext[widgetId];
state.caseForm = data;
},
updateTemplateData: (state, action) => {
state.templateData = action.payload;
},
setAllCases: (state, action) => {
state.allCases = action.payload;
},
@@ -185,7 +193,7 @@ export const caseSlice = createSlice({
},
});
export const {increaseByOne, decreaseByOne, updateInteraction, setAllCases} =
export const {increaseByOne, decreaseByOne, updateInteraction, setAllCases, deleteInteraction, updateTemplateData} =
caseSlice.actions;
export default caseSlice.reducer;

View File

@@ -18,6 +18,8 @@ import {
import AllCases from './AllCases';
import ComplatedCase from './ComplatedCase';
import {CaseStatuses} from './interface';
import RealJson from '../../data/RealTemplateData.json';
import { updateTemplateData } from '../../reducer/caseReducre';
const LogoActions = () => {
const {newlyPinnedCases, selectedTodoListCount} = useAppSelector(
@@ -44,6 +46,8 @@ const LogoActions = () => {
);
};
const AllCasesMain = () => {
const {casesList, newlyPinnedCases, selectedTodoListCount} = useAppSelector(
state => state.allCases,
@@ -75,6 +79,9 @@ const AllCasesMain = () => {
const handleProfileIconPress = () => {
navigateToScreen('Profile');
};
useEffect(() => {
dispatch(updateTemplateData(RealJson));
}, []);
return (
<View style={GenericStyles.fill}>

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { SafeAreaView, StyleSheet, View } from 'react-native';
import { Alert, SafeAreaView, StyleSheet, View } from 'react-native';
import Button from '../../../RN-UI-LIB/src/components/Button';
import BackArrowIcon from '../../../RN-UI-LIB/src/Icons/BackArrowIcon';
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
@@ -30,12 +30,23 @@ const CaseDetails: React.FC<ICaseDetails> = (props) => {
<View style={GenericStyles.p16}>
<UserDetailsSection caseDetail={detailObject} />
<TaskStepper caseDetail={detailObject} />
<Button
{/* <Button
style={{}}
title="Trigger journey"
variant="secondary"
onPress={() => navigateToScreen('w1', {caseId: 'labkjisbldfyuk', journey: 'COMMUNICATION_ADDRESS_VERIFICATION_TASK'})}
/>
onPress={() => Alert.alert("Confirm" , 'Are you sure you want to logout?', [
{
text: 'Cancle',
onPress: (value?: string) => console.log(value),
style: 'default'
},
{
text: 'Logout',
onPress: (value?: string) => console.log(value),
style: 'cancel'
},
])}
/> */}
</View>
</SafeAreaView>
);

View File

@@ -1,66 +1,108 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { useSelector } from 'react-redux';
import React, { useCallback, useState } from 'react';
import {
Image, Modal,
Pressable,
SafeAreaView,
StyleSheet,
View
} from 'react-native';
import Avatar from '../../../RN-UI-LIB/src/components/Avatar';
import Heading from '../../../RN-UI-LIB/src/components/Heading';
import Text from '../../../RN-UI-LIB/src/components/Text';
import CloseIcon from '../../../RN-UI-LIB/src/Icons/CloseIcon';
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
import { COLORS } from '../../../RN-UI-LIB/src/styles/colors';
import { dateFormat } from '../../../RN-UI-LIB/src/utlis/dates';
import CalenderIcon from '../../assets/icons/CalenderIcon';
import DocumentIcon from '../../assets/icons/DocumentIcon';
import IconLabel from '../../common/IconLabel';
import { RootState } from '../../store/store';
import { CaseDetail, LoanAccountStatusUIMapping, LoanTypeUIMapping } from './interface';
interface IUserDetailsSection{
caseDetail : any;
interface IUserDetailsSection {
caseDetail: CaseDetail;
}
const UserDetailsSection: React.FC<IUserDetailsSection> = (props) => {
const UserDetailsSection: React.FC<IUserDetailsSection> = props => {
const {caseDetail} = props;
const {customerInfo, loanDetails} = caseDetail;
const disbursalDate = loanDetails.disbursalDate;
const allocationDate = loanDetails.allocationDate;
const disbursalDate = dateFormat(new Date(loanDetails.disbursalDate), "DD MMM, YYYY");
const allocationDate = dateFormat(new Date(caseDetail.allocatedAt), "DD MMM, YYYY");
const [openImage, setOpenImage] = useState<boolean>(false);
const handleOpenClose = useCallback(() => {
setOpenImage(prev => !prev);
}, []);
return (
<View style={[GenericStyles.row, GenericStyles.pb16, styles.container]}>
<View style={[styles.avatarContainer]}>
<Avatar
name={customerInfo.customerName}
dataURI={customerInfo.imageURL}
/>
<Pressable onPress={handleOpenClose}>
<Avatar
size={60}
name={customerInfo.customerName}
dataURI={customerInfo.imageURL}
/>
</Pressable>
</View>
<View style={[styles.infoContainer]}>
<Heading dark type="h4">
{customerInfo.customerName}
</Heading>
<View style={[GenericStyles.row]}>
<View style={[GenericStyles.row, GenericStyles.mb8]}>
<IconLabel
containerStyle={{flex: 0.5}}
icon={<DocumentIcon />}
text={loanDetails.loanType}
text={LoanTypeUIMapping[loanDetails.loanType]}
/>
<IconLabel
text={loanDetails.loanAccountStatus}
containerStyle={{flex: 0.5}}
containerStyle={GenericStyles.ml10}
text={LoanAccountStatusUIMapping[loanDetails.loanAccountStatus]}
/>
</View>
<View style={[GenericStyles.row]}>
<View style={[GenericStyles.row, GenericStyles.mb8]}>
<IconLabel
icon={<CalenderIcon />}
text={disbursalDate}
containerStyle={{flex: 0.5}}
/>
<IconLabel
text={`${loanDetails.tenureMonths} months`}
containerStyle={{flex: 0.5}}
text={`${loanDetails.tenureMonths} ${loanDetails.tenureMonths > 1 ? "Months" : "Month"}`}
containerStyle={GenericStyles.ml10}
/>
</View>
<View style={[GenericStyles.pt16]}>
<Text style={[GenericStyles.fontSize15]}>
<Text light style={[GenericStyles.fontSize15]}>
Allocated on {allocationDate}
</Text>
</View>
</View>
<Modal animationType={'slide'} visible={openImage} onRequestClose={handleOpenClose}>
<SafeAreaView
style={[
GenericStyles.fill,
{backgroundColor: COLORS.BACKGROUND.HEADER},
]}>
<View style={GenericStyles.p16}>
<Pressable onPress={handleOpenClose}>
<CloseIcon />
</Pressable>
</View>
<View
style={[
GenericStyles.fill,
GenericStyles.alignCenter,
GenericStyles.row
]}>
<Image
style={[
GenericStyles.fill,
styles.imageStyle,
]}
source={{uri: customerInfo.imageURL}}
resizeMode={'contain'}
/>
</View>
</SafeAreaView>
</Modal>
</View>
);
};
@@ -78,6 +120,12 @@ const styles = StyleSheet.create({
infoContainer: {
flex: 0.6,
},
imageStyle:{
height: 300
},
backgroundColor:{
backgroundColor: COLORS.BACKGROUND.HEADER
}
});
export default UserDetailsSection;

View File

@@ -0,0 +1,28 @@
import { StyleSheet, Text, View } from 'react-native'
import React, { useEffect } from 'react'
import { useAppDispatch, useAppSelector } from '../../hooks'
import { _map } from '../../../RN-UI-LIB/src/utlis/common';
import { syncCaseDetail } from '../../action/dataActions';
const interactionsHandler = () => {
const dispatch = useAppDispatch();
const allCasesDetails = useAppSelector(state => state.allCases.caseDetails);
useEffect(() => {
const notSyncedCases: Array<any> = [];
_map(allCasesDetails, (el)=> {
if(!allCasesDetails[el].isSynced){
const caseId = allCasesDetails[el].id
notSyncedCases.push({...allCasesDetails[el], caseId})
}
})
console.log(notSyncedCases.length, "not synced")
notSyncedCases.map((caseItem)=> dispatch(syncCaseDetail(caseItem)))
}, [allCasesDetails])
return null
}
export default interactionsHandler

View File

@@ -0,0 +1,121 @@
export enum LoanType {
PERSONAL_LOAN = "PERSONAL_LOAN",
HOUSE_LOAN = "HOUSE_LOAN"
}
export enum LoanAccountStatus {
ACTIVE = "ACTIVE",
CLOSE = "CLOSE"
}
export enum LoanAccountStatusUIMapping {
ACTIVE = "Active",
CLOSE = "Close"
}
export enum LoanTypeUIMapping {
PERSONAL_LOAN = 'Personal loan',
HOUSE_LOAN = 'House loan'
}
export interface LoanDetails {
loanAccountNumber: string;
disbursalDate: string;
disbursementAmount: number;
firstDueDate: string;
tenureMonths: number;
loanType: LoanType;
loanAccountStatus: LoanAccountStatus;
productCode: string;
}
export interface CustomerInfo {
customerReferenceId: string;
customerName: string;
primaryPhoneNumber: string;
imageURL: string;
geoLocation: string;
}
export interface Address {
referenceId: string;
houseNumber: string;
lineOne: string;
lineTwo: string;
locality: string;
street: string;
city: string;
state: string;
pinCode: string;
type: string;
source: string;
current: boolean;
permanent: boolean;
zipCode?: any;
addressQualityStatus?: any;
}
export interface Metadata {
'@class': string;
address: Address;
geoLocation: string;
primaryPhoneNumber: string;
}
export interface Task {
taskId: number;
taskType: string;
metadata: Metadata;
}
export interface Q1 {
optionId: string;
text: string;
}
export interface QuestionContext {
q1: Q1[];
}
export interface S1 {
questionContext: QuestionContext;
}
export interface SectionContext {
s1: S1;
}
export interface W1 {
sectionContext: SectionContext;
}
export interface WidgetContext {
w1: W1;
}
export interface COMMUNICATIONADDRESSVERIFICATIONTASK {
taskStatus: string;
widgetContext: WidgetContext;
}
export interface TaskContext {
COMMUNICATION_ADDRESS_VERIFICATION_TASK: COMMUNICATIONADDRESSVERIFICATIONTASK[];
}
export interface Context {
taskSequence: string[];
currentTask: string;
taskContext: TaskContext;
}
export interface CaseDetail {
id: string;
caseStatus: string;
createdAt: number;
updatedAt: number;
allocatedAt: number;
loanDetails: LoanDetails;
customerInfo: CustomerInfo;
tasks: Task[];
context: Context;
}