Fakultas Ilmu Komputer UI

Commit d21f9c3d authored by Wulan Mantiri's avatar Wulan Mantiri

Fix returning users and navigation logic

parent 3b8d062e
......@@ -5,6 +5,8 @@ import {
adminNavigation,
unpaidClientNavigation,
paidClientNavigation,
cartsExpireClientNavigation,
takeQuizClientNavigation,
} from 'constants/navigation';
import { UserRole, AuthUserResponse } from 'services/auth/models';
import { TransactionStatus } from 'services/payment/models';
......@@ -18,20 +20,28 @@ export const getNavigation = (
if (!user.cart_id) {
return {
initialRoute: ROUTES.allAccessQuestionnaire,
navigation: unpaidClientNavigation,
navigation: takeQuizClientNavigation,
};
}
if ([TransactionStatus.UNPAID, null].includes(user.transaction_status)) {
if (user.all_carts_are_expired) {
return {
initialRoute: ROUTES.checkout,
navigation: unpaidClientNavigation,
initialRoute: ROUTES.allCartsExpired,
navigation: cartsExpireClientNavigation,
};
} else {
}
if (user.transaction_status === TransactionStatus.UNPAID) {
return {
initialRoute: ROUTES.clientTabNavigation,
navigation: paidClientNavigation,
initialRoute: ROUTES.checkout,
navigation: unpaidClientNavigation,
};
}
return {
initialRoute: ROUTES.clientTabNavigation,
navigation: paidClientNavigation,
};
}
if (user.role === UserRole.NUTRITIONIST) {
......
......@@ -24,6 +24,7 @@ import {
LoginChoosePlan,
ClientNavigation,
ChooseWeekForNutritionist,
AllCartsExpiredPage,
} from 'scenes';
import { FC } from 'react';
import DietReportForNutritionist from 'scenes/nutritionist/DietReportForNutritionist';
......@@ -90,6 +91,62 @@ export const publicNavigation: NavRoute[] = [
},
];
export const takeQuizClientNavigation: NavRoute[] = [
...navigation,
{
name: ROUTES.allCartsExpired,
component: AllCartsExpiredPage,
header: 'Dietela',
},
{
name: ROUTES.checkout,
component: Checkout,
header: 'Checkout',
},
{
name: ROUTES.payment,
component: PaymentWebView,
header: 'Pembayaran',
},
{
name: ROUTES.paymentResult,
component: PaymentResult,
},
{
name: ROUTES.clientTabNavigation,
component: ClientNavigation,
header: 'Dietela',
},
];
export const cartsExpireClientNavigation: NavRoute[] = [
{
name: ROUTES.allCartsExpired,
component: AllCartsExpiredPage,
header: 'Dietela',
},
{
name: ROUTES.checkout,
component: Checkout,
header: 'Checkout',
},
{
name: ROUTES.payment,
component: PaymentWebView,
header: 'Pembayaran',
},
{
name: ROUTES.paymentResult,
component: PaymentResult,
},
...navigation,
{
name: ROUTES.clientTabNavigation,
component: ClientNavigation,
header: 'Dietela',
},
];
export const unpaidClientNavigation: NavRoute[] = [
{
name: ROUTES.checkout,
......
export const initial = 'initial-page';
export const allCartsExpired = 'all-carts-expired';
export const comingSoon = '*';
const questionnaire = 'questionnaire';
......
import { PermissionsAndroid } from 'react-native';
import RNFetchBlob from 'rn-fetch-blob';
import { FileType } from './schema';
import Toast from 'react-native-toast-message';
const useDownloadFiles = (
url = '',
......@@ -22,13 +23,19 @@ const useDownloadFiles = (
return;
}
Toast.show({
type: 'success',
text1: `Mengunduh ${fileName}...`,
text2: 'Notifikasi akan muncul setelah unduh selesai. Mohon menunggu.',
});
const dirs = RNFetchBlob.fs.dirs;
RNFetchBlob.config({
addAndroidDownloads: {
useDownloadManager: true,
notification: true,
mime: fileType,
title: 'Mengunduh ' + title + '...',
title: title || fileName,
mediaScannable: true,
path: dirs.DownloadDir + `/${fileName}`,
},
......
......@@ -31,6 +31,7 @@ export const initialUser = {
is_finished_onboarding: false,
cart_id: null,
nutritionist: null,
all_carts_are_expired: false,
};
export const UserContext = createContext<iUserContext>({
......
......@@ -19,6 +19,7 @@ import { setCache, getCache } from 'utils/cache';
import { PricingList } from './components';
import { initialValues, getRecommendedPrograms } from './schema';
import { UserContext } from 'provider';
import { TransactionStatus } from 'services/payment/models';
const ChoosePlan: FC = () => {
const navigation = useNavigation();
......@@ -47,6 +48,7 @@ const ChoosePlan: FC = () => {
setUser({
...user,
cart_id: cartId,
transaction_status: TransactionStatus.UNPAID,
});
navigation.navigate(ROUTES.checkout);
} else {
......
import { StyleSheet } from 'react-native';
import { colors, typography } from 'styles';
import { colors, typography, layoutStyles } from 'styles';
export const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
...layoutStyles,
paddingHorizontal: 30,
},
nutritionist: {
textAlign: 'center',
......
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import AllCartsExpiredPage from '.';
const mockedNavigate = jest.fn();
jest.mock('@react-navigation/native', () => {
return {
useNavigation: () => ({
navigate: mockedNavigate,
}),
};
});
describe('AllCartsExpiredPage', () => {
it('has call-to-action button that navigates to Dietela Quiz', () => {
const { getByText, queryByText } = render(<AllCartsExpiredPage />);
expect(queryByText(/konsultasi lagi/i)).toBeTruthy();
fireEvent.press(getByText(/konsultasi lagi/i));
expect(mockedNavigate).toHaveBeenCalled();
});
});
import React, { FC } from 'react';
import { View } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import * as ROUTES from 'constants/routes';
import { EmptyDataPage, BigButton } from 'components/core';
import { styles } from './styles';
const AllCartsExpiredPage: FC = () => {
const navigation = useNavigation();
return (
<View style={styles.center}>
<EmptyDataPage text="Program diet Anda telah selesai!" />
<BigButton
title="konsultasi lagi"
onPress={() => navigation.navigate(ROUTES.allAccessQuestionnaire)}
/>
</View>
);
};
export default AllCartsExpiredPage;
import { StyleSheet } from 'react-native';
import { typography, layoutStyles } from 'styles';
export const styles = StyleSheet.create({
center: {
flex: 1,
...layoutStyles,
},
noRecomText: {
...typography.headingMedium,
textAlign: 'center',
marginTop: 10,
},
});
......@@ -5,6 +5,7 @@ export { default as ManualRegistrationPage } from './auth/ManualRegistrationPage
export { default as InitialPage } from './common/InitialPage';
export { default as ComingSoonPage } from './common/ComingSoonPage';
export { default as AllCartsExpiredPage } from './common/AllCartsExpiredPage';
export { default as AllAccessQuestionnaire } from './questionnaire/AllAccessQuestionnaire';
export { default as DietelaQuizResult } from './questionnaire/DietelaQuizResult';
......
......@@ -6,13 +6,43 @@ import ClientNavigation, {
ExtQuestionnaireStackScreen,
} from '.';
import { NavigationContainer } from '@react-navigation/native';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
describe('ClientNavigation', () => {
it('renders correctly', () => {
it('provides tab if user has finished onboarding', () => {
const userProviderValues = {
...mockUserContext,
user: {
...mockUserContext.user,
is_finished_onboarding: true,
},
};
render(
<NavigationContainer>
<ClientNavigation />
</NavigationContainer>,
<UserContext.Provider value={userProviderValues}>
<NavigationContainer>
<ClientNavigation />
</NavigationContainer>
</UserContext.Provider>,
);
});
it('does not provide tab if user has not finished onboarding', () => {
const userProviderValues = {
...mockUserContext,
user: {
...mockUserContext.user,
is_finished_onboarding: false,
},
};
render(
<UserContext.Provider value={userProviderValues}>
<NavigationContainer>
<ClientNavigation />
</NavigationContainer>
</UserContext.Provider>,
);
});
......
import React, { FC } from 'react';
import React, { FC, useContext } from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Icon } from 'react-native-elements';
......@@ -21,6 +21,7 @@ import {
import ReadOnlyWeeklyReport from 'scenes/report/ReadOnlyWeeklyReport';
import ChooseWeekForClient from 'scenes/report/ChooseWeekForClient';
import ChatForClient from 'scenes/chat/ChatForClient';
import { UserContext } from 'provider';
interface NavRoute<T = any> {
name: string;
......@@ -107,6 +108,11 @@ export const WeeklyReportStackScreen: FC = () => (
const ClientTab = createBottomTabNavigator();
const ClientNavigation: FC = () => {
const { user } = useContext(UserContext);
if (!user.is_finished_onboarding) {
return <ExtQuestionnaireStackScreen />;
}
return (
<ClientTab.Navigator
initialRouteName={ROUTES.clientTabProfile}
......
......@@ -3,6 +3,8 @@ import { render } from '@testing-library/react-native';
import ConsentForm from '.';
import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
const mockedNavigate = jest.fn();
......@@ -15,7 +17,15 @@ jest.mock('@react-navigation/native', () => {
});
describe('ConsentForm', () => {
const userProviderValues = {
...mockUserContext,
};
it('renders correctly', () => {
render(<ConsentForm route={{ params: mockDietQuestionnaire }} />);
render(
<UserContext.Provider value={userProviderValues}>
<ConsentForm route={{ params: mockDietQuestionnaire }} />
</UserContext.Provider>,
);
});
});
......@@ -5,6 +5,8 @@ import axios from 'axios';
import Questionnaire1 from '.';
import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire';
import { textFields } from 'constants/questionnaire';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
const mockedNavigate = jest.fn();
......@@ -27,6 +29,10 @@ describe('Questionnaire1', () => {
waist_size: '765',
};
const userProviderValues = {
...mockUserContext,
};
it('does not redirect to extended questionnaire if form values are invalid', async () => {
const updateDietQuestionnaireApi = () =>
Promise.resolve({
......@@ -36,7 +42,9 @@ describe('Questionnaire1', () => {
mockAxios.request.mockImplementationOnce(updateDietQuestionnaireApi);
const { getByPlaceholderText, getByText } = render(
<Questionnaire1 route={{ params: mockDietQuestionnaire }} />,
<UserContext.Provider value={userProviderValues}>
<Questionnaire1 route={{ params: mockDietQuestionnaire }} />
</UserContext.Provider>,
);
textFields.identity.forEach(({ name, placeholder }) => {
......
......@@ -3,6 +3,8 @@ import { render } from '@testing-library/react-native';
import Questionnaire2 from '.';
import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
const mockedNavigate = jest.fn();
......@@ -15,7 +17,15 @@ jest.mock('@react-navigation/native', () => {
});
describe('Questionnaire2', () => {
const userProviderValues = {
...mockUserContext,
};
it('renders correctly', () => {
render(<Questionnaire2 route={{ params: mockDietQuestionnaire }} />);
render(
<UserContext.Provider value={userProviderValues}>
<Questionnaire2 route={{ params: mockDietQuestionnaire }} />
</UserContext.Provider>,
);
});
});
......@@ -4,6 +4,8 @@ import axios from 'axios';
import Questionnaire3 from '.';
import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
const mockedNavigate = jest.fn();
......@@ -19,8 +21,16 @@ jest.mock('axios');
const mockAxios = axios as jest.Mocked<typeof axios>;
describe('Questionnaire3', () => {
const userProviderValues = {
...mockUserContext,
};
it('renders correctly', () => {
render(<Questionnaire3 route={{ params: mockDietQuestionnaire }} />);
render(
<UserContext.Provider value={userProviderValues}>
<Questionnaire3 route={{ params: mockDietQuestionnaire }} />
</UserContext.Provider>,
);
});
it('does not redirect to extended questionnaire if submit fails', async () => {
......@@ -34,7 +44,9 @@ describe('Questionnaire3', () => {
mockAxios.request.mockImplementationOnce(updateDietQuestionnaireApi);
const { getAllByPlaceholderText, getByText } = render(
<Questionnaire3 route={{ params: mockDietQuestionnaire }} />,
<UserContext.Provider value={userProviderValues}>
<Questionnaire3 route={{ params: mockDietQuestionnaire }} />
</UserContext.Provider>,
);
const formFields = getAllByPlaceholderText('Sesuai format jawaban');
......@@ -57,7 +69,9 @@ describe('Questionnaire3', () => {
mockAxios.request.mockImplementationOnce(updateDietQuestionnaireApi);
const { getAllByPlaceholderText, getByText } = render(
<Questionnaire3 route={{ params: mockDietQuestionnaire }} />,
<UserContext.Provider value={userProviderValues}>
<Questionnaire3 route={{ params: mockDietQuestionnaire }} />
</UserContext.Provider>,
);
const formFields = getAllByPlaceholderText('Sesuai format jawaban');
......
......@@ -4,6 +4,8 @@ import axios from 'axios';
import Questionnaire4 from '.';
import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
const mockedNavigate = jest.fn();
......@@ -19,8 +21,16 @@ jest.mock('axios');
const mockAxios = axios as jest.Mocked<typeof axios>;
describe('Questionnaire4', () => {
const userProviderValues = {
...mockUserContext,
};
it('renders correctly', () => {
render(<Questionnaire4 route={{ params: mockDietQuestionnaire }} />);
render(
<UserContext.Provider value={userProviderValues}>
<Questionnaire4 route={{ params: mockDietQuestionnaire }} />
</UserContext.Provider>,
);
});
it('redirects to extended questionnaire if all form values are valid and submit success', async () => {
......@@ -32,7 +42,9 @@ describe('Questionnaire4', () => {
mockAxios.request.mockImplementationOnce(updateDietQuestionnaireApi);
const { getByText } = render(
<Questionnaire4 route={{ params: mockDietQuestionnaire }} />,
<UserContext.Provider value={userProviderValues}>
<Questionnaire4 route={{ params: mockDietQuestionnaire }} />
</UserContext.Provider>,
);
const submitButton = getByText(/Simpan/i);
......
......@@ -3,6 +3,8 @@ import { render } from '@testing-library/react-native';
import Questionnaire5 from '.';
import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
const mockedNavigate = jest.fn();
......@@ -16,6 +18,14 @@ jest.mock('@react-navigation/native', () => {
describe('Questionnaire5', () => {
it('renders correctly', () => {
render(<Questionnaire5 route={{ params: mockDietQuestionnaire }} />);
const userProviderValues = {
...mockUserContext,
};
render(
<UserContext.Provider value={userProviderValues}>
<Questionnaire5 route={{ params: mockDietQuestionnaire }} />
</UserContext.Provider>,
);
});
});
import React, { FC } from 'react';
import React, { FC, useContext } from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';
......@@ -10,6 +10,7 @@ import { layoutStyles } from 'styles';
import { generateValidationSchema } from 'utils/form';
import { Props } from './types';
import { updateDietQuestionnaireApi } from 'services/dietQuestionnaire';
import { UserContext } from 'provider';
const QuestionnaireWrapper: FC<Props> = ({
Questionnaire,
......@@ -20,6 +21,7 @@ const QuestionnaireWrapper: FC<Props> = ({
}) => {
const navigation = useNavigation();
const dietQuestionnaireResponse = route.params;
const { user, setUser } = useContext(UserContext);
const getInitialValues = () => {
let defaultValues: typeof initialValues = {};
......@@ -46,6 +48,12 @@ const QuestionnaireWrapper: FC<Props> = ({
payload,
);
if (response.success && response.data) {
if (response.data.finished_steps.length === 6) {
setUser({
...user,
is_finished_onboarding: true,
});
}
navigation.reset({
index: 0,
routes: [{ name: ROUTES.extendedQuestionnaire }],
......
......@@ -15,11 +15,7 @@ import { Props } from './types';
const ReadOnlyDietRecommendation: FC<Props> = ({ children, data }) => {
const url = API_BASE_URL + data?.client_plan_meal;
const { download, fileName } = useDownloadFiles(
url,
'rencana makan',
FileType.PDF,
);
const { download, fileName } = useDownloadFiles(url, '', FileType.PDF);
const hasValues = (obj: DietRecommendationResponse) =>
[obj.client_plan_meal, obj.nutritional_advice, obj.lifestyle_advice].reduce(
......
......@@ -31,6 +31,7 @@ export interface AuthUserResponse extends User {
transaction_status: TransactionStatus | null;
is_finished_onboarding: boolean;
cart_id: number | null;
all_carts_are_expired: boolean;
nutritionist: {
full_name_and_degree: string;
phone_number: string | null;
......
Markdown is supported
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