Fakultas Ilmu Komputer UI

Commit b6c6e618 authored by Wulan Mantiri's avatar Wulan Mantiri
Browse files

Integrate client weekly report API and implement bottom navbar

parent f7e12547
import React from 'react';
import { render } from '@testing-library/react-native';
import Report4 from '.';
describe('Report4', () => {
it('renders correctly', () => {
render(
<Report4
getTextInputProps={() => ({
value: 'hello',
onChangeText: jest.fn(),
})}
getFormFieldProps={() => ({
value: [],
onChange: jest.fn(),
})}
/>,
);
});
});
import React, { FC } from 'react'; import React, { FC } from 'react';
import { ScrollView, StyleSheet, View } from 'react-native'; import { StyleSheet, View } from 'react-native';
import { TextField, CheckboxGroup, RadioButtonGroup } from 'components/form'; import { TextField, CheckboxGroup, RadioButtonGroup } from 'components/form';
import { import {
...@@ -8,15 +8,9 @@ import { ...@@ -8,15 +8,9 @@ import {
} from 'constants/weeklyReport'; } from 'constants/weeklyReport';
import { ReportProps } from '../../types'; import { ReportProps } from '../../types';
import { layoutStyles } from 'styles'; import { layoutStyles } from 'styles';
import { BigButton } from 'components/core';
const Report4: FC<ReportProps> = ({ const Report4: FC<ReportProps> = ({ getTextInputProps, getFormFieldProps }) => (
getTextInputProps, <View style={layoutStyles}>
getFormFieldProps,
handleSubmit,
isSubmitting,
}) => (
<ScrollView contentContainerStyle={layoutStyles}>
{dietReportSelectFields.dietReportPage4 {dietReportSelectFields.dietReportPage4
.filter((item) => item.checkbox) .filter((item) => item.checkbox)
.map((props, i) => { .map((props, i) => {
...@@ -60,8 +54,7 @@ const Report4: FC<ReportProps> = ({ ...@@ -60,8 +54,7 @@ const Report4: FC<ReportProps> = ({
key={`report4-textfield${i}`} key={`report4-textfield${i}`}
/> />
))} ))}
<BigButton title="simpan" onPress={handleSubmit} loading={isSubmitting} /> </View>
</ScrollView>
); );
export const styles = StyleSheet.create({ export const styles = StyleSheet.create({
......
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import axios from 'axios';
import WeeklyReport from '.';
import { dietReportTextFields } from 'constants/weeklyReport';
jest.mock('react-native-toast-message');
jest.mock('axios');
const mockAxios = axios as jest.Mocked<typeof axios>;
const mockedNavigate = jest.fn();
jest.mock('@react-navigation/native', () => {
return {
useNavigation: () => ({
reset: mockedNavigate,
}),
};
});
describe('WeeklyReport', () => {
const validFormValues: { [_: string]: any } = {
weight: '52',
height: '90',
waist_size: '190',
changes_felt: 1,
hunger_level: 1,
fullness_level: 1,
heavy_meal: 1,
snacks: 1,
sweet_beverages: 1,
sugary_ingredients: 1,
fried_snacks: 1,
umami_snacks: 1,
sweet_snacks: 1,
fruits_portion: 1,
vegetables_portion: 1,
water_consumption: '80',
physical_activity: [1],
physical_activity_other: '',
time_for_activity: 1,
feeling_rating: 1,
lesson_learned: 'hai',
problem_faced_and_feedbacks: 'feedback',
};
it('initially has disabled next button', () => {
const { getByText, queryByText } = render(<WeeklyReport />);
const weightTextField = queryByText(/Berat Badan/i);
expect(weightTextField).toBeTruthy();
const nextButton = getByText(/Lanjut/i);
expect(nextButton).toBeTruthy();
fireEvent.press(nextButton);
expect(queryByText(/Berat Badan/i)).toBeTruthy();
});
it('redirects to choose week for report if all form values are valid and submit success', async () => {
const createUserReportApi = () =>
Promise.resolve({
status: 201,
data: validFormValues,
});
mockAxios.request.mockImplementationOnce(createUserReportApi);
const { getByText, getByPlaceholderText } = render(<WeeklyReport />);
dietReportTextFields.dietReportPage1.forEach(({ name, placeholder }) => {
const formField = getByPlaceholderText(placeholder as string);
fireEvent.changeText(formField, validFormValues[name]);
});
fireEvent.press(getByText(/Lanjut/i));
// dietReportTextFields.dietReportPage2.forEach(({ name, placeholder }) => {
// const formField = getByPlaceholderText(placeholder as string);
// fireEvent.changeText(formField, validFormValues[name]);
// });
// dietReportSelectFields.dietReportPage2.forEach(
// ({ choiceList, scaleDescription, label }) => {
// if (scaleDescription) {
// const firstChoice = getByTestId(`${label}-1`);
// fireEvent.press(firstChoice);
// } else {
// const firstChoice = getByText(choiceList[0]);
// act(() => fireEvent.press(firstChoice));
// }
// },
// );
// fireEvent.press(getByText(/Lanjut/i));
// dietReportSelectFields.dietReportPage3.forEach(({ label }) => {
// const firstChoice = getByTestId(`${label}-1`);
// act(() => fireEvent.press(firstChoice));
// });
// fireEvent.press(getByText(/Lanjut/i));
// dietReportSelectFields.dietReportPage4.forEach(({ choiceList }) => {
// const firstChoice = getByText(choiceList[0]);
// fireEvent.press(firstChoice);
// });
// dietReportTextFields.dietReportPage4.forEach(({ name, placeholder }) => {
// const formField = getByPlaceholderText(placeholder as string);
// fireEvent.changeText(formField, validFormValues[name]);
// });
// fireEvent.press(getByText(/Lanjut/i));
// const submitButton = getByText('Selesai');
// await waitFor(() => fireEvent.press(submitButton));
// expect(mockedNavigate).toHaveBeenCalledTimes(1);
});
// it('does not redirect to choose week for report if all form values are valid but submit fails', async () => {
// const createUserReportApi = () =>
// Promise.reject({
// status: 400,
// response: {
// data: 'error',
// },
// });
// mockAxios.request.mockImplementationOnce(createUserReportApi);
// const { getByText, getByPlaceholderText, getByTestId } = render(
// <WeeklyReport />,
// );
// dietReportTextFields.dietReportPage1.forEach(({ name, placeholder }) => {
// const formField = getByPlaceholderText(placeholder as string);
// fireEvent.changeText(formField, validFormValues[name]);
// });
// fireEvent.press(getByText(/Lanjut/i));
// dietReportSelectFields.dietReportPage2.forEach(
// ({ choiceList, scaleDescription, label }) => {
// if (scaleDescription) {
// const firstChoice = getByTestId(`${label}-1`);
// fireEvent.press(firstChoice);
// } else {
// const firstChoice = getByText(choiceList[0]);
// fireEvent.press(firstChoice);
// }
// },
// );
// dietReportTextFields.dietReportPage2.forEach(({ name, placeholder }) => {
// const formField = getByPlaceholderText(placeholder as string);
// fireEvent.changeText(formField, validFormValues[name]);
// });
// fireEvent.press(getByText(/Lanjut/i));
// dietReportSelectFields.dietReportPage3.forEach(({ label }) => {
// const firstChoice = getByTestId(`${label}-1`);
// fireEvent.press(firstChoice);
// });
// fireEvent.press(getByText(/Lanjut/i));
// dietReportSelectFields.dietReportPage4.forEach(({ choiceList }) => {
// const firstChoice = getByText(choiceList[0]);
// fireEvent.press(firstChoice);
// });
// dietReportTextFields.dietReportPage4.forEach(({ name, placeholder }) => {
// const formField = getByPlaceholderText(placeholder as string);
// fireEvent.changeText(formField, validFormValues[name]);
// });
// fireEvent.press(getByText(/Lanjut/i));
// const submitButton = getByText('Selesai');
// await waitFor(() => fireEvent.press(submitButton));
// expect(mockedNavigate).toHaveBeenCalledTimes(1);
// });
afterAll(() => {
jest.clearAllMocks();
});
});
import React, { FC, useState } from 'react';
import { useNavigation } from '@react-navigation/native';
import * as ROUTES from 'constants/routes';
import { WizardContainer, Toast } from 'components/core';
import {
dietReportTextFields,
dietReportSelectFields,
} from 'constants/weeklyReport';
import { useForm } from 'hooks';
import { generateValidationSchema } from 'utils/form';
import { createUserReportApi } from 'services/progress';
import { initialValues, fieldValidations, convertPayload } from './schema';
import { pages } from './components';
const WeeklyReport: FC = () => {
const [currentPage, setCurrentPage] = useState(1);
const navigation = useNavigation();
const {
getTextInputProps,
getFormFieldProps,
isFormUntouched,
isFieldError,
handleSubmit,
isSubmitting,
} = useForm({
initialValues,
validationSchema: generateValidationSchema(fieldValidations),
onSubmit: async (values) => {
const response = await createUserReportApi(convertPayload(values));
if (response.success && response.data) {
navigation.reset({
index: 0,
routes: [{ name: ROUTES.weeklyReportChooseWeek }],
});
} else {
Toast.show({
type: 'error',
text1: 'Gagal menyimpan data',
text2: 'Terjadi kesalahan pada sisi kami. Silakan coba lagi',
});
}
},
});
const isCurrentPageError = (): boolean => {
const fields = [
...(dietReportTextFields[`dietReportPage${currentPage}`] || []),
...(dietReportSelectFields[`dietReportPage${currentPage}`] || []),
];
return (
isFormUntouched() ||
fields.reduce(
(acc: boolean, item) => acc || isFieldError(item.name),
false,
)
);
};
return (
<WizardContainer
currentStep={currentPage}
setCurrentStep={setCurrentPage}
onFinish={handleSubmit}
isLoading={isSubmitting}
isNextDisabled={isCurrentPageError()}
components={pages.map((Component) => (
<Component
getFormFieldProps={getFormFieldProps}
getTextInputProps={getTextInputProps}
/>
))}
/>
);
};
export default WeeklyReport;
...@@ -3,6 +3,7 @@ import { ...@@ -3,6 +3,7 @@ import {
dietReportTextFields, dietReportTextFields,
dietReportSelectFields, dietReportSelectFields,
} from 'constants/weeklyReport'; } from 'constants/weeklyReport';
import { UserReportRequest } from 'services/progress/models';
export const initialValues = { export const initialValues = {
weight: '', weight: '',
...@@ -14,6 +15,7 @@ export const initialValues = { ...@@ -14,6 +15,7 @@ export const initialValues = {
heavy_meal: 0, heavy_meal: 0,
snacks: 0, snacks: 0,
sweet_beverages: 0, sweet_beverages: 0,
sugary_ingredients: 0,
fried_snacks: 0, fried_snacks: 0,
umami_snacks: 0, umami_snacks: 0,
sweet_snacks: 0, sweet_snacks: 0,
...@@ -47,3 +49,13 @@ export const fieldValidations: FieldValidation[] = [ ...@@ -47,3 +49,13 @@ export const fieldValidations: FieldValidation[] = [
type: field.checkbox ? FieldType.CHECKBOX : FieldType.RADIO_BUTTON, type: field.checkbox ? FieldType.CHECKBOX : FieldType.RADIO_BUTTON,
})), })),
]; ];
export const convertPayload = (
values: typeof initialValues,
): UserReportRequest => ({
...values,
weight: parseInt(values.weight, 10),
height: parseInt(values.height, 10),
waist_size: parseInt(values.waist_size, 10),
water_consumption: parseInt(values.water_consumption, 10),
});
export interface ReportProps { export interface ReportProps {
getTextInputProps: (_: string) => any; getTextInputProps: (_: string) => any;
getFormFieldProps: (_: string) => any; getFormFieldProps: (_: string) => any;
isSubmitting: boolean;
handleSubmit: () => void;
} }
...@@ -32,7 +32,7 @@ export const updateCartApi = ( ...@@ -32,7 +32,7 @@ export const updateCartApi = (
}; };
export const payWithMidtransApi = ( export const payWithMidtransApi = (
cartId?: number, cartId?: number | null,
): ApiResponse<MidtransResponse> => { ): ApiResponse<MidtransResponse> => {
if (cartId) { if (cartId) {
return api(RequestMethod.POST, apiUrls.midtrans, { cart_id: cartId }); return api(RequestMethod.POST, apiUrls.midtrans, { cart_id: cartId });
......
import { api, RequestMethod, ApiResponse } from '../api'; import { api, RequestMethod, ApiResponse } from '../api';
import * as apiUrls from './urls'; import * as apiUrls from './urls';
import { UserReportResponse } from './models'; import { UserReportResponse, UserReportRequest } from './models';
export const retrieveUserReportById = ( export const retrieveUserReportById = (
id: number, clientId: number,
): ApiResponse<UserReportResponse[]> => { ): ApiResponse<UserReportResponse[]> => {
return api(RequestMethod.GET, apiUrls.userReportById(id)); return api(RequestMethod.GET, apiUrls.userReportByClientId(clientId));
};
export const createUserReportApi = (
payload: UserReportRequest,
): ApiResponse<UserReportResponse> => {
return api(RequestMethod.POST, apiUrls.userReport, payload);
}; };
export const progress = 'progress/'; export const progress = 'progress/';
export const userReportById = (id: number) => `${progress}${id}`;
export const userReport = `${progress}user_report/`;
export const userReportByClientId = (id: number) =>
`${userReport}?client_id=${id}`;
...@@ -1170,6 +1170,14 @@ ...@@ -1170,6 +1170,14 @@
resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-1.15.0.tgz#f571a8b013e001a840db905b21ec18e9d205f01b" resolved "https://registry.yarnpkg.com/@react-native-picker/picker/-/picker-1.15.0.tgz#f571a8b013e001a840db905b21ec18e9d205f01b"
integrity sha512-TYdhavldt3ami860fVgYv2mf0d1u5YhMQ6kTw+soX/a1rYbpRCRjdz01sEQXwCW8oL7/lzHakOFnwqCo6JazDw== integrity sha512-TYdhavldt3ami860fVgYv2mf0d1u5YhMQ6kTw+soX/a1rYbpRCRjdz01sEQXwCW8oL7/lzHakOFnwqCo6JazDw==
"@react-navigation/bottom-tabs@^5.11.11":
version "5.11.11"
resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-5.11.11.tgz#ad4dfee4316522d8c05b5a8ad460f597bddb9e3c"
integrity sha512-hThj6Vfw+ITzAVj5TgLEoxkVEcBD+gYeieWOe6FryBRgokgKNCzFQzqArJ5UCmNMxklNH0rstJfcdyHflLuPtw==
dependencies:
color "^3.1.3"
react-native-iphone-x-helper "^1.3.0"
"@react-navigation/core@^5.15.2": "@react-navigation/core@^5.15.2":
version "5.15.2" version "5.15.2"
resolved "https://registry.npmjs.org/@react-navigation/core/-/core-5.15.2.tgz" resolved "https://registry.npmjs.org/@react-navigation/core/-/core-5.15.2.tgz"
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment