Fakultas Ilmu Komputer UI

Commit 570704b6 authored by Kefas Satrio Bangkit Solidedantyo's avatar Kefas Satrio Bangkit Solidedantyo

Merge branch 'fix-update-nutritionist-comments' into 'staging'

Allow update for nutritionist comments

See merge request !86
parents 4cc5c8f8 8c52b868
Pipeline #84576 passed with stages
in 32 minutes and 31 seconds
......@@ -42,6 +42,8 @@ export const dietReportTextFields: { [_: string]: TextFieldSchema[] } = {
name: 'water_consumption',
required: true,
placeholder: 'ex: 8',
fieldType: FieldType.NUMBER,
max: 20,
keyboardType: 'numeric',
errorMessage: 'Konsumsi minuman',
},
......
......@@ -38,6 +38,8 @@ export const mockUserReportResponse: UserReportResponse = {
name: 'Shin Ryujin',
email: 'ryujin@itzy.com',
role: UserRole.CLIENT,
phone_number: null,
deadline: null,
},
...mockUserReportRequest,
};
......@@ -60,3 +62,28 @@ export const mockUserReportHistory = {
},
],
};
export const mockUserReportComment: { [_: string]: any } = {
weight: 'baik',
height: 'baik',
waist_size: 'baik',
changes_felt: 'baik',
hunger_level: 'baik',
fullness_level: 'baik',
heavy_meal: 'baik',
snacks: 'baik',
sweet_beverages: 'baik',
sugary_ingredients: 'baik',
fried_snacks: 'baik',
umami_snacks: 'baik',
sweet_snacks: 'baik',
fruits_portion: 'baik',
vegetables_portion: 'baik',
water_consumption: 'baik',
physical_activity: 'baik',
physical_activity_other: 'baik',
time_for_activity: 'baik',
feeling_rating: 'baik',
lesson_learned: 'baik',
problem_faced_and_feedbacks: 'baik',
};
export const validWeeklyReportCommentValues: { [_: string]: any } = {
weight: 'baik',
height: 'baik',
waist_size: 'baik',
changes_felt: 'baik',
hunger_level: 'baik',
fullness_level: 'baik',
heavy_meal: 'baik',
snacks: 'baik',
sweet_beverages: 'baik',
sugary_ingredients: 'baik',
fried_snacks: 'baik',
umami_snacks: 'baik',
sweet_snacks: 'baik',
fruits_portion: 'baik',
vegetables_portion: 'baik',
water_consumption: 'baik',
physical_activity: 'baik',
physical_activity_other: 'baik',
time_for_activity: 'baik',
feeling_rating: 'baik',
lesson_learned: 'baik',
problem_faced_and_feedbacks: 'baik',
};
......@@ -21,6 +21,7 @@ import {
import { setAuthHeader, resetAuthHeader } from 'services/api';
import { iUserContext } from './types';
import { TransactionStatus } from 'services/payment/models';
export const initialUser = {
id: null,
......@@ -88,27 +89,39 @@ export const useUserContext = (): iUserContext => {
setUser(data.user);
};
const linkUserData = async (email: string) => {
// For signup, link user to cart and diet profile
const linkUserData = async (data: LoginResponse) => {
const dietProfileId = await getCache(CACHE_KEYS.dietProfileId);
const cartId = await getCache(CACHE_KEYS.cartId);
if (dietProfileId && cartId) {
const response = await linkUserDataApi({
email,
email: data.user.email,
diet_profile_id: parseInt(dietProfileId, 10),
cart_id: parseInt(cartId, 10),
});
return response;
await authSuccess({
...data,
user: {
...data.user,
cart_id: response.data?.cart.id || null,
transaction_status: TransactionStatus.UNPAID,
},
});
} else {
await logout();
Toast.show({
type: 'error',
text1: 'Gagal registrasi akun',
text2: 'Terjadi kesalahan di sisi kami. Silakan coba lagi',
});
}
return {
success: false,
};
};
const signup = async (registerData: RegistrationRequest) => {
const response = await signupApi(registerData);
if (response.success && response.data) {
await linkUserData(response.data.user.email);
await authSuccess(response.data);
await linkUserData(response.data);
}
return response;
};
......@@ -130,20 +143,8 @@ export const useUserContext = (): iUserContext => {
access_token: tokens.accessToken,
});
if (response.success && response.data) {
// If signup, link user to cart and diet profile
if (!isLogin) {
const linkResponse = await linkUserData(response.data.user.email);
if (!linkResponse.success) {
await logout();
Toast.show({
type: 'error',
text1: 'Gagal registrasi dengan Google',
text2: 'Terjadi kesalahan di sisi kami. Silakan coba lagi',
});
}
}
await authSuccess(response.data);
const authProcess = isLogin ? authSuccess : linkUserData;
await authProcess(response.data);
} else {
await logout();
}
......
......@@ -2,9 +2,12 @@ import React from 'react';
import { render, waitFor, fireEvent } from 'utils/testing';
import DietReportForNutritionist from '.';
import { mockUserReportResponse } from 'mocks/userReport';
import axios from 'axios';
import {
mockUserReportResponse,
mockUserReportComment,
} from 'mocks/userReport';
import * as ROUTES from 'constants/routes';
import axios from 'axios';
jest.mock('react-native-toast-message');
jest.mock('axios');
......@@ -13,7 +16,26 @@ const mockAxios = axios as jest.Mocked<typeof axios>;
describe('DietReportForNutritionist', () => {
const data = mockUserReportResponse;
it('renders and submits correctly when given valid comments', async () => {
it('renders correctly when fetching previous comments', async () => {
mockAxios.request.mockImplementationOnce(() =>
Promise.resolve({
status: 200,
data: [mockUserReportComment],
}),
);
render(<DietReportForNutritionist />, ROUTES.clientDietReportNutritionist, {
routeParams: data,
});
await waitFor(() => expect(mockAxios.request).toBeCalled());
});
it('renders correctly when first creating valid comments', async () => {
mockAxios.request.mockImplementationOnce(() =>
Promise.resolve({
status: 200,
data: [],
}),
);
const { queryByText, getByText, getAllByPlaceholderText } = render(
<DietReportForNutritionist />,
ROUTES.clientDietReportNutritionist,
......@@ -21,6 +43,7 @@ describe('DietReportForNutritionist', () => {
routeParams: data,
},
);
await waitFor(() => expect(mockAxios.request).toBeCalled());
const textFieldsPage1 = getAllByPlaceholderText(/Tuliskan komentar.../i);
textFieldsPage1.forEach((field) => {
......@@ -57,10 +80,16 @@ describe('DietReportForNutritionist', () => {
const submitButton = getByText('Selesai');
await waitFor(() => fireEvent.press(submitButton));
expect(queryByText(/Daftar Klien/i)).toBeTruthy();
expect(queryByText(/Riwayat Laporan Diet Klien/i)).toBeTruthy();
});
it('renders and does not redirect when api fails', async () => {
it('renders correctly but does not redirect when api fails', async () => {
mockAxios.request.mockImplementationOnce(() =>
Promise.resolve({
status: 200,
data: [],
}),
);
const { queryByText, getByText, getAllByPlaceholderText } = render(
<DietReportForNutritionist />,
ROUTES.clientDietReportNutritionist,
......@@ -68,6 +97,7 @@ describe('DietReportForNutritionist', () => {
routeParams: data,
},
);
await waitFor(() => expect(mockAxios.request).toBeCalled());
const textFieldsPage1 = getAllByPlaceholderText(/Tuliskan komentar.../i);
textFieldsPage1.forEach((field) => {
......@@ -106,7 +136,7 @@ describe('DietReportForNutritionist', () => {
const submitButton = getByText('Selesai');
await waitFor(() => fireEvent.press(submitButton));
expect(queryByText(/Daftar Klien/i)).toBeFalsy();
expect(queryByText(/Riwayat Laporan Diet Klien/i)).toBeFalsy();
});
afterAll(() => {
......
......@@ -2,16 +2,21 @@ import React, { FC, useState } from 'react';
import { View, StyleSheet } from 'react-native';
import { layoutStyles } from 'styles';
import { pages } from './pages';
import { WizardContainer, Toast } from 'components/core';
import { useForm } from 'hooks';
import { WizardContainer, Toast, Loader } from 'components/core';
import { useForm, useApi } from 'hooks';
import { dietReportCommentInitialValues, fieldValidations } from './schema';
import { generateValidationSchema } from 'utils/form';
import { DietReportPage } from './components';
import { useRoute, useNavigation } from '@react-navigation/native';
import { createNutritionistCommentApi } from 'services/progress';
import {
createNutritionistCommentApi,
retrieveUserReportCommentByReportId,
updateNutritionistCommentApi,
} from 'services/progress';
import {
NutritionistCommentRequest,
UserReportResponse,
NutritionistComment,
} from 'services/progress/models';
import * as ROUTES from 'constants/routes';
import {
......@@ -19,17 +24,29 @@ import {
dietReportSelectFields,
} from 'constants/weeklyReport';
interface ParamsDietReport {
id: number;
}
const DietReportForNutritionist: FC = () => {
const navigation = useNavigation();
const route = useRoute();
const data = route.params as UserReportResponse;
const userReport = route.params as UserReportResponse;
const [activeSlide, setActiveSlide] = useState(1);
const { isLoading, data: commentResponse } = useApi(() =>
retrieveUserReportCommentByReportId(userReport.id),
);
const getInitialValues = () => {
let defaultValues = { ...dietReportCommentInitialValues };
if (commentResponse && commentResponse.length > 0) {
const data = commentResponse[0];
Object.entries(data).map(([field, comment]) => {
const key = field as keyof NutritionistComment;
defaultValues[key] = comment;
});
}
return defaultValues;
};
const {
getTextInputProps,
handleSubmit,
......@@ -37,21 +54,37 @@ const DietReportForNutritionist: FC = () => {
isFieldError,
values: formValues,
} = useForm({
initialValues: dietReportCommentInitialValues,
initialValues: getInitialValues(),
validationSchema: generateValidationSchema(fieldValidations),
enableReinitialize: true,
onSubmit: async (values) => {
const payload: NutritionistCommentRequest = {
weekly_report: data.id,
weekly_report: userReport.id,
...values,
};
const response = await createNutritionistCommentApi(payload);
const commentId =
commentResponse && commentResponse.length > 0
? commentResponse[0].id
: null;
const response = commentId
? await updateNutritionistCommentApi(payload, commentId)
: await createNutritionistCommentApi(payload);
if (response.success) {
Toast.show({
type: 'success',
text1: 'Sukses membuat komen',
text2: 'Komen anda terhadap laporan mingguan klien sudah tersimpan.',
});
navigation.navigate(ROUTES.clientListForNutritionist);
commentId
? Toast.show({
type: 'success',
text1: 'Sukses mengubah komen',
text2:
'Perubahan komen Anda terhadap laporan mingguan klien sudah tersimpan.',
})
: Toast.show({
type: 'success',
text1: 'Sukses membuat komen',
text2:
'Komen Anda terhadap laporan mingguan klien sudah tersimpan.',
});
navigation.navigate(ROUTES.weeklyReportChooseWeekForNutritionist);
} else {
Toast.show({
type: 'error',
......@@ -62,7 +95,9 @@ const DietReportForNutritionist: FC = () => {
},
});
const userReport = data;
if (isLoading) {
return <Loader />;
}
const isCurrentPageError = (): boolean => {
if (activeSlide === 1) {
......
......@@ -48,8 +48,7 @@ const statusBeratBadan = (response: DietProfileResponse): ResultPageContent => {
},
},
{
header:
'Untuk orang dengan tinggi 156 cm, berikut rentang berat badan yang ideal:',
header: `Untuk orang dengan tinggi ${height} cm, berikut rentang berat badan yang ideal:`,
content: {
statistics: [
[
......
......@@ -12,11 +12,13 @@ const physicalActivity = [
];
export const getPhysicalActivity = (activity: number[]) => {
return activity.map((act, i) =>
let res = '';
activity.map((act, i) =>
i === activity.length - 1
? `- ${physicalActivity[act - 1]}`
: `- ${physicalActivity[act - 1]}\n`,
? res.concat(`- ${physicalActivity[act - 1]}`)
: res.concat(`- ${physicalActivity[act - 1]}\n`),
);
return res;
};
const timeForActivity = [
......
......@@ -20,23 +20,27 @@ const ReadOnlyWeeklyReport: FC = () => {
retrieveUserReportCommentByReportId(data.id),
);
const pageComponents = [
<WeeklyReportPage questions={page1(data)} title="Laporan Diet Anda" />,
<WeeklyReportPage questions={page2(data)} />,
<WeeklyReportPage
questions={page3(data)}
title="Selama 1 minggu terakhir, berapa rata-rata Anda mengonsumsi jenis makanan dibawah ini selama seharian?"
/>,
<WeeklyReportPage questions={page4(data)} />,
];
if (isLoading) {
return <Loader />;
}
if (commentData.length === 0) {
if (!commentData || commentData.length === 0) {
return <EmptyDataPage text="Belum ada komentar dari nutrisionis" />;
}
const firstCommentData = commentData[0];
const pageComponents = [
<WeeklyReportPage
questions={page1(data, firstCommentData)}
title="Laporan Diet Anda"
/>,
<WeeklyReportPage questions={page2(data, firstCommentData)} />,
<WeeklyReportPage
questions={page3(data, firstCommentData)}
title="Selama 1 minggu terakhir, berapa rata-rata Anda mengonsumsi jenis makanan dibawah ini selama seharian?"
/>,
<WeeklyReportPage questions={page4(data, firstCommentData)} />,
];
return (
<>
......
import { UserReportResponse } from 'services/progress/models';
import {
UserReportResponse,
NutritionistCommentResponse,
} from 'services/progress/models';
import { QuestionComment } from '../types';
export const page1 = (reportData: UserReportResponse): QuestionComment[] => [
export const page1 = (
reportData: UserReportResponse,
commentData: NutritionistCommentResponse,
): QuestionComment[] => [
{
questions: [
{
......@@ -9,7 +15,7 @@ export const page1 = (reportData: UserReportResponse): QuestionComment[] => [
answer: `${reportData.weight}`,
},
],
comment: 'keren bingits',
comment: commentData.weight,
},
{
questions: [
......@@ -18,7 +24,7 @@ export const page1 = (reportData: UserReportResponse): QuestionComment[] => [
answer: `${reportData.height}`,
},
],
comment: 'pertahankan\nnak',
comment: commentData.height,
},
{
questions: [
......@@ -27,6 +33,6 @@ export const page1 = (reportData: UserReportResponse): QuestionComment[] => [
answer: `${reportData.waist_size}`,
},
],
comment: 'gils',
comment: commentData.waist_size,
},
];
import { UserReportResponse } from 'services/progress/models';
import {
UserReportResponse,
NutritionistCommentResponse,
} from 'services/progress/models';
import { QuestionComment } from '../types';
export const page2 = (reportData: UserReportResponse): QuestionComment[] => [
export const page2 = (
reportData: UserReportResponse,
commentData: NutritionistCommentResponse,
): QuestionComment[] => [
{
questions: [
{
......@@ -14,7 +20,7 @@ export const page2 = (reportData: UserReportResponse): QuestionComment[] => [
answer: `${reportData.changes_felt}`,
},
],
comment: 'keren bingits',
comment: commentData.changes_felt,
},
{
questions: [
......@@ -28,7 +34,7 @@ export const page2 = (reportData: UserReportResponse): QuestionComment[] => [
answer: `${reportData.hunger_level}`,
},
],
comment: 'keren bingits',
comment: commentData.hunger_level,
},
{
questions: [
......@@ -42,7 +48,7 @@ export const page2 = (reportData: UserReportResponse): QuestionComment[] => [
answer: `${reportData.fullness_level}`,
},
],
comment: 'keren bingits',
comment: commentData.fullness_level,
},
{
questions: [
......@@ -52,7 +58,7 @@ export const page2 = (reportData: UserReportResponse): QuestionComment[] => [
answer: `${reportData.heavy_meal}x/hari`,
},
],
comment: 'lere',
comment: commentData.heavy_meal,
},
{
questions: [
......@@ -62,7 +68,7 @@ export const page2 = (reportData: UserReportResponse): QuestionComment[] => [
answer: `${reportData.snacks}x/hari`,
},
],
comment: 'keren bingits',
comment: commentData.snacks,
},
{
questions: [
......@@ -72,6 +78,6 @@ export const page2 = (reportData: UserReportResponse): QuestionComment[] => [
answer: `${reportData.water_consumption}`,
},
],
comment: 'keren bingits',
comment: commentData.water_consumption,
},
];
import { UserReportResponse } from 'services/progress/models';
import {
UserReportResponse,
NutritionistCommentResponse,
} from 'services/progress/models';
import { QuestionComment } from '../types';
export const page3 = (responseData: UserReportResponse): QuestionComment[] => [
export const page3 = (
responseData: UserReportResponse,
commentData: NutritionistCommentResponse,
): QuestionComment[] => [
{
questions: [
{
......@@ -36,6 +42,6 @@ export const page3 = (responseData: UserReportResponse): QuestionComment[] => [
answer: `${responseData.vegetables_portion}`,
},
],
comment: 'nais',
comment: commentData.average_consumption,
},
];
import { UserReportResponse } from 'services/progress/models';
import {
UserReportResponse,
NutritionistCommentResponse,
} from 'services/progress/models';
import {
getFeeling,
getPhysicalActivity,
......@@ -6,7 +9,10 @@ import {
} from '../../components/WeeklyReportPage/utils';
import { QuestionComment } from '../types';
export const page4 = (responseData: UserReportResponse): QuestionComment[] => [
export const page4 = (
responseData: UserReportResponse,
commentData: NutritionistCommentResponse,
): QuestionComment[] => [
{
questions: [
{
......@@ -15,7 +21,7 @@ export const page4 = (responseData: UserReportResponse): QuestionComment[] => [
answer: getPhysicalActivity(responseData.physical_activity),
},
],
comment: 'keren bingits',
comment: commentData.physical_activity,
},
{
questions: [
......@@ -25,7 +31,7 @@ export const page4 = (responseData: UserReportResponse): QuestionComment[] => [
answer: getTimeForActivity(responseData.time_for_activity),
},
],
comment: 'keren bingits',
comment: commentData.time_for_activity,
},
{
questions: [
......@@ -35,7 +41,7 @@ export const page4 = (responseData: UserReportResponse): QuestionComment[] => [
answer: getFeeling(responseData.feeling_rating),
},
],
comment: 'keren bingits',
comment: commentData.feeling_rating,
},
{
questions: [
......@@ -45,7 +51,7 @@ export const page4 = (responseData: UserReportResponse): QuestionComment[] => [
answer: `${responseData.lesson_learned}`,
},
],
comment: 'keren bingits',
comment: commentData.lesson_learned,
},
{
questions: [
......@@ -55,6 +61,6 @@ export const page4 = (responseData: UserReportResponse): QuestionComment[] => [
answer: `${responseData.problem_faced_and_feedbacks}`,
},
],
comment: 'keren bingits',
comment: commentData.problem_faced_and_feedbacks,
},
];