Fakultas Ilmu Komputer UI

Commit 9fd365e4 authored by Kefas Satrio Bangkit Solidedantyo's avatar Kefas Satrio Bangkit Solidedantyo
Browse files

Merge branch 'PBI-9-read-diet-profile' into 'staging'

read-only diet profile

See merge request !49
parents 94bfdc25 1838bdd9
Pipeline #76531 passed with stages
in 51 minutes and 17 seconds
...@@ -2,6 +2,7 @@ import { ...@@ -2,6 +2,7 @@ import {
DietQuestionnaireResponse, DietQuestionnaireResponse,
DietQuestionnaireRequest, DietQuestionnaireRequest,
} from 'services/dietQuestionnaire/models'; } from 'services/dietQuestionnaire/models';
import { authResponse } from './auth';
const validFormValues: DietQuestionnaireRequest = { const validFormValues: DietQuestionnaireRequest = {
agree_to_all_statements_consent: 1, agree_to_all_statements_consent: 1,
...@@ -57,5 +58,5 @@ export const mockDietQuestionnaire: DietQuestionnaireResponse = { ...@@ -57,5 +58,5 @@ export const mockDietQuestionnaire: DietQuestionnaireResponse = {
id: 7, id: 7,
...validFormValues, ...validFormValues,
finished_steps: [], finished_steps: [],
user: 30, user: authResponse.user,
}; };
...@@ -3,6 +3,7 @@ import { ...@@ -3,6 +3,7 @@ import {
publicNavigation, publicNavigation,
unpaidClientNavigation, unpaidClientNavigation,
clientNavigation, clientNavigation,
nutritionistNavigation,
} from 'constants/navigation'; } from 'constants/navigation';
import { UserRole, AuthUserResponse } from 'services/auth/models'; import { UserRole, AuthUserResponse } from 'services/auth/models';
import { TransactionStatus } from 'services/payment/models'; import { TransactionStatus } from 'services/payment/models';
...@@ -26,6 +27,12 @@ export const getNavigation = ( ...@@ -26,6 +27,12 @@ export const getNavigation = (
navigation: clientNavigation, navigation: clientNavigation,
}; };
} }
if (user.role === UserRole.NUTRITIONIST) {
return {
initialRoute: ROUTES.clientListForNutritionist,
navigation: nutritionistNavigation,
};
}
} }
return { return {
initialRoute: ROUTES.initial, initialRoute: ROUTES.initial,
......
...@@ -23,7 +23,9 @@ import { ...@@ -23,7 +23,9 @@ import {
// Private // Private
Checkout, Checkout,
PaymentResult, PaymentResult,
ReadOnlyDietProfile,
ClientListNutritionist, ClientListNutritionist,
ComingSoonPage,
ClientProfile, ClientProfile,
ClientProfileForAdmin, ClientProfileForAdmin,
} from 'scenes'; } from 'scenes';
...@@ -63,29 +65,6 @@ const navigation: NavRoute[] = [ ...@@ -63,29 +65,6 @@ const navigation: NavRoute[] = [
}, },
]; ];
export const nutritionistNavigation: NavRoute[] = [
{
name: ROUTES.clientListNutritionist,
component: ClientListNutritionist,
header: 'Client List',
},
{
name: ROUTES.clientChatNutritionist,
component: ClientListNutritionist, //TODO next sprint
header: 'Chat Client',
},
{
name: ROUTES.clientDietReportNutritionist,
component: ClientListNutritionist, //TODO next sprint
header: "Client's Diet Report",
},
{
name: ROUTES.clientProfileNutritionist,
component: ClientListNutritionist, //TODO next sprint
header: "Client's Profile",
},
];
export const publicNavigation: NavRoute[] = [ export const publicNavigation: NavRoute[] = [
{ {
name: ROUTES.initial, name: ROUTES.initial,
...@@ -165,6 +144,29 @@ export const clientNavigation: NavRoute[] = [ ...@@ -165,6 +144,29 @@ export const clientNavigation: NavRoute[] = [
})), })),
]; ];
export const nutritionistNavigation: NavRoute[] = [
{
name: ROUTES.clientListForNutritionist,
component: ClientListNutritionist,
header: 'List Klien',
},
{
name: ROUTES.clientChatNutritionist,
component: ComingSoonPage,
header: 'Chat Klien',
},
{
name: ROUTES.clientDietReportNutritionist,
component: ComingSoonPage,
header: 'Laporan Klien',
},
{
name: ROUTES.clientProfileNutritionist,
component: ReadOnlyDietProfile,
header: 'Profil Klien',
},
];
export const adminNavigation: NavRoute[] = [ export const adminNavigation: NavRoute[] = [
{ {
name: ROUTES.clientProfile, name: ROUTES.clientProfile,
......
...@@ -10,6 +10,11 @@ export const pageHeaders: string[] = [ ...@@ -10,6 +10,11 @@ export const pageHeaders: string[] = [
'Kondisi Pribadi', 'Kondisi Pribadi',
]; ];
export const dateField: { label: string; name: string } = {
label: 'Tanggal Lahir',
name: 'date_of_birth',
};
export const textFields: { [_: string]: TextFieldSchema[] } = { export const textFields: { [_: string]: TextFieldSchema[] } = {
identity: [ identity: [
{ {
...@@ -158,7 +163,7 @@ export const textFields: { [_: string]: TextFieldSchema[] } = { ...@@ -158,7 +163,7 @@ export const textFields: { [_: string]: TextFieldSchema[] } = {
], ],
}; };
export const selectFields = { export const selectFields: { [_: string]: any[] } = {
identity: [ identity: [
{ {
name: 'profession', name: 'profession',
...@@ -301,7 +306,7 @@ export const selectFields = { ...@@ -301,7 +306,7 @@ export const selectFields = {
}, },
{ {
name: 'cigarette_alcohol_condition', name: 'cigarette_alcohol_condition',
label: 'Adakah Anda termasuk salah satu dibawah ini?', label: 'Apakah Anda merokok/minum minuman beralkohol?',
choiceList: [ choiceList: [
'Saat ini merokok', 'Saat ini merokok',
'Pernah merokok tetapi sudah berhenti', 'Pernah merokok tetapi sudah berhenti',
......
...@@ -25,7 +25,7 @@ const payment = 'payment'; ...@@ -25,7 +25,7 @@ const payment = 'payment';
export const paymentResult = `${payment}/result`; export const paymentResult = `${payment}/result`;
const nutritionist = 'nutritionist'; const nutritionist = 'nutritionist';
export const clientListNutritionist = `${nutritionist}/client-list`; export const clientListForNutritionist = `${nutritionist}/client-list`;
export const clientProfileNutritionist = `${nutritionist}/client-profile`; export const clientProfileNutritionist = `${nutritionist}/client-profile`;
export const clientDietReportNutritionist = `${nutritionist}/client-diet-report`; export const clientDietReportNutritionist = `${nutritionist}/client-diet-report`;
export const clientChatNutritionist = `${nutritionist}/client-chat`; export const clientChatNutritionist = `${nutritionist}/client-chat`;
...@@ -8,6 +8,7 @@ export { default as ComingSoonPage } from './common/ComingSoonPage'; ...@@ -8,6 +8,7 @@ export { default as ComingSoonPage } from './common/ComingSoonPage';
export { default as AllAccessQuestionnaire } from './questionnaire/AllAccessQuestionnaire'; export { default as AllAccessQuestionnaire } from './questionnaire/AllAccessQuestionnaire';
export { default as DietelaQuizResult } from './questionnaire/DietelaQuizResult'; export { default as DietelaQuizResult } from './questionnaire/DietelaQuizResult';
export { default as ExtendedQuestionnaire } from './questionnaire/ExtendedQuestionnaire'; export { default as ExtendedQuestionnaire } from './questionnaire/ExtendedQuestionnaire';
export { default as ReadOnlyDietProfile } from './questionnaire/ReadOnlyDietProfile';
export * from './questionnaire/ExtendedQuestionnaire/components'; export * from './questionnaire/ExtendedQuestionnaire/components';
export { default as Checkout } from './cart/Checkout'; export { default as Checkout } from './cart/Checkout';
......
...@@ -16,7 +16,7 @@ describe('ClientListNutritionist', () => { ...@@ -16,7 +16,7 @@ describe('ClientListNutritionist', () => {
data: [], data: [],
}), }),
); );
render(<ClientListNutritionist />, ROUTES.clientListNutritionist); render(<ClientListNutritionist />, ROUTES.clientListForNutritionist);
await waitFor(() => expect(mockAxios.request).toBeCalled()); await waitFor(() => expect(mockAxios.request).toBeCalled());
}); });
}); });
...@@ -20,21 +20,17 @@ const ClientListNutritionist: FC = () => { ...@@ -20,21 +20,17 @@ const ClientListNutritionist: FC = () => {
{clients.map((client, idx) => ( {clients.map((client, idx) => (
<ClientCardNutritionist <ClientCardNutritionist
key={idx} key={idx}
clientName={client.name} clientName={client.user.name}
onPressClientProfile={() => { onPressClientProfile={() => {
navigation.navigate(ROUTES.clientProfileNutritionist, { navigation.navigate(ROUTES.clientProfileNutritionist, {
id: client.id, id: client.diet_questionnaire_id,
}); });
}} }}
onPressClientDietReport={() => { onPressClientDietReport={() => {
navigation.navigate(ROUTES.clientDietReportNutritionist, { navigation.navigate(ROUTES.clientDietReportNutritionist, {});
id: client.id,
});
}} }}
onPressClientChat={() => { onPressClientChat={() => {
navigation.navigate(ROUTES.clientChatNutritionist, { navigation.navigate(ROUTES.clientChatNutritionist, {});
id: client.id,
});
}} }}
/> />
))} ))}
......
import React from 'react';
import { render } from 'utils/testing';
import * as ROUTES from 'constants/routes';
import { pages } from '../../pages';
import DietProfilePage from '.';
import { mockDietQuestionnaire } from '__mocks__/dietQuestionnaire';
describe('DietProfilePage', () => {
it('identity page renders correctly', () => {
render(
<DietProfilePage content={pages[0](mockDietQuestionnaire)} />,
ROUTES.clientProfileNutritionist,
);
});
it('eating pattern page renders correctly', () => {
render(
<DietProfilePage content={pages[1](mockDietQuestionnaire)} />,
ROUTES.clientProfileNutritionist,
);
});
it('daily food consumption page renders correctly', () => {
render(
<DietProfilePage content={pages[2](mockDietQuestionnaire)} />,
ROUTES.clientProfileNutritionist,
);
});
it('lifestyle page renders correctly', () => {
render(
<DietProfilePage content={pages[3](mockDietQuestionnaire)} />,
ROUTES.clientProfileNutritionist,
);
});
it('health condition page renders correctly', () => {
render(
<DietProfilePage content={pages[4](mockDietQuestionnaire)} />,
ROUTES.clientProfileNutritionist,
);
});
});
import { FC } from 'react';
import { Text, ScrollView } from 'react-native';
import { typography } from 'styles';
import { styles } from './styles';
import React from 'react';
import QuestionAnswerCard from '../QuestionAnswerCard';
import { DietProfilePageContent } from '../../pages/types';
const DietProfilePage: FC<{
content: DietProfilePageContent;
}> = ({ content }) => {
return (
<ScrollView style={styles.container}>
<Text style={[typography.headingLarge, styles.nameMargin]}>
{content.title}
</Text>
{content.questions.map((qst, idx) => (
<QuestionAnswerCard
key={idx}
question={qst.label}
answer={qst.answer}
/>
))}
</ScrollView>
);
};
export default DietProfilePage;
import { StyleSheet, Dimensions } from 'react-native';
export const styles = StyleSheet.create({
nameMargin: {
marginBottom: 15,
},
container: {
width: Dimensions.get('window').width - 40,
},
});
import React from 'react';
import { render } from 'utils/testing';
import * as ROUTES from 'constants/routes';
import QuestionAnswerCard from '.';
describe('QuestionAnswerCard', () => {
it('renders correctly', () => {
render(
<QuestionAnswerCard question="apa?" answer="yes" />,
ROUTES.clientProfileNutritionist,
);
});
it('renders correctly with testID', () => {
render(
<QuestionAnswerCard
testID="question answer"
question="apa?"
answer="yes"
/>,
ROUTES.clientProfileNutritionist,
);
});
});
import { FC } from 'react';
import { View, Text } from 'react-native';
import React from 'react';
import { InfoCard } from 'components/core';
import { typographyStyles } from 'styles';
import { styles } from './styles';
interface Props {
question: string;
answer: string;
testID?: string;
}
const QuestionAnswerCard: FC<Props> = ({ question, answer, testID }) => {
return (
<View style={styles.labelContainer} testID={testID}>
<Text style={[typographyStyles.bodyLarge, styles.label]}>{question}</Text>
<InfoCard content={answer} />
</View>
);
};
export default QuestionAnswerCard;
import { StyleSheet } from 'react-native';
export const styles = StyleSheet.create({
label: {
marginBottom: 5,
marginTop: 15,
},
labelContainer: {
alignSelf: 'stretch',
},
answer: {
flexDirection: 'row',
},
});
export { default as QuestionAnswerCard } from './QuestionAnswerCard';
export { default as DietProfilePage } from './DietProfilePage';
import React from 'react';
import { render } from 'utils/testing';
import * as ROUTES from 'constants/routes';
import ReadOnlyDietProfile from '.';
import { mockDietQuestionnaire } from '__mocks__/dietQuestionnaire';
describe('ReadOnlyDietProfile', () => {
it('renders correctly', () => {
render(<ReadOnlyDietProfile />, ROUTES.clientProfileNutritionist, {
routeParams: mockDietQuestionnaire,
});
});
});
import { FC, useState } from 'react';
import { Dimensions, ScrollView } from 'react-native';
import { styles } from './styles';
import { layoutStyles } from 'styles';
import { useRoute } from '@react-navigation/native';
import React from 'react';
import { DietProfilePage } from './components';
import { CarouselPagination, BigButton, Loader } from 'components/core';
import Carousel from 'react-native-snap-carousel';
import { pages } from './pages';
import { useApi } from 'hooks';
import { retrieveDietQuestionnaireByIdApi } from 'services/dietQuestionnaire';
import { DietQuestionnaireResponse } from 'services/dietQuestionnaire/models';
interface QuestionnaireID {
id: number;
}
const ReadOnlyDietProfile: FC = () => {
const [activeSlide, setActiveSlide] = useState(0);
const route = useRoute();
const { id } = route.params as QuestionnaireID;
const { isLoading, data } = useApi(() =>
retrieveDietQuestionnaireByIdApi(id),
);
if (isLoading) {
return <Loader />;
}
return (
<ScrollView contentContainerStyle={[layoutStyles, styles.container]}>
<Carousel
data={pages.map((page, idx) => (
<DietProfilePage
key={idx}
content={page(data as DietQuestionnaireResponse)}
/>
))}
renderItem={({ item }: any) => item}
sliderWidth={Dimensions.get('window').width}
itemWidth={Dimensions.get('window').width}
onSnapToItem={setActiveSlide}
/>
<CarouselPagination index={activeSlide} length={5} />
<BigButton
title="Berikan Rekomendasi"
onPress={() => {
// TODO: Doan
}}
/>
</ScrollView>
);
};
export default ReadOnlyDietProfile;
import { DietProfilePageContent, answerTypes } from '../types';
import { getQuestionAnswerString } from '../utils';
import { DietQuestionnaireResponse } from 'services/dietQuestionnaire/models';
import { pageHeaders } from 'constants/questionnaire';
export const dailyFoodConsumption = ({
breakfast_meal_explanation,
morning_snack_explanation,
lunch_meal_explanation,
evening_snack_explanation,
dinner_meal_explanation,
night_snack_explanation,
}: DietQuestionnaireResponse): DietProfilePageContent => {
const page = 'foodConsumption';
return {
title: pageHeaders[3],
questions: [
getQuestionAnswerString(
page,
'breakfast_meal_explanation',
breakfast_meal_explanation,
answerTypes.TEXT,
),
getQuestionAnswerString(
page,
'morning_snack_explanation',
morning_snack_explanation,
answerTypes.TEXT,
),
getQuestionAnswerString(
page,
'lunch_meal_explanation',
lunch_meal_explanation,
answerTypes.TEXT,
),
getQuestionAnswerString(
page,
'evening_snack_explanation',
evening_snack_explanation,
answerTypes.TEXT,
),
getQuestionAnswerString(
page,
'dinner_meal_explanation',
dinner_meal_explanation,
answerTypes.TEXT,
),
getQuestionAnswerString(
page,
'night_snack_explanation',
night_snack_explanation,
answerTypes.TEXT,
),
],
};
};
import { DietProfilePageContent, answerTypes } from '../types';
import { getQuestionAnswerString } from '../utils';
import { DietQuestionnaireResponse } from 'services/dietQuestionnaire/models';
import { pageHeaders } from 'constants/questionnaire';
export const eatingPattern = ({
breakfast_frequency,
breakfast_meal_type,
sweet_tea_consumption_frequency,
coffee_consumption_frequency,
milk_consumption_frequency,
other_drink_consumption_frequency,
additional_sugar_in_a_day,