Fakultas Ilmu Komputer UI

Commit 1838bdd9 authored by Kefas Satrio Bangkit Solidedantyo's avatar Kefas Satrio Bangkit Solidedantyo
Browse files

read-only diet profile

parent 94bfdc25
......@@ -2,6 +2,7 @@ import {
DietQuestionnaireResponse,
DietQuestionnaireRequest,
} from 'services/dietQuestionnaire/models';
import { authResponse } from './auth';
const validFormValues: DietQuestionnaireRequest = {
agree_to_all_statements_consent: 1,
......@@ -57,5 +58,5 @@ export const mockDietQuestionnaire: DietQuestionnaireResponse = {
id: 7,
...validFormValues,
finished_steps: [],
user: 30,
user: authResponse.user,
};
......@@ -3,6 +3,7 @@ import {
publicNavigation,
unpaidClientNavigation,
clientNavigation,
nutritionistNavigation,
} from 'constants/navigation';
import { UserRole, AuthUserResponse } from 'services/auth/models';
import { TransactionStatus } from 'services/payment/models';
......@@ -26,6 +27,12 @@ export const getNavigation = (
navigation: clientNavigation,
};
}
if (user.role === UserRole.NUTRITIONIST) {
return {
initialRoute: ROUTES.clientListForNutritionist,
navigation: nutritionistNavigation,
};
}
}
return {
initialRoute: ROUTES.initial,
......
......@@ -23,7 +23,9 @@ import {
// Private
Checkout,
PaymentResult,
ReadOnlyDietProfile,
ClientListNutritionist,
ComingSoonPage,
ClientProfile,
ClientProfileForAdmin,
} from 'scenes';
......@@ -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[] = [
{
name: ROUTES.initial,
......@@ -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[] = [
{
name: ROUTES.clientProfile,
......
......@@ -10,6 +10,11 @@ export const pageHeaders: string[] = [
'Kondisi Pribadi',
];
export const dateField: { label: string; name: string } = {
label: 'Tanggal Lahir',
name: 'date_of_birth',
};
export const textFields: { [_: string]: TextFieldSchema[] } = {
identity: [
{
......@@ -158,7 +163,7 @@ export const textFields: { [_: string]: TextFieldSchema[] } = {
],
};
export const selectFields = {
export const selectFields: { [_: string]: any[] } = {
identity: [
{
name: 'profession',
......@@ -301,7 +306,7 @@ export const selectFields = {
},
{
name: 'cigarette_alcohol_condition',
label: 'Adakah Anda termasuk salah satu dibawah ini?',
label: 'Apakah Anda merokok/minum minuman beralkohol?',
choiceList: [
'Saat ini merokok',
'Pernah merokok tetapi sudah berhenti',
......
......@@ -25,7 +25,7 @@ const payment = 'payment';
export const paymentResult = `${payment}/result`;
const nutritionist = 'nutritionist';
export const clientListNutritionist = `${nutritionist}/client-list`;
export const clientListForNutritionist = `${nutritionist}/client-list`;
export const clientProfileNutritionist = `${nutritionist}/client-profile`;
export const clientDietReportNutritionist = `${nutritionist}/client-diet-report`;
export const clientChatNutritionist = `${nutritionist}/client-chat`;
......@@ -8,6 +8,7 @@ export { default as ComingSoonPage } from './common/ComingSoonPage';
export { default as AllAccessQuestionnaire } from './questionnaire/AllAccessQuestionnaire';
export { default as DietelaQuizResult } from './questionnaire/DietelaQuizResult';
export { default as ExtendedQuestionnaire } from './questionnaire/ExtendedQuestionnaire';
export { default as ReadOnlyDietProfile } from './questionnaire/ReadOnlyDietProfile';
export * from './questionnaire/ExtendedQuestionnaire/components';
export { default as Checkout } from './cart/Checkout';
......
......@@ -16,7 +16,7 @@ describe('ClientListNutritionist', () => {
data: [],
}),
);
render(<ClientListNutritionist />, ROUTES.clientListNutritionist);
render(<ClientListNutritionist />, ROUTES.clientListForNutritionist);
await waitFor(() => expect(mockAxios.request).toBeCalled());
});
});
......@@ -20,21 +20,17 @@ const ClientListNutritionist: FC = () => {
{clients.map((client, idx) => (
<ClientCardNutritionist
key={idx}
clientName={client.name}
clientName={client.user.name}
onPressClientProfile={() => {
navigation.navigate(ROUTES.clientProfileNutritionist, {
id: client.id,
id: client.diet_questionnaire_id,
});
}}
onPressClientDietReport={() => {
navigation.navigate(ROUTES.clientDietReportNutritionist, {
id: client.id,
});
navigation.navigate(ROUTES.clientDietReportNutritionist, {});
}}
onPressClientChat={() => {
navigation.navigate(ROUTES.clientChatNutritionist, {
id: client.id,
});
navigation.navigate(ROUTES.clientChatNutritionist, {});
}}
/>
))}
......
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,
liquid_consumption_frequency,
meal_consumed_almost_every_day,
unliked_food,
preferred_food_taste,
expected_food_on_breakfast,
expected_food_on_lunch_dinner,
}: DietQuestionnaireResponse): DietProfilePageContent => {
const page = 'eatingPattern';
return {
title: pageHeaders[2],
questions: [
getQuestionAnswerString(
page,
'breakfast_frequency',
breakfast_frequency,
answerTypes.SELECT,
),
getQuestionAnswerString(
page,
'breakfast_meal_type',
breakfast_meal_type,
answerTypes.SELECT,
),
getQuestionAnswerString(
page,
'sweet_tea_consumption_frequency',
sweet_tea_consumption_frequency,
answerTypes.SELECT,
),
getQuestionAnswerString(
page,
'coffee_consumption_frequency',
coffee_consumption_frequency,
answerTypes.SELECT,
),
getQuestionAnswerString(
page,
'milk_consumption_frequency',
milk_consumption_frequency,
answerTypes.SELECT,
),
getQuestionAnswerString(
page,
'other_drink_consumption_frequency',
other_drink_consumption_frequency,
answerTypes.SELECT,
),
getQuestionAnswerString(
page,
'additional_sugar_in_a_day',
additional_sugar_in_a_day,
answerTypes.SELECT,
),
getQuestionAnswerString(
page,
'liquid_consumption_frequency',
liquid_consumption_frequency,
answerTypes.SELECT,
),
getQuestionAnswerString(
page,
'meal_consumed_almost_every_day',
meal_consumed_almost_every_day,
answerTypes.TEXT,
),
getQuestionAnswerString(
page,
'unliked_food',
unliked_food,
answerTypes.TEXT,
),
getQuestionAnswerString(
page,
'preferred_food_taste',
preferred_food_taste,