Fakultas Ilmu Komputer UI

Commit 62921390 authored by wulanmantiri's avatar wulanmantiri
Browse files

[GREEN] Integrate update diet questionnaire API

parent f2eecd03
......@@ -27,9 +27,9 @@ import {
} from 'scenes';
import { FC } from 'react';
export interface NavRoute {
export interface NavRoute<T = any> {
name: string;
component: FC;
component: FC<T>;
header?: string;
}
......@@ -121,7 +121,12 @@ export const unpaidClientNavigation: NavRoute[] = [
...navigation,
];
export const onboardingClientNavigation: NavRoute[] = [
export const clientNavigation: NavRoute[] = [
{
name: ROUTES.clientProfile,
component: ClientProfile,
header: 'Profil Saya',
},
{
name: ROUTES.extendedQuestionnaire,
component: ExtendedQuestionnaire,
......@@ -158,15 +163,6 @@ export const onboardingClientNavigation: NavRoute[] = [
})),
];
export const clientNavigation: NavRoute[] = [
{
name: ROUTES.clientProfile,
component: ClientProfile,
header: 'Profil Saya',
},
...onboardingClientNavigation,
];
export const adminNavigation: NavRoute[] = [
{
name: ROUTES.clientProfile,
......@@ -177,7 +173,8 @@ export const adminNavigation: NavRoute[] = [
export const testNavigation: NavRoute[] = [
...navigation,
...onboardingClientNavigation,
...clientNavigation,
...nutritionistNavigation,
{
name: ROUTES.initial,
component: InitialPage,
......
......@@ -11,7 +11,3 @@ export const drinkFrequency = [
'2 gelas (500 ml) per hari',
'Lebih dari 2 gelas per hari',
];
export const checkbox = {
OTHER: 'OTHER',
};
......@@ -313,6 +313,7 @@ export const selectFields = {
label:
'Apakah Anda melakukan salah satu dari aktivitas fisik atau olahraga berikut?',
hasOtherChoice: true,
otherChoiceValue: 9,
choiceList: [
'Hampir tidak pernah olahraga',
'Jogging',
......
import React, { FC } from 'react';
import { View, ScrollView } from 'react-native';
import { View } from 'react-native';
import { Text } from 'react-native-elements';
import { useNavigation } from '@react-navigation/native';
import { InfoCard, BigButton } from 'components/core';
import { InfoCard } from 'components/core';
import { RadioButtonGroup } from 'components/form';
import { Section } from 'components/layout';
import * as ROUTES from 'constants/routes';
import { useForm } from 'hooks';
import { layoutStyles, typographyStyles } from 'styles';
import { typographyStyles } from 'styles';
import { styles } from './styles';
import { consentQuestions, termsAndConditions, initialValues } from './schema';
const ConsentForm: FC = () => {
const navigation = useNavigation();
import QuestionnaireWrapper from '../QuestionnaireWrapper';
import { NavProps } from '../QuestionnaireWrapper/types';
const {
getFormFieldProps,
handleSubmit,
isSubmitting,
values: formValues,
} = useForm({
initialValues,
onSubmit: async (values) => {
console.log(values);
navigation.navigate(ROUTES.extendedQuestionnaire);
},
});
const isValid =
formValues.agree_to_all_statements_consent &&
formValues.personal_data_consent;
return (
<ScrollView contentContainerStyle={layoutStyles}>
<View style={styles.tnc}>
<Text style={[typographyStyles.bodyMedium, styles.spacing]}>
Pelayanan gizi di Dietela, dilakukan dalam bentuk program perubahan
pola makan dan gaya hidup menjadi lebih sehat yang berlangsung mulai
dari 3 hari sampai dengan 6 bulan. Selama program berlangsung Anda
sebagai klien akan didampingi oleh seorang Nutrisionis atau Dietisien
yang didelegasikan oleh Dietela.
</Text>
<Text style={[typographyStyles.bodyMedium, styles.spacing]}>
Dalam program tersebut akan diberikan servis berupa, “pengecekan gizi”
secara langsung atau online, “konseling gizi” via pesan elektronik
atau telepon atau video call, “diet coaching” via pesan elektronik,
“monitoring rutin” melalui pesan elektronik, “kunjungan gizi ke
lokasi” klien (hanya untuk layanan tertentu), “follow-up” (hanya untuk
klien layanan berjangka 1 bulan atau lebih) melalui telepon atau video
call, dan “exit counseling” atau konseling akhir (hanya untuk klien
layanan berjangka 1 bulan atau lebih) yang dilakukan 1 bulan sekali
melalui telepon atau video call.
</Text>
<Text style={[typographyStyles.bodyMedium, styles.spacing]}>
Seluruh data dan informasi klien akan disimpan pada lokasi penyimpanan
yang aman dan tidak akan kami berikan kepada pihak manapun.
</Text>
<Text style={[typographyStyles.bodyMedium, styles.spacing]}>
Dengan menceklis kolom dibawah ini, maka klien setuju untuk:
</Text>
{termsAndConditions.map((condition, i) => (
<Section key={`condition${i}`}>
<InfoCard content={`${i + 1}. ${condition}`} />
const ConsentForm: FC<NavProps> = (navProps) => (
<QuestionnaireWrapper
{...navProps}
Questionnaire={({ getFormFieldProps }) => (
<View>
<View style={styles.tnc}>
<Text style={[typographyStyles.bodyMedium, styles.spacing]}>
Pelayanan gizi di Dietela, dilakukan dalam bentuk program perubahan
pola makan dan gaya hidup menjadi lebih sehat yang berlangsung mulai
dari 3 hari sampai dengan 6 bulan. Selama program berlangsung Anda
sebagai klien akan didampingi oleh seorang Nutrisionis atau
Dietisien yang didelegasikan oleh Dietela.
</Text>
<Text style={[typographyStyles.bodyMedium, styles.spacing]}>
Dalam program tersebut akan diberikan servis berupa, “pengecekan
gizi” secara langsung atau online, “konseling gizi” via pesan
elektronik atau telepon atau video call, “diet coaching” via pesan
elektronik, “monitoring rutin” melalui pesan elektronik, “kunjungan
gizi ke lokasi” klien (hanya untuk layanan tertentu), “follow-up”
(hanya untuk klien layanan berjangka 1 bulan atau lebih) melalui
telepon atau video call, dan “exit counseling” atau konseling akhir
(hanya untuk klien layanan berjangka 1 bulan atau lebih) yang
dilakukan 1 bulan sekali melalui telepon atau video call.
</Text>
<Text style={[typographyStyles.bodyMedium, styles.spacing]}>
Seluruh data dan informasi klien akan disimpan pada lokasi
penyimpanan yang aman dan tidak akan kami berikan kepada pihak
manapun.
</Text>
<Text style={[typographyStyles.bodyMedium, styles.spacing]}>
Dengan menceklis kolom dibawah ini, maka klien setuju untuk:
</Text>
{termsAndConditions.map((condition, i) => (
<Section key={`condition${i}`}>
<InfoCard content={`${i + 1}. ${condition}`} />
</Section>
))}
</View>
{consentQuestions.map((props, i) => (
<Section key={`radiobuttongroup${i}`}>
<RadioButtonGroup
{...props}
{...getFormFieldProps(props.name)}
choices={props.choiceList.map((label, id) => ({
label,
value: id + 1,
}))}
/>
</Section>
))}
</View>
{consentQuestions.map((props, i) => (
<Section key={`radiobuttongroup${i}`}>
<RadioButtonGroup
{...props}
{...getFormFieldProps(props.name)}
choices={props.choiceList.map((label, id) => ({
label,
value: id + 1,
}))}
/>
</Section>
))}
<Section>
<BigButton
title="lanjut"
onPress={handleSubmit}
loading={isSubmitting}
disabled={!isValid}
/>
</Section>
</ScrollView>
);
};
)}
initialValues={initialValues}
/>
);
export default ConsentForm;
......@@ -13,6 +13,7 @@ export const termsAndConditions = [
export const initialValues = {
agree_to_all_statements_consent: 0,
personal_data_consent: 0,
step: 1,
};
export const consentQuestions = [
......
......@@ -7,12 +7,14 @@ import {
Datepicker,
} from 'components/form';
import { initialValues, fieldValidations } from './schema';
import { initialValues, fieldValidations, convertPayload } from './schema';
import { textFields, selectFields } from 'constants/questionnaire';
import QuestionnaireWrapper, { styles } from '../QuestionnaireWrapper';
import { NavProps } from '../QuestionnaireWrapper/types';
const Questionnaire1: FC = () => (
const Questionnaire1: FC<NavProps> = (navProps) => (
<QuestionnaireWrapper
{...navProps}
Questionnaire={({ getTextInputProps, getFormFieldProps }) => (
<View>
<Datepicker
......@@ -48,6 +50,7 @@ const Questionnaire1: FC = () => (
)}
initialValues={initialValues}
fieldValidations={fieldValidations}
convertPayload={convertPayload}
/>
);
......
import { FieldValidation, FieldType } from 'utils/form';
import { textFields, selectFields } from 'constants/questionnaire';
import { dateToString } from 'utils/format';
export const initialValues = {
date_of_birth: new Date(),
......@@ -13,6 +14,7 @@ export const initialValues = {
general_purpose: 0,
dietary_change: 0,
has_weigher: 0,
step: 2,
};
export const fieldValidations: FieldValidation[] = [
......@@ -29,3 +31,9 @@ export const fieldValidations: FieldValidation[] = [
type: FieldType.RADIO_BUTTON,
})),
];
export const convertPayload = (values: typeof initialValues) => ({
...values,
date_of_birth: dateToString(values.date_of_birth),
waist_size: parseInt(values.waist_size, 10),
});
......@@ -5,9 +5,11 @@ import { RadioButtonGroup, TextField, Picker } from 'components/form';
import { initialValues, fieldValidations } from './schema';
import { textFields, selectFields } from 'constants/questionnaire';
import QuestionnaireWrapper, { styles } from '../QuestionnaireWrapper';
import { NavProps } from '../QuestionnaireWrapper/types';
const Questionnaire2: FC = () => (
const Questionnaire2: FC<NavProps> = (navProps) => (
<QuestionnaireWrapper
{...navProps}
Questionnaire={({ getTextInputProps, getFormFieldProps }) => (
<View>
{selectFields.eatingPattern.map((props, i) => {
......
......@@ -15,6 +15,7 @@ export const initialValues = {
preferred_food_taste: '',
expected_food_on_breakfast: '',
expected_food_on_lunch_dinner: '',
step: 3,
};
export const fieldValidations: FieldValidation[] = [
......
......@@ -9,9 +9,11 @@ import { TextField } from 'components/form';
import { initialValues, answerFormat } from './schema';
import { textFields } from 'constants/questionnaire';
import QuestionnaireWrapper, { styles } from '../QuestionnaireWrapper';
import { NavProps } from '../QuestionnaireWrapper/types';
const Questionnaire3: FC = () => (
const Questionnaire3: FC<NavProps> = (navProps) => (
<QuestionnaireWrapper
{...navProps}
Questionnaire={({ getTextInputProps }) => (
<View>
<Text style={[typographyStyles.headingMedium, styles.spacing]}>
......
......@@ -12,4 +12,5 @@ export const initialValues = {
evening_snack_explanation: '',
dinner_meal_explanation: '',
night_snack_explanation: '',
step: 4,
};
......@@ -4,11 +4,13 @@ import { View } from 'react-native';
import { TextField, CheckboxGroup } from 'components/form';
import { textFields, selectFields } from 'constants/questionnaire';
import { initialValues } from './schema';
import { initialValues, convertPayload } from './schema';
import QuestionnaireWrapper, { styles } from '../QuestionnaireWrapper';
import { NavProps } from '../QuestionnaireWrapper/types';
const Questionnaire4: FC = () => (
const Questionnaire4: FC<NavProps> = (navProps) => (
<QuestionnaireWrapper
{...navProps}
Questionnaire={({ getTextInputProps, getFormFieldProps }) => (
<View>
{selectFields.lifestyle.map((props, i) => {
......@@ -17,6 +19,7 @@ const Questionnaire4: FC = () => (
const textFieldProps = getTextInputProps(`other_${props.name}`);
otherProps = {
hasOtherChoice: true,
otherChoiceValue: props.otherChoiceValue,
otherValue: textFieldProps.value,
setOtherValue: textFieldProps.onChangeText,
};
......@@ -27,7 +30,7 @@ const Questionnaire4: FC = () => (
{...props}
choices={props.choiceList.map((label, id) => ({
label,
value: props.hasOtherChoice ? label : id + 1,
value: id + 1,
}))}
{...getFormFieldProps(props.name)}
{...otherProps}
......@@ -46,6 +49,7 @@ const Questionnaire4: FC = () => (
</View>
)}
initialValues={initialValues}
convertPayload={convertPayload}
/>
);
......
import { filterArr } from 'utils/form';
export const initialValues = {
meal_provider: [],
cigarette_alcohol_condition: [],
......@@ -7,4 +9,13 @@ export const initialValues = {
diet_drinks: '',
multivitamin_tablet_suplement: '',
diet_and_life_style_story: '',
step: 5,
};
export const convertPayload = (values: typeof initialValues) => ({
...values,
cigarette_alcohol_condition:
values.cigarette_alcohol_condition.length === 0
? [4]
: filterArr(values.cigarette_alcohol_condition, 4),
});
......@@ -4,11 +4,13 @@ import { View } from 'react-native';
import { TextField, CheckboxGroup } from 'components/form';
import { textFields, selectFields } from 'constants/questionnaire';
import { initialValues } from './schema';
import { initialValues, convertPayload } from './schema';
import QuestionnaireWrapper, { styles } from '../QuestionnaireWrapper';
import { NavProps } from '../QuestionnaireWrapper/types';
const Questionnaire5: FC = () => (
const Questionnaire5: FC<NavProps> = (navProps) => (
<QuestionnaireWrapper
{...navProps}
Questionnaire={({ getTextInputProps, getFormFieldProps }) => (
<View>
{selectFields.healthCondition.map((props, i) => (
......@@ -17,7 +19,7 @@ const Questionnaire5: FC = () => (
{...props}
choices={props.choiceList.map((label, id) => ({
label,
value: id + 1,
value: id + 2,
}))}
{...getFormFieldProps(props.name)}
/>
......@@ -34,6 +36,7 @@ const Questionnaire5: FC = () => (
</View>
)}
initialValues={initialValues}
convertPayload={convertPayload}
/>
);
......
import { filterArr } from 'utils/form';
export const initialValues = {
disease: [],
complaint: [],
......@@ -6,4 +8,12 @@ export const initialValues = {
motivation_using_dietela: '',
dietela_nutritionist_expectation: '',
dietela_program_expectation: '',
step: 6,
};
export const convertPayload = (values: typeof initialValues) => ({
...values,
disease: values.disease.length === 0 ? [1] : filterArr(values.disease, 1),
complaint:
values.complaint.length === 0 ? [1] : filterArr(values.complaint, 1),
});
......@@ -2,42 +2,82 @@ import React, { FC } from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { BigButton } from 'components/core';
import { BigButton, Toast } from 'components/core';
import * as ROUTES from 'constants/routes';
import { useForm } from 'hooks';
import { layoutStyles } from 'styles';
import { generateValidationSchema } from 'utils/form';
import { Props } from './types';
import { updateDietQuestionnaireApi } from 'services/dietQuestionnaire';
const QuestionnaireWrapper: FC<Props> = ({
Questionnaire,
initialValues,
fieldValidations = [],
convertPayload,
route,
}) => {
const navigation = useNavigation();
const dietQuestionnaireResponse = route.params;
const getInitialValues = () => {
let defaultValues: typeof initialValues = {};
Object.entries(initialValues).forEach(([k, value]) => {
const key = k as keyof typeof initialValues;
defaultValues[key] = dietQuestionnaireResponse[key] || value;
});
return defaultValues;
};
const {
getTextInputProps,
getFormFieldProps,
handleSubmit,
isSubmitting,
values: formValues,
} = useForm({
initialValues,
initialValues: getInitialValues(),
validationSchema: generateValidationSchema(fieldValidations),
onSubmit: async (values) => {
console.log(values);
navigation.navigate(ROUTES.extendedQuestionnaire);
const payload = convertPayload ? convertPayload(values) : values;
const response = await updateDietQuestionnaireApi(
dietQuestionnaireResponse.id,
payload,
);
if (response.success && response.data) {
navigation.reset({
index: 0,
routes: [{ name: ROUTES.extendedQuestionnaire }],
});
} else {
Toast.show({
type: 'error',
text1: 'Gagal menyimpan data',
text2: 'Terjadi kesalahan pada sisi kami. Silakan coba lagi',
});
}
},
});
const isValid =
formValues.step === 1
? formValues.agree_to_all_statements_consent === 1 &&
formValues.personal_data_consent === 1
: true;
return (
<ScrollView contentContainerStyle={layoutStyles}>
<Questionnaire
getTextInputProps={getTextInputProps}
getFormFieldProps={getFormFieldProps}
/>
<BigButton title="lanjut" onPress={handleSubmit} loading={isSubmitting} />
<BigButton
title="simpan"
onPress={handleSubmit}
loading={isSubmitting}
disabled={!isValid}
/>
</ScrollView>
);
};
......
import { FC } from 'react';
import { FieldValidation } from 'utils/form';
import {
DietQuestionnaireRequest,
DietQuestionnaireResponse,
} from 'services/dietQuestionnaire/models';
export interface QuestionnaireProps {
getTextInputProps: (_: string) => any;
getFormFieldProps: (_: string) => any;
}
export interface Props {
export interface NavProps {
route: {
params: DietQuestionnaireResponse;
};
}
export interface Props extends NavProps {
Questionnaire: FC<QuestionnaireProps>;
initialValues: { [_: string]: any };
initialValues: { [key in keyof DietQuestionnaireRequest]: any };
fieldValidations?: FieldValidation[];
convertPayload?: (_: any) => DietQuestionnaireRequest;
}