Fakultas Ilmu Komputer UI

Commit d21f9c3d authored by Wulan Mantiri's avatar Wulan Mantiri
Browse files

Fix returning users and navigation logic

parent 3b8d062e
...@@ -5,6 +5,8 @@ import { ...@@ -5,6 +5,8 @@ import {
adminNavigation, adminNavigation,
unpaidClientNavigation, unpaidClientNavigation,
paidClientNavigation, paidClientNavigation,
cartsExpireClientNavigation,
takeQuizClientNavigation,
} 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';
...@@ -18,20 +20,28 @@ export const getNavigation = ( ...@@ -18,20 +20,28 @@ export const getNavigation = (
if (!user.cart_id) { if (!user.cart_id) {
return { return {
initialRoute: ROUTES.allAccessQuestionnaire, initialRoute: ROUTES.allAccessQuestionnaire,
navigation: unpaidClientNavigation, navigation: takeQuizClientNavigation,
}; };
} }
if ([TransactionStatus.UNPAID, null].includes(user.transaction_status)) {
if (user.all_carts_are_expired) {
return { return {
initialRoute: ROUTES.checkout, initialRoute: ROUTES.allCartsExpired,
navigation: unpaidClientNavigation, navigation: cartsExpireClientNavigation,
}; };
} else { }
if (user.transaction_status === TransactionStatus.UNPAID) {
return { return {
initialRoute: ROUTES.clientTabNavigation, initialRoute: ROUTES.checkout,
navigation: paidClientNavigation, navigation: unpaidClientNavigation,
}; };
} }
return {
initialRoute: ROUTES.clientTabNavigation,
navigation: paidClientNavigation,
};
} }
if (user.role === UserRole.NUTRITIONIST) { if (user.role === UserRole.NUTRITIONIST) {
......
...@@ -24,6 +24,7 @@ import { ...@@ -24,6 +24,7 @@ import {
LoginChoosePlan, LoginChoosePlan,
ClientNavigation, ClientNavigation,
ChooseWeekForNutritionist, ChooseWeekForNutritionist,
AllCartsExpiredPage,
} from 'scenes'; } from 'scenes';
import { FC } from 'react'; import { FC } from 'react';
import DietReportForNutritionist from 'scenes/nutritionist/DietReportForNutritionist'; import DietReportForNutritionist from 'scenes/nutritionist/DietReportForNutritionist';
...@@ -90,6 +91,62 @@ export const publicNavigation: NavRoute[] = [ ...@@ -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[] = [ export const unpaidClientNavigation: NavRoute[] = [
{ {
name: ROUTES.checkout, name: ROUTES.checkout,
......
export const initial = 'initial-page'; export const initial = 'initial-page';
export const allCartsExpired = 'all-carts-expired';
export const comingSoon = '*'; export const comingSoon = '*';
const questionnaire = 'questionnaire'; const questionnaire = 'questionnaire';
......
import { PermissionsAndroid } from 'react-native'; import { PermissionsAndroid } from 'react-native';
import RNFetchBlob from 'rn-fetch-blob'; import RNFetchBlob from 'rn-fetch-blob';
import { FileType } from './schema'; import { FileType } from './schema';
import Toast from 'react-native-toast-message';
const useDownloadFiles = ( const useDownloadFiles = (
url = '', url = '',
...@@ -22,13 +23,19 @@ const useDownloadFiles = ( ...@@ -22,13 +23,19 @@ const useDownloadFiles = (
return; return;
} }
Toast.show({
type: 'success',
text1: `Mengunduh ${fileName}...`,
text2: 'Notifikasi akan muncul setelah unduh selesai. Mohon menunggu.',
});
const dirs = RNFetchBlob.fs.dirs; const dirs = RNFetchBlob.fs.dirs;
RNFetchBlob.config({ RNFetchBlob.config({
addAndroidDownloads: { addAndroidDownloads: {
useDownloadManager: true, useDownloadManager: true,
notification: true, notification: true,
mime: fileType, mime: fileType,
title: 'Mengunduh ' + title + '...', title: title || fileName,
mediaScannable: true, mediaScannable: true,
path: dirs.DownloadDir + `/${fileName}`, path: dirs.DownloadDir + `/${fileName}`,
}, },
......
...@@ -31,6 +31,7 @@ export const initialUser = { ...@@ -31,6 +31,7 @@ export const initialUser = {
is_finished_onboarding: false, is_finished_onboarding: false,
cart_id: null, cart_id: null,
nutritionist: null, nutritionist: null,
all_carts_are_expired: false,
}; };
export const UserContext = createContext<iUserContext>({ export const UserContext = createContext<iUserContext>({
......
...@@ -19,6 +19,7 @@ import { setCache, getCache } from 'utils/cache'; ...@@ -19,6 +19,7 @@ import { setCache, getCache } from 'utils/cache';
import { PricingList } from './components'; import { PricingList } from './components';
import { initialValues, getRecommendedPrograms } from './schema'; import { initialValues, getRecommendedPrograms } from './schema';
import { UserContext } from 'provider'; import { UserContext } from 'provider';
import { TransactionStatus } from 'services/payment/models';
const ChoosePlan: FC = () => { const ChoosePlan: FC = () => {
const navigation = useNavigation(); const navigation = useNavigation();
...@@ -47,6 +48,7 @@ const ChoosePlan: FC = () => { ...@@ -47,6 +48,7 @@ const ChoosePlan: FC = () => {
setUser({ setUser({
...user, ...user,
cart_id: cartId, cart_id: cartId,
transaction_status: TransactionStatus.UNPAID,
}); });
navigation.navigate(ROUTES.checkout); navigation.navigate(ROUTES.checkout);
} else { } else {
......
import { StyleSheet } from 'react-native'; import { StyleSheet } from 'react-native';
import { colors, typography } from 'styles'; import { colors, typography, layoutStyles } from 'styles';
export const styles = StyleSheet.create({ export const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
...layoutStyles,
paddingHorizontal: 30,
}, },
nutritionist: { nutritionist: {
textAlign: 'center', 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 ...@@ -5,6 +5,7 @@ export { default as ManualRegistrationPage } from './auth/ManualRegistrationPage
export { default as InitialPage } from './common/InitialPage'; export { default as InitialPage } from './common/InitialPage';
export { default as ComingSoonPage } from './common/ComingSoonPage'; export { default as ComingSoonPage } from './common/ComingSoonPage';
export { default as AllCartsExpiredPage } from './common/AllCartsExpiredPage';
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';
......
...@@ -6,13 +6,43 @@ import ClientNavigation, { ...@@ -6,13 +6,43 @@ import ClientNavigation, {
ExtQuestionnaireStackScreen, ExtQuestionnaireStackScreen,
} from '.'; } from '.';
import { NavigationContainer } from '@react-navigation/native'; import { NavigationContainer } from '@react-navigation/native';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
describe('ClientNavigation', () => { describe('ClientNavigation', () => {
it('renders correctly', () => { it('provides tab if user has finished onboarding', () => {
const userProviderValues = {
...mockUserContext,
user: {
...mockUserContext.user,
is_finished_onboarding: true,
},
};
render( render(
<NavigationContainer> <UserContext.Provider value={userProviderValues}>
<ClientNavigation /> <NavigationContainer>
</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 { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Icon } from 'react-native-elements'; import { Icon } from 'react-native-elements';
...@@ -21,6 +21,7 @@ import { ...@@ -21,6 +21,7 @@ import {
import ReadOnlyWeeklyReport from 'scenes/report/ReadOnlyWeeklyReport'; import ReadOnlyWeeklyReport from 'scenes/report/ReadOnlyWeeklyReport';
import ChooseWeekForClient from 'scenes/report/ChooseWeekForClient'; import ChooseWeekForClient from 'scenes/report/ChooseWeekForClient';
import ChatForClient from 'scenes/chat/ChatForClient'; import ChatForClient from 'scenes/chat/ChatForClient';
import { UserContext } from 'provider';
interface NavRoute<T = any> { interface NavRoute<T = any> {
name: string; name: string;
...@@ -107,6 +108,11 @@ export const WeeklyReportStackScreen: FC = () => ( ...@@ -107,6 +108,11 @@ export const WeeklyReportStackScreen: FC = () => (
const ClientTab = createBottomTabNavigator(); const ClientTab = createBottomTabNavigator();
const ClientNavigation: FC = () => { const ClientNavigation: FC = () => {
const { user } = useContext(UserContext);
if (!user.is_finished_onboarding) {
return <ExtQuestionnaireStackScreen />;
}
return ( return (
<ClientTab.Navigator <ClientTab.Navigator
initialRouteName={ROUTES.clientTabProfile} initialRouteName={ROUTES.clientTabProfile}
......
...@@ -3,6 +3,8 @@ import { render } from '@testing-library/react-native'; ...@@ -3,6 +3,8 @@ import { render } from '@testing-library/react-native';
import ConsentForm from '.'; import ConsentForm from '.';
import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire'; import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
const mockedNavigate = jest.fn(); const mockedNavigate = jest.fn();
...@@ -15,7 +17,15 @@ jest.mock('@react-navigation/native', () => { ...@@ -15,7 +17,15 @@ jest.mock('@react-navigation/native', () => {
}); });
describe('ConsentForm', () => { describe('ConsentForm', () => {
const userProviderValues = {
...mockUserContext,
};
it('renders correctly', () => { 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'; ...@@ -5,6 +5,8 @@ import axios from 'axios';
import Questionnaire1 from '.'; import Questionnaire1 from '.';
import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire'; import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire';
import { textFields } from 'constants/questionnaire'; import { textFields } from 'constants/questionnaire';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
const mockedNavigate = jest.fn(); const mockedNavigate = jest.fn();
...@@ -27,6 +29,10 @@ describe('Questionnaire1', () => { ...@@ -27,6 +29,10 @@ describe('Questionnaire1', () => {
waist_size: '765', waist_size: '765',
}; };
const userProviderValues = {
...mockUserContext,
};
it('does not redirect to extended questionnaire if form values are invalid', async () => { it('does not redirect to extended questionnaire if form values are invalid', async () => {
const updateDietQuestionnaireApi = () => const updateDietQuestionnaireApi = () =>
Promise.resolve({ Promise.resolve({
...@@ -36,7 +42,9 @@ describe('Questionnaire1', () => { ...@@ -36,7 +42,9 @@ describe('Questionnaire1', () => {
mockAxios.request.mockImplementationOnce(updateDietQuestionnaireApi); mockAxios.request.mockImplementationOnce(updateDietQuestionnaireApi);
const { getByPlaceholderText, getByText } = render( const { getByPlaceholderText, getByText } = render(
<Questionnaire1 route={{ params: mockDietQuestionnaire }} />, <UserContext.Provider value={userProviderValues}>
<Questionnaire1 route={{ params: mockDietQuestionnaire }} />
</UserContext.Provider>,
); );
textFields.identity.forEach(({ name, placeholder }) => { textFields.identity.forEach(({ name, placeholder }) => {
......
...@@ -3,6 +3,8 @@ import { render } from '@testing-library/react-native'; ...@@ -3,6 +3,8 @@ import { render } from '@testing-library/react-native';
import Questionnaire2 from '.'; import Questionnaire2 from '.';
import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire'; import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
const mockedNavigate = jest.fn(); const mockedNavigate = jest.fn();
...@@ -15,7 +17,15 @@ jest.mock('@react-navigation/native', () => { ...@@ -15,7 +17,15 @@ jest.mock('@react-navigation/native', () => {
}); });
describe('Questionnaire2', () => { describe('Questionnaire2', () => {
const userProviderValues = {
...mockUserContext,
};
it('renders correctly', () => { 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'; ...@@ -4,6 +4,8 @@ import axios from 'axios';
import Questionnaire3 from '.'; import Questionnaire3 from '.';
import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire'; import { mockDietQuestionnaire } from 'mocks/dietQuestionnaire';
import { mockUserContext } from 'mocks/userContext';
import { UserContext } from 'provider';
const mockedNavigate = jest.fn(); const mockedNavigate = jest.fn();
...@@ -19,8 +21,16 @@ jest.mock('axios'); ...@@ -19,8 +21,16 @@ jest.mock('axios');
const mockAxios = axios as jest.Mocked<typeof axios>; const mockAxios = axios as jest.Mocked<typeof axios>;
describe('Questionnaire3', () => { describe('Questionnaire3', () => {
const userProviderValues = {
...mockUserContext,
};
it('renders correctly', () => { 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 () => { it('does not redirect to extended questionnaire if submit fails', async () => {
...@@ -34,7 +44,9 @@ describe('Questionnaire3', () => { ...@@ -34,7 +44,9 @@ describe('Questionnaire3', () => {
mockAxios.request.mockImplementationOnce(updateDietQuestionnaireApi); mockAxios.request.mockImplementationOnce(updateDietQuestionnaireApi);