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
...@@ -4,8 +4,6 @@ import { render } from '@testing-library/react-native'; ...@@ -4,8 +4,6 @@ import { render } from '@testing-library/react-native';
import { ErrorToast } from './styles'; import { ErrorToast } from './styles';
import App from '.'; import App from '.';
jest.useFakeTimers();
describe('Application', () => { describe('Application', () => {
it('renders correctly', () => { it('renders correctly', () => {
render(<App />); render(<App />);
......
...@@ -5,12 +5,11 @@ import { ThemeProvider } from 'react-native-elements'; ...@@ -5,12 +5,11 @@ import { ThemeProvider } from 'react-native-elements';
import Toast from 'react-native-toast-message'; import Toast from 'react-native-toast-message';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { DietelaCoverLoader } from 'components/core'; import { DietelaCoverLoader, LogoutButton } from 'components/core';
import ContextProvider, { UserContext } from 'provider'; import ContextProvider, { UserContext } from 'provider';
import { theme } from 'styles/theme'; import { theme } from 'styles/theme';
import { screenOptions, toastConfig } from './styles'; import { screenOptions, toastConfig } from './styles';
import LogoutButton from './LogoutButton';
import { getNavigation } from './schema'; import { getNavigation } from './schema';
dayjs.locale('id'); dayjs.locale('id');
......
import * as ROUTES from 'constants/routes'; import * as ROUTES from 'constants/routes';
import { import {
publicNavigation, publicNavigation,
clientNavigation,
nutritionistNavigation, nutritionistNavigation,
adminNavigation, adminNavigation,
onboardingClientNavigation,
unpaidClientNavigation, unpaidClientNavigation,
paidClientNavigation,
} 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';
...@@ -21,16 +20,12 @@ export const getNavigation = ( ...@@ -21,16 +20,12 @@ export const getNavigation = (
initialRoute: ROUTES.checkout, initialRoute: ROUTES.checkout,
navigation: unpaidClientNavigation, navigation: unpaidClientNavigation,
}; };
} else if (!user.is_finished_onboarding) { } else {
return { return {
initialRoute: ROUTES.extendedQuestionnaire, initialRoute: ROUTES.clientTabNavigation,
navigation: onboardingClientNavigation, navigation: paidClientNavigation,
}; };
} }
return {
initialRoute: ROUTES.clientProfile,
navigation: clientNavigation,
};
} }
if (user.role === UserRole.NUTRITIONIST) { if (user.role === UserRole.NUTRITIONIST) {
......
...@@ -6,8 +6,10 @@ import LogoutButton from '.'; ...@@ -6,8 +6,10 @@ import LogoutButton from '.';
describe('LogoutButton', () => { describe('LogoutButton', () => {
const userContextMock = { const userContextMock = {
user: {
id: 1,
},
isAuthenticated: true, isAuthenticated: true,
isUnpaidClient: true,
logout: jest.fn(), logout: jest.fn(),
}; };
......
...@@ -28,7 +28,6 @@ export const styles = StyleSheet.create({ ...@@ -28,7 +28,6 @@ export const styles = StyleSheet.create({
justifyContent: 'space-between', justifyContent: 'space-between',
backgroundColor: colors.primaryYellow, backgroundColor: colors.primaryYellow,
borderRadius: 20, borderRadius: 20,
paddingRight: 10,
}, },
finishButton: { finishButton: {
borderRadius: 30, borderRadius: 30,
......
...@@ -4,6 +4,7 @@ export { default as CarouselPagination } from './CarouselPagination'; ...@@ -4,6 +4,7 @@ export { default as CarouselPagination } from './CarouselPagination';
export { default as InfoCard } from './InfoCard'; export { default as InfoCard } from './InfoCard';
export { default as Link } from './Link'; export { default as Link } from './Link';
export { default as Loader, DietelaCoverLoader } from './Loader'; export { default as Loader, DietelaCoverLoader } from './Loader';
export { default as LogoutButton } from './LogoutButton';
export { default as ResultCard } from './ResultCard'; export { default as ResultCard } from './ResultCard';
export { default as Statistic } from './Statistic'; export { default as Statistic } from './Statistic';
export { default as Toast } from 'react-native-toast-message'; export { default as Toast } from 'react-native-toast-message';
......
...@@ -46,6 +46,7 @@ const LikertScale: FC<Props> = ({ ...@@ -46,6 +46,7 @@ const LikertScale: FC<Props> = ({
style={{ flex: 1 / choices.length }} style={{ flex: 1 / choices.length }}
key={`button${choice.value}`}> key={`button${choice.value}`}>
<RadioButton <RadioButton
testID={`${label}-${value}`}
key={choice.value} key={choice.value}
checked={value === choice.value} checked={value === choice.value}
onPress={() => onChange(choice.value)} onPress={() => onChange(choice.value)}
......
...@@ -22,7 +22,6 @@ describe('StepByStepForm component', () => { ...@@ -22,7 +22,6 @@ describe('StepByStepForm component', () => {
route: name, route: name,
})), })),
currentPage: 0, currentPage: 0,
finishRedirectRoute: 'finishroute',
}; };
it('only has "Isi" button when form is never filled', () => { it('only has "Isi" button when form is never filled', () => {
...@@ -50,7 +49,11 @@ describe('StepByStepForm component', () => { ...@@ -50,7 +49,11 @@ describe('StepByStepForm component', () => {
it('redirects to finish route when button "Selesai" is pressed', () => { it('redirects to finish route when button "Selesai" is pressed', () => {
const { getByText } = render( const { getByText } = render(
<StepByStepForm {...props} currentPage={pages.length} />, <StepByStepForm
{...props}
currentPage={pages.length}
finishRedirectRoute="finish"
/>,
); );
const finishButton = getByText(/Selesai/i); const finishButton = getByText(/Selesai/i);
expect(finishButton).toBeTruthy(); expect(finishButton).toBeTruthy();
......
...@@ -60,20 +60,22 @@ const StepByStepForm: FC<Props> = ({ ...@@ -60,20 +60,22 @@ const StepByStepForm: FC<Props> = ({
</View> </View>
))} ))}
</View> </View>
<View style={styles.bottomContainer}> {finishRedirectRoute ? (
<Button <View style={styles.bottomContainer}>
title="Selesai" <Button
onPress={() => title="Selesai"
navigation.reset({ onPress={() =>
index: 0, navigation.reset({
routes: [{ name: finishRedirectRoute }], index: 0,
}) routes: [{ name: finishRedirectRoute }],
} })
buttonStyle={styles.finishButton} }
titleStyle={styles.buttonTitle} buttonStyle={styles.finishButton}
disabled={currentPage !== pages.length} titleStyle={styles.buttonTitle}
/> disabled={currentPage !== pages.length}
</View> />
</View>
) : null}
</ScrollView> </ScrollView>
); );
}; };
......
...@@ -5,5 +5,5 @@ export interface Props { ...@@ -5,5 +5,5 @@ export interface Props {
}[]; }[];
currentPage: number; currentPage: number;
defaultValues?: any; defaultValues?: any;
finishRedirectRoute: string; finishRedirectRoute?: string;
} }
import * as ROUTES from 'constants/routes'; import * as ROUTES from 'constants/routes';
import { import {
// Extended Questionnaire
ExtendedQuestionnaire,
ConsentForm,
Questionnaire1,
Questionnaire2,
Questionnaire3,
Questionnaire4,
Questionnaire5,
// Public // Public
AllAccessQuestionnaire, AllAccessQuestionnaire,
ChoosePlan, ChoosePlan,
...@@ -26,13 +17,12 @@ import { ...@@ -26,13 +17,12 @@ import {
ReadOnlyDietProfile, ReadOnlyDietProfile,
ClientListNutritionist, ClientListNutritionist,
ComingSoonPage, ComingSoonPage,
ClientProfile,
ClientDietRecommendationForAdmin, ClientDietRecommendationForAdmin,
PaymentWebView, PaymentWebView,
ProfileDietRecommendation, ProfileDietRecommendation,
ClientListAdmin, ClientListAdmin,
LoginChoosePlan, LoginChoosePlan,
WeeklyReport, ClientNavigation,
} from 'scenes'; } from 'scenes';
import { FC } from 'react'; import { FC } from 'react';
import DietReportForNutritionist from 'scenes/nutritionist/DietReportForNutritionist'; import DietReportForNutritionist from 'scenes/nutritionist/DietReportForNutritionist';
...@@ -99,52 +89,6 @@ export const publicNavigation: NavRoute[] = [ ...@@ -99,52 +89,6 @@ export const publicNavigation: NavRoute[] = [
}, },
]; ];
const defaultClientNavigation: NavRoute[] = [
{
name: ROUTES.extendedQuestionnaire,
component: ExtendedQuestionnaire,
header: 'Profil Saya',
},
...[
{
component: ConsentForm,
header: 'Persetujuan',
},
{
component: Questionnaire1,
header: 'Identitas Diri',
},
{
component: Questionnaire2,
header: 'Pola Makan',
},
{
component: Questionnaire3,
header: 'Konsumsi Makan',
},
{
component: Questionnaire4,
header: 'Gaya Hidup',
},
{
component: Questionnaire5,
header: 'Kondisi Pribadi',
},
].map((nav, id) => ({
...nav,
name: ROUTES.extendedQuestionnaireById(id),
})),
];
export const onboardingClientNavigation: NavRoute[] = [
...defaultClientNavigation,
{
name: ROUTES.clientProfile,
component: ClientProfile,
header: 'Profil Saya',
},
];
export const unpaidClientNavigation: NavRoute[] = [ export const unpaidClientNavigation: NavRoute[] = [
{ {
name: ROUTES.checkout, name: ROUTES.checkout,
...@@ -162,25 +106,18 @@ export const unpaidClientNavigation: NavRoute[] = [ ...@@ -162,25 +106,18 @@ export const unpaidClientNavigation: NavRoute[] = [
}, },
...navigation, ...navigation,
{ {
name: ROUTES.clientProfile, name: ROUTES.clientTabNavigation,
component: ClientProfile, component: ClientNavigation,
header: 'Profil Saya', header: 'Dietela',
}, },
...defaultClientNavigation,
]; ];
export const clientNavigation: NavRoute[] = [ export const paidClientNavigation: NavRoute[] = [
{
name: ROUTES.clientProfile,
component: ClientProfile,
header: 'Profil Saya',
},
{ {
name: ROUTES.clientWeeklyReport, name: ROUTES.clientTabNavigation,
component: WeeklyReport, component: ClientNavigation,
header: 'Laporan Diet Saya', header: 'Dietela',
}, },
...defaultClientNavigation,
]; ];
export const nutritionistNavigation: NavRoute[] = [ export const nutritionistNavigation: NavRoute[] = [
...@@ -239,6 +176,7 @@ export const adminNavigation: NavRoute[] = [ ...@@ -239,6 +176,7 @@ export const adminNavigation: NavRoute[] = [
}, },
]; ];
// FOR TESTING PURPOSES
export const testNavigation: NavRoute[] = [ export const testNavigation: NavRoute[] = [
...unpaidClientNavigation, ...unpaidClientNavigation,
...nutritionistNavigation, ...nutritionistNavigation,
......
...@@ -23,8 +23,11 @@ export const payment = 'payment'; ...@@ -23,8 +23,11 @@ export const payment = 'payment';
export const paymentResult = `${payment}/result`; export const paymentResult = `${payment}/result`;
const client = 'client'; const client = 'client';
export const clientTabNavigation = `${client}/tab-navigation`;
export const clientProfile = `${client}/profile`; export const clientProfile = `${client}/profile`;
export const clientRecommendation = `${client}/recommendation`;
export const clientWeeklyReport = `${client}/report`; export const clientWeeklyReport = `${client}/report`;
export const clientChat = `${client}/chat`;
const nutritionist = 'nutritionist'; const nutritionist = 'nutritionist';
export const clientListForNutritionist = `${nutritionist}/client-list`; export const clientListForNutritionist = `${nutritionist}/client-list`;
...@@ -41,5 +44,6 @@ export const clientDietReportAdmin = `${admin}/client-diet-report`; ...@@ -41,5 +44,6 @@ export const clientDietReportAdmin = `${admin}/client-diet-report`;
export const clientChatAdmin = `${admin}/client-chat`; export const clientChatAdmin = `${admin}/client-chat`;
export const clientDietRecommendation = `${admin}/client-diet-recommendation`; export const clientDietRecommendation = `${admin}/client-diet-recommendation`;
const progress = 'progress'; const weeklyReport = 'weekly-report';
export const userReport = `${progress}/user-report`; export const weeklyReportForm = `${weeklyReport}/form`;
export const weeklyReportChooseWeek = `${weeklyReport}/choose-week`;
...@@ -10,21 +10,21 @@ export const dietReportTextFields: { [_: string]: TextFieldSchema[] } = { ...@@ -10,21 +10,21 @@ export const dietReportTextFields: { [_: string]: TextFieldSchema[] } = {
dietReportPage1: [ dietReportPage1: [
{ {
label: 'Berat Badan (kg)', label: 'Berat Badan (kg)',
placeholder: 'Masukkan yang terakhir diukur', placeholder: 'Masukkan berat badan yang terakhir diukur',
name: 'weight', name: 'weight',
required: true, required: true,
keyboardType: 'numeric', keyboardType: 'numeric',
}, },
{ {
label: 'Tinggi Badan (cm)', label: 'Tinggi Badan (cm)',
placeholder: 'Masukkan yang terakhir diukur', placeholder: 'Masukkan tinggi badan yang terakhir diukur',
name: 'height', name: 'height',
required: true, required: true,
keyboardType: 'numeric', keyboardType: 'numeric',
}, },
{ {
label: 'Lingkar Pinggang (cm)', label: 'Lingkar Pinggang (cm)',
placeholder: 'Masukkan yang terakhir diukur', placeholder: 'Masukkan lingkar pinggang yang terakhir diukur',
name: 'waist_size', name: 'waist_size',
required: true, required: true,
keyboardType: 'numeric', keyboardType: 'numeric',
...@@ -45,6 +45,7 @@ export const dietReportTextFields: { [_: string]: TextFieldSchema[] } = { ...@@ -45,6 +45,7 @@ export const dietReportTextFields: { [_: string]: TextFieldSchema[] } = {
{ {
label: label:
'Dalam 1 minggu terakhir, apa saja yang sudah bisa Anda pelajari dari program ini?', 'Dalam 1 minggu terakhir, apa saja yang sudah bisa Anda pelajari dari program ini?',
placeholder: 'Masukkan pelajaran minggu ini',
name: 'lesson_learned', name: 'lesson_learned',
required: true, required: true,
multiline: true, multiline: true,
...@@ -54,6 +55,7 @@ export const dietReportTextFields: { [_: string]: TextFieldSchema[] } = { ...@@ -54,6 +55,7 @@ export const dietReportTextFields: { [_: string]: TextFieldSchema[] } = {
label: `Silahkan sampaikan disini, jika Anda mempunyai: label: `Silahkan sampaikan disini, jika Anda mempunyai:
(1) kendala, keluhan atau kesulitan dalam mengikuti program, atau (1) kendala, keluhan atau kesulitan dalam mengikuti program, atau
(2) masukan, saran, dan komplain terkait layanan sejauh ini.`, (2) masukan, saran, dan komplain terkait layanan sejauh ini.`,
placeholder: 'Masukkan keluh kesah atau saran bagi Dietela',
name: 'problem_faced_and_feedbacks', name: 'problem_faced_and_feedbacks',
multiline: true, multiline: true,
}, },
......
...@@ -8,7 +8,7 @@ const useDownloadFiles = ( ...@@ -8,7 +8,7 @@ const useDownloadFiles = (
fileType: FileType, fileType: FileType,
fileTitle?: string, fileTitle?: string,
) => { ) => {
const fileName = fileTitle ? fileTitle : url.split('/').pop(); const fileName = fileTitle ? fileTitle : url?.split('/').pop();
const extension = fileName?.split('.').pop()?.toUpperCase() || '-'; const extension = fileName?.split('.').pop()?.toUpperCase() || '-';
const askWritePermission = async () => const askWritePermission = async () =>
......
import { createContext, useCallback, useState } from 'react'; import { createContext, useCallback, useState } from 'react';
import { GoogleSignin } from '@react-native-google-signin/google-signin'; import { GoogleSignin } from '@react-native-google-signin/google-signin';
import { Toast } from 'components/core'; import Toast from 'react-native-toast-message';
import CACHE_KEYS from 'constants/cacheKeys'; import CACHE_KEYS from 'constants/cacheKeys';
import { removeCache, getCache, setCache } from 'utils/cache'; import { removeCache, getCache, setCache } from 'utils/cache';
...@@ -37,6 +37,7 @@ export const UserContext = createContext<iUserContext>({ ...@@ -37,6 +37,7 @@ export const UserContext = createContext<iUserContext>({
isAuthenticated: false, isAuthenticated: false,
isLoading: false, isLoading: false,
isFirstLoading: false, isFirstLoading: false,
setUser: (_: AuthUserResponse) => {},
getUser: () => Promise.reject(), getUser: () => Promise.reject(),
signup: () => Promise.reject(), signup: () => Promise.reject(),
login: () => Promise.reject(), login: () => Promise.reject(),
...@@ -155,6 +156,7 @@ export const useUserContext = (): iUserContext => { ...@@ -155,6 +156,7 @@ export const useUserContext = (): iUserContext => {
isAuthenticated: user.id !== null, isAuthenticated: user.id !== null,
isLoading, isLoading,
isFirstLoading, isFirstLoading,
setUser,
getUser, getUser,
signup, signup,
login, login,
......
...@@ -12,6 +12,7 @@ export interface iUserContext { ...@@ -12,6 +12,7 @@ export interface iUserContext {
isAuthenticated: boolean; isAuthenticated: boolean;
isLoading: boolean; isLoading: boolean;
isFirstLoading: boolean; isFirstLoading: boolean;
setUser: (_: AuthUserResponse) => void;
getUser: () => Promise<void>; getUser: () => Promise<void>;
signup: ( signup: (
data: RegistrationRequest, data: RegistrationRequest,
......
...@@ -22,7 +22,7 @@ const Checkout: FC = () => { ...@@ -22,7 +22,7 @@ const Checkout: FC = () => {
const { isLoading, data } = useApi(() => retrieveCartApi(user.cart_id)); const { isLoading, data } = useApi(() => retrieveCartApi(user.cart_id));
const pay = async () => { const pay = async () => {
const response = await payWithMidtransApi(data?.id); const response = await payWithMidtransApi(user.cart_id);
if (response.success && response.data) { if (response.success && response.data) {
navigation.navigate(ROUTES.payment, { url: response.data.redirect_url }); navigation.navigate(ROUTES.payment, { url: response.data.redirect_url });
} else { } else {
......
...@@ -22,7 +22,7 @@ import { UserContext } from 'provider'; ...@@ -22,7 +22,7 @@ import { UserContext } from 'provider';
const ChoosePlan: FC = () => { const ChoosePlan: FC = () => {
const navigation = useNavigation(); const navigation = useNavigation();
const { user, isAuthenticated } = useContext(UserContext); const { user, setUser, isAuthenticated } = useContext(UserContext);
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
...@@ -41,8 +41,13 @@ const ChoosePlan: FC = () => { ...@@ -41,8 +41,13 @@ const ChoosePlan: FC = () => {
setIsSubmitting(false); setIsSubmitting(false);
if (response.success) { if (response.success) {
await setCache(CACHE_KEYS.cartId, response.data?.id); const cartId = response.data?.id as number;
await setCache(CACHE_KEYS.cartId, cartId);
if (isAuthenticated) { if (isAuthenticated) {