Merge pull request #2 from lemeow125/feature/onboarding

Feature/onboarding
This commit is contained in:
lemeow125 2023-07-06 21:28:07 +08:00 committed by GitHub
commit 3f40140577
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 559 additions and 144 deletions

33
App.tsx
View file

@ -17,6 +17,7 @@ import Onboarding from "./src/routes/Onboarding/Onboarding";
import Revalidation from "./src/routes/Revalidation/Revalidation";
import Activation from "./src/routes/Activation/Activation";
import { useState, useEffect } from "react";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
const Drawer = createDrawerNavigator();
@ -35,6 +36,8 @@ const linking = {
},
};
const queryClient = new QueryClient();
export default function App() {
const [initialRoute, setInitialRoute] = useState<string | null>(null);
useEffect(() => {
@ -50,20 +53,22 @@ export default function App() {
}, [initialRoute]);
return (
<Provider store={store}>
<NavigationContainer linking={linking}>
<Drawer.Navigator
initialRouteName="Revalidation"
drawerContent={CustomDrawerContent}
screenOptions={DrawerScreenSettings}
>
<Drawer.Screen name="Home" component={Home} />
<Drawer.Screen name="Login" component={Login} />
<Drawer.Screen name="Register" component={Register} />
<Drawer.Screen name="Onboarding" component={Onboarding} />
<Drawer.Screen name="Revalidation" component={Revalidation} />
<Drawer.Screen name="Activation" component={Activation} />
</Drawer.Navigator>
</NavigationContainer>
<QueryClientProvider client={queryClient}>
<NavigationContainer linking={linking}>
<Drawer.Navigator
initialRouteName="Revalidation"
drawerContent={CustomDrawerContent}
screenOptions={DrawerScreenSettings}
>
<Drawer.Screen name="Home" component={Home} />
<Drawer.Screen name="Login" component={Login} />
<Drawer.Screen name="Register" component={Register} />
<Drawer.Screen name="Onboarding" component={Onboarding} />
<Drawer.Screen name="Revalidation" component={Revalidation} />
<Drawer.Screen name="Activation" component={Activation} />
</Drawer.Navigator>
</NavigationContainer>
</QueryClientProvider>
</Provider>
);
}

46
package-lock.json generated
View file

@ -13,6 +13,7 @@
"@react-navigation/native": "^6.1.7",
"@react-navigation/native-stack": "^6.9.13",
"@reduxjs/toolkit": "^1.9.5",
"@tanstack/react-query": "^4.29.19",
"axios": "^1.4.0",
"expo": "~48.0.18",
"expo-linking": "~4.0.1",
@ -20,6 +21,7 @@
"moti": "^0.25.3",
"react": "18.2.0",
"react-native": "0.71.8",
"react-native-dropdown-picker": "^5.4.6",
"react-native-gesture-handler": "~2.9.0",
"react-native-reanimated": "~2.14.4",
"react-native-safe-area-context": "4.5.0",
@ -5201,6 +5203,41 @@
"@sinonjs/commons": "^3.0.0"
}
},
"node_modules/@tanstack/query-core": {
"version": "4.29.19",
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.29.19.tgz",
"integrity": "sha512-uPe1DukeIpIHpQi6UzIgBcXsjjsDaLnc7hF+zLBKnaUlh7jFE/A+P8t4cU4VzKPMFB/C970n/9SxtpO5hmIRgw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tanstack/react-query": {
"version": "4.29.19",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.29.19.tgz",
"integrity": "sha512-XiTIOHHQ5Cw1WUlHaD4fmVUMhoWjuNJlAeJGq7eM4BraI5z7y8WkZO+NR8PSuRnQGblpuVdjClQbDFtwxTtTUw==",
"dependencies": {
"@tanstack/query-core": "4.29.19",
"use-sync-external-store": "^1.2.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-native": "*"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
}
}
},
"node_modules/@types/hammerjs": {
"version": "2.0.41",
"resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz",
@ -12160,6 +12197,15 @@
"nullthrows": "^1.1.1"
}
},
"node_modules/react-native-dropdown-picker": {
"version": "5.4.6",
"resolved": "https://registry.npmjs.org/react-native-dropdown-picker/-/react-native-dropdown-picker-5.4.6.tgz",
"integrity": "sha512-T1XBHbE++M6aRU3wFYw3MvcOuabhWZ29RK/Ivdls2r1ZkZ62iEBZknLUPeVLMX3x6iUxj4Zgr3X2DGlEGXeHsA==",
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-gesture-handler": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.9.0.tgz",

View file

@ -14,20 +14,22 @@
"@react-navigation/native": "^6.1.7",
"@react-navigation/native-stack": "^6.9.13",
"@reduxjs/toolkit": "^1.9.5",
"@tanstack/react-query": "^4.29.19",
"axios": "^1.4.0",
"expo": "~48.0.18",
"expo-linking": "~4.0.1",
"expo-status-bar": "~1.4.4",
"moti": "^0.25.3",
"react": "18.2.0",
"react-native": "0.71.8",
"react-native-dropdown-picker": "^5.4.6",
"react-native-gesture-handler": "~2.9.0",
"react-native-reanimated": "~2.14.4",
"react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.20.0",
"react-native-svg": "13.4.0",
"react-redux": "^8.1.1",
"redux": "^4.2.1",
"expo-linking": "~4.0.1"
"redux": "^4.2.1"
},
"devDependencies": {
"@babel/core": "^7.20.0",

View file

@ -3,6 +3,7 @@ import AsyncStorage from "@react-native-async-storage/async-storage";
import {
ActivationParams,
LoginParams,
OnboardingParams,
RegistrationParams,
} from "../../interfaces/Interfaces";
@ -51,7 +52,10 @@ export function UserRegister(register: RegistrationParams) {
return [true, response.status];
})
.catch((error) => {
return [false, error.response.status, error.response.data];
let error_message = "";
if (error.response) error_message = error.response.data;
else error_message = "Unable to reach servers";
return [false, error_message];
});
}
@ -70,8 +74,10 @@ export function UserLogin(user: LoginParams) {
return [true];
})
.catch((error) => {
console.log("Login Failed:" + JSON.stringify(error.response.data));
return [false, error.response.data];
let error_message = "";
if (error.response) error_message = error.response.data;
else error_message = "Unable to reach servers";
return [false, error_message];
});
}
@ -88,11 +94,14 @@ export async function TokenRefresh() {
"Token refresh success! New Access Token",
response.data.access
);*/
return [true];
return true;
})
.catch((error) => {
console.log("Refresh Failed: " + JSON.stringify(error.response.data));
return [false, error.response.data];
let error_message = "";
if (error.response) error_message = error.response.data;
else error_message = "Unable to reach servers";
console.log("Token Refresh error:", error_message);
return false;
});
}
export async function UserInfo() {
@ -105,11 +114,13 @@ export async function UserInfo() {
})
.then((response) => {
// console.log(JSON.stringify(response.data));
return response.data;
return [true, response.data];
})
.catch((error) => {
console.log("User Info Error", error.response.data);
return [false, error.response.data];
let error_message = "";
if (error.response) error_message = error.response.data;
else error_message = "Unable to reach servers";
return [false, error_message];
});
}
@ -125,3 +136,86 @@ export function UserActivate(activation: ActivationParams) {
}
// App APIs
export async function GetCourses() {
const accessToken = await getAccessToken();
return instance
.get("/api/v1/courses/", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
.then((response) => {
// console.log(JSON.stringify(response.data));
return response.data;
})
.catch((error) => {
let error_message = "";
if (error.response) error_message = error.response.data;
else error_message = "Unable to reach servers";
console.log("Error getting courses", error_message);
return false;
});
}
export async function GetSemesters() {
const accessToken = await getAccessToken();
return instance
.get("/api/v1/semesters/", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
.then((response) => {
// console.log(JSON.stringify(response.data));
return response.data;
})
.catch((error) => {
let error_message = "";
if (error.response) error_message = error.response.data;
else error_message = "Unable to reach servers";
console.log("Error getting semesters", error_message);
return false;
});
}
export async function GetYearLevels() {
const accessToken = await getAccessToken();
return instance
.get("/api/v1/year_levels/", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
.then((response) => {
// console.log(JSON.stringify(response.data));
return response.data;
})
.catch((error) => {
let error_message = "";
if (error.response) error_message = error.response.data;
else error_message = "Unable to reach servers";
console.log("Error getting year levels", error_message);
return false;
});
}
export async function OnboardingUpdateStudentInfo(info: OnboardingParams) {
const accessToken = await getAccessToken();
const headers = {
Authorization: `Bearer ${accessToken}`,
};
return instance
.patch("/api/v1/accounts/users/me/", info, { headers })
.then((response) => {
console.log(JSON.stringify(response.data));
return [true, response.data];
})
.catch((error) => {
let error_message = "";
if (error.response) error_message = error.response.data;
else error_message = "Unable to reach servers";
console.log("Error updating onboarding info", error_message);
return [false, error_message];
});
}

View file

@ -6,15 +6,25 @@ export interface props {
children: React.ReactNode;
onPress: (event: GestureResponderEvent) => void;
color: string;
disabled?: boolean;
}
export default function Button(props: props) {
export default function Button({ disabled = false, ...props }: props) {
const rgb = props.color.match(/\d+/g);
return (
<Pressable
disabled={disabled}
onPress={props.onPress}
style={{
...styles.button_template,
...{ backgroundColor: props.color, width: "50%" },
...{
backgroundColor: disabled
? rgb
? `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, 0.3)`
: "rgba(0, 0, 0, 0)"
: props.color,
width: "50%",
},
}}
>
{props.children}

View file

@ -14,16 +14,40 @@ import DrawerButton from "../Button/DrawerButton";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "../../features/redux/Store/Store";
import LogoutIcon from "../../icons/LogoutIcon/LogoutIcon";
import { clear } from "../../features/redux/slices/AuthSlice/AuthSlice";
import { logout } from "../../features/redux/slices/StatusSlice/StatusSlice";
import AsyncStorage from "@react-native-async-storage/async-storage";
export default function CustomDrawerContent(props: {}) {
const navigation = useNavigation<RootDrawerParamList>();
const logged_in = useSelector(
(state: RootState) => state.auth.creds.logged_in
);
const status = useSelector((state: RootState) => state.status);
const dispatch = useDispatch();
if (logged_in) {
if (status.logged_in && status.onboarding) {
return (
<DrawerContentScrollView {...props}>
<View
style={{
...styles.flex_row,
...{ justifyContent: "center" },
}}
>
<AppIcon size={32} />
<Text style={styles.text_white_medium}>Stud-E</Text>
</View>
<DrawerButton
color={colors.blue_2}
onPress={async () => {
dispatch(logout());
await AsyncStorage.clear();
navigation.navigate("Login");
}}
>
<LogoutIcon size={32} />
<Text style={styles.text_white_medium}>Logout</Text>
</DrawerButton>
</DrawerContentScrollView>
);
} else if (status.logged_in) {
return (
<DrawerContentScrollView {...props}>
<View
@ -47,7 +71,7 @@ export default function CustomDrawerContent(props: {}) {
<DrawerButton
color={colors.blue_2}
onPress={async () => {
dispatch(await clear());
dispatch(logout());
await AsyncStorage.clear();
navigation.navigate("Login");
}}
@ -69,15 +93,6 @@ export default function CustomDrawerContent(props: {}) {
<AppIcon size={32} />
<Text style={styles.text_white_medium}>Stud-E</Text>
</View>
<DrawerButton
color={colors.blue_2}
onPress={() => {
navigation.navigate("Home");
}}
>
<HomeIcon size={32} />
<Text style={styles.text_white_medium}>Home</Text>
</DrawerButton>
<DrawerButton
color={colors.blue_2}
onPress={() => {
@ -90,7 +105,6 @@ export default function CustomDrawerContent(props: {}) {
<DrawerButton
color={colors.blue_2}
onPress={() => {
dispatch(clear());
navigation.navigate("Register");
}}
>

View file

@ -0,0 +1,3 @@
export default function isStringEmpty(str: string) {
return str === "" || str === null || str === undefined;
}

View file

@ -1,9 +1,11 @@
import { configureStore } from "@reduxjs/toolkit";
import AuthReducer from "../slices/AuthSlice/AuthSlice";
import StatusReducer from "../slices/StatusSlice/StatusSlice";
import UserReducer from "../slices/UserSlice/UserSlice";
const store = configureStore({
reducer: {
auth: AuthReducer,
status: StatusReducer,
user: UserReducer,
},
});

View file

@ -1,42 +0,0 @@
import { createSlice } from "@reduxjs/toolkit";
export const AuthSlice = createSlice({
name: "Auth",
initialState: {
creds: {
email: "",
uid: "",
username: "",
full_name: "",
logged_in: false,
},
},
reducers: {
login: (state) => {
state.creds.logged_in = true;
},
setUser: (state, action) => {
state.creds = {
email: action.payload.email,
uid: action.payload.uid,
username: action.payload.username,
full_name: action.payload.full_name,
logged_in: true,
};
},
clear: (state) => {
state.creds = {
email: "",
uid: "",
username: "",
full_name: "",
logged_in: false,
};
},
},
});
// Action creators are generated for each case reducer function
export const { login, setUser, clear } = AuthSlice.actions;
export default AuthSlice.reducer;

View file

@ -0,0 +1,29 @@
import { createSlice } from "@reduxjs/toolkit";
export const StatusSlice = createSlice({
name: "Status",
initialState: {
logged_in: false,
onboarding: false,
},
reducers: {
login: (state) => {
state.logged_in = true;
},
logout: (state) => {
state.logged_in = false;
},
setOnboarding: (state) => {
state.onboarding = true;
},
unsetOnboarding: (state) => {
state.onboarding = false;
},
},
});
// Action creators are generated for each case reducer function
export const { login, logout, setOnboarding, unsetOnboarding } =
StatusSlice.actions;
export default StatusSlice.reducer;

View file

@ -0,0 +1,51 @@
import { createSlice } from "@reduxjs/toolkit";
export const UserSlice = createSlice({
name: "User",
initialState: {
user: {
email: "",
uid: "",
username: "",
first_name: "",
last_name: "",
full_name: "",
year_level: "",
semester: " ",
course: "",
},
},
reducers: {
setUser: (state, action) => {
state.user = {
email: action.payload.email,
uid: action.payload.uid,
username: action.payload.username,
first_name: action.payload.first_name,
last_name: action.payload.last_name,
full_name: action.payload.first_name + " " + action.payload.last_name,
year_level: action.payload.year_level,
semester: action.payload.semester,
course: action.payload.course,
};
},
clear: (state) => {
state.user = {
email: "",
uid: "",
username: "",
first_name: "",
last_name: "",
full_name: "",
year_level: "",
semester: " ",
course: "",
};
},
},
});
// Action creators are generated for each case reducer function
export const { setUser, clear } = UserSlice.actions;
export default UserSlice.reducer;

View file

@ -46,3 +46,27 @@ export interface ActivationParams {
uid: string;
token: string;
}
export interface SemesterParams {
id: string;
name: string;
shortname: string;
}
export interface YearLevelParams {
id: string;
name: string;
shortname: string;
}
export interface CourseParams {
id: string;
name: string;
shortname: string;
}
export interface OnboardingParams {
year_level: string;
course: string;
semester: string;
}

View file

@ -6,7 +6,7 @@ import { RootState } from "../../features/redux/Store/Store";
import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer";
export default function Home() {
const creds = useSelector((state: RootState) => state.auth.creds);
const creds = useSelector((state: RootState) => state.user.user);
return (
<View style={styles.background}>
<AnimatedContainer>

View file

@ -17,12 +17,17 @@ import { RootDrawerParamList } from "../../interfaces/Interfaces";
import { UserInfo, UserLogin } from "../../components/Api/Api";
import { ParseLoginError } from "../../components/ParseError/ParseError";
import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer";
import { setUser as setStateUser } from "../../features/redux/slices/AuthSlice/AuthSlice";
import { setUser } from "../../features/redux/slices/UserSlice/UserSlice";
import {
login,
setOnboarding,
unsetOnboarding,
} from "../../features/redux/slices/StatusSlice/StatusSlice";
export default function Login() {
const navigation = useNavigation<RootDrawerParamList>();
const dispatch = useDispatch();
const [user, setUser] = useState({
const [creds, setCreds] = useState({
username: "",
password: "",
error: "",
@ -50,11 +55,11 @@ export default function Login() {
placeholder="Username"
placeholderTextColor="white"
autoCapitalize="none"
value={user.username}
value={creds.username}
onChange={(
e: NativeSyntheticEvent<TextInputChangeEventData>
): void => {
setUser({ ...user, username: e.nativeEvent.text });
setCreds({ ...creds, username: e.nativeEvent.text });
}}
/>
<View style={{ paddingVertical: 4 }} />
@ -63,42 +68,43 @@ export default function Login() {
placeholder="Password"
placeholderTextColor="white"
secureTextEntry={true}
value={user.password}
value={creds.password}
onChange={(
e: NativeSyntheticEvent<TextInputChangeEventData>
): void => {
setUser({ ...user, password: e.nativeEvent.text });
setCreds({ ...creds, password: e.nativeEvent.text });
}}
/>
<View style={{ paddingVertical: 2 }} />
<Text style={styles.text_white_small}>{user.error}</Text>
<Text style={styles.text_white_small}>{creds.error}</Text>
<View style={{ paddingVertical: 4 }} />
<Button
onPress={async () => {
await UserLogin({
username: user.username,
password: user.password,
username: creds.username,
password: creds.password,
}).then(async (result) => {
if (result[0]) {
setUser({ ...user, username: "", password: "", error: "" });
setUser({ ...creds, username: "", password: "", error: "" });
let user_info = await UserInfo();
dispatch(setStateUser(user_info));
dispatch(login());
dispatch(setUser(user_info[1]));
// Redirect to onboarding if no year level, course, or semester specified
if (
!(
user_info.year_level ||
user_info.course ||
user_info.semester
)
user_info[1].year_level == null ||
user_info[1].course == null ||
user_info[1].semester == null
) {
dispatch(setOnboarding());
navigation.navigate("Onboarding");
} else {
dispatch(unsetOnboarding());
navigation.navigate("Home");
}
console.log(JSON.stringify(user_info));
} else {
setUser({
...user,
...creds,
error: ParseLoginError(JSON.stringify(result[1])),
});
}

View file

@ -2,46 +2,99 @@ import * as React from "react";
import styles from "../../styles";
import { View, Text } from "react-native";
import { useNavigation } from "@react-navigation/native";
import { RootDrawerParamList } from "../../interfaces/Interfaces";
import {
CourseParams,
RootDrawerParamList,
SemesterParams,
YearLevelParams,
} from "../../interfaces/Interfaces";
import { colors } from "../../styles";
import { AnimatePresence, MotiView } from "moti";
import { useEffect, useState } from "react";
import Button from "../../components/Button/Button";
import DropDownPicker from "react-native-dropdown-picker";
import isStringEmpty from "../../components/IsStringEmpty/IsStringEmpty";
import { useQuery } from "@tanstack/react-query";
import {
GetCourses,
GetSemesters,
GetYearLevels,
OnboardingUpdateStudentInfo,
} from "../../components/Api/Api";
import { useDispatch } from "react-redux";
import { unsetOnboarding } from "../../features/redux/slices/StatusSlice/StatusSlice";
import { setUser } from "../../features/redux/slices/UserSlice/UserSlice";
export default function Onboarding() {
const navigation = useNavigation<RootDrawerParamList>();
// const dispatch = useDispatch();
const dispatch = useDispatch();
// const creds = useSelector((state: RootState) => state.auth.creds);
const [student_info, setStudentInfo] = useState({
year_level: "",
course: "",
semester: "",
const [error, setError] = useState("");
// Semesters
const [selected_semester, setSelectedSemester] = useState("");
const [semesterOpen, setSemesterOpen] = useState(false);
const [semesters, setSemesters] = useState([
{ label: "1st Semester", value: "1st Sem" },
{ label: "2nd Semester", value: "2nd Sem" },
]);
const semester_query = useQuery({
queryKey: ["semesters"],
queryFn: GetSemesters,
onSuccess: (data) => {
let semesters = data.map((item: SemesterParams) => ({
label: item.name,
value: item.name,
}));
setSemesters(semesters);
},
});
function Introduction() {
const [shown, setShown] = useState(true);
useEffect(() => {
setTimeout(() => {
setShown(false);
}, 5000);
}, []);
// Year Level
const [selected_yearlevel, setSelectedYearLevel] = useState("");
const [yearLevelOpen, setYearLevelOpen] = useState(false);
const [year_levels, setYearLevels] = useState([
{ label: "1st Year", value: "1st Year" },
{ label: "2nd Year", value: "2nd Year" },
]);
const yearlevel_query = useQuery({
queryKey: ["year_levels"],
queryFn: GetYearLevels,
onSuccess: (data) => {
let year_levels = data.map((item: YearLevelParams) => ({
label: item.name,
value: item.name,
}));
setYearLevels(year_levels);
},
});
// Course
const [selected_course, setSelectedCourse] = useState("");
const [courseOpen, setCourseOpen] = useState(false);
const [courses, setCourses] = useState([
{
label: "Bachelor of Science in Information Technology",
value: "BSIT",
},
{ label: "Bachelor of Science in Computer Science", value: "BSCS" },
]);
const course_query = useQuery({
queryKey: ["courses"],
queryFn: GetCourses,
onSuccess: (data) => {
let courses = data.map((item: CourseParams) => ({
label: item.name,
value: item.name,
}));
setCourses(courses);
},
});
if (yearlevel_query.error || semester_query.error || course_query.error) {
return (
<AnimatePresence>
{shown && (
<MotiView
from={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
exitTransition={{ type: "timing", duration: 1200, delay: 600 }}
transition={{ type: "timing", duration: 1200, delay: 600 }}
>
<Text style={styles.text_white_small}>
We're glad to have you on board {"\n"}
Just a few more things before we get started
</Text>
</MotiView>
)}
</AnimatePresence>
<View style={styles.background}>
<View style={styles.container}>
<Text style={styles.text_white_medium}>Error loading details</Text>
</View>
</View>
);
}
return (
<View style={styles.background}>
<View style={styles.container}>
@ -63,14 +116,112 @@ export default function Onboarding() {
}}
/>
<View style={{ paddingVertical: 4 }} />
<Introduction />
<View style={{ paddingVertical: 8 }} />
<MotiView
from={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ type: "timing", duration: 900, delay: 2000 }}
transition={{ type: "timing", duration: 1200, delay: 600 }}
>
<Text style={styles.text_white_small}>
We're glad to have you on board {"\n"}
Just a few more things before we get started
</Text>
</MotiView>
<View style={{ paddingVertical: 8 }} />
<MotiView
from={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ type: "timing", duration: 400, delay: 1700 }}
>
<Text style={styles.text_white_medium}>Academic Info</Text>
<DropDownPicker
zIndex={3000}
open={courseOpen}
value={selected_course}
items={courses}
setOpen={(open) => {
setCourseOpen(open);
setSemesterOpen(false);
setYearLevelOpen(false);
}}
setValue={setSelectedCourse}
placeholder="Choose your course"
containerStyle={{
...styles.dropdown_template,
...{ zIndex: 3000 },
}}
dropDownContainerStyle={{ backgroundColor: "white" }}
/>
<DropDownPicker
zIndex={2000}
open={semesterOpen}
value={selected_semester}
items={semesters}
setOpen={(open) => {
setSemesterOpen(open);
setCourseOpen(false);
setYearLevelOpen(false);
}}
setValue={setSelectedSemester}
placeholder="Current semester"
containerStyle={{
...styles.dropdown_template,
...{ zIndex: 2000 },
}}
dropDownContainerStyle={{ backgroundColor: "white" }}
/>
<DropDownPicker
zIndex={1000}
open={yearLevelOpen}
value={selected_yearlevel}
items={year_levels}
setOpen={(open) => {
setYearLevelOpen(open);
setSemesterOpen(false);
setCourseOpen(false);
}}
setValue={setSelectedYearLevel}
placeholder="Your Year Level"
containerStyle={{
...styles.dropdown_template,
...{ zIndex: 1000 },
}}
dropDownContainerStyle={{ backgroundColor: "white" }}
/>
</MotiView>
<MotiView
from={{ opacity: 0 }}
animate={{ opacity: 1, zIndex: -1 }}
transition={{ type: "timing", duration: 400, delay: 1700 }}
style={styles.button_template}
>
<Text style={styles.text_white_small}>{error}</Text>
<Button
disabled={
!selected_yearlevel || !selected_course || !selected_semester
}
onPress={async () => {
let result = await OnboardingUpdateStudentInfo({
semester: selected_semester,
course: selected_course,
year_level: selected_yearlevel,
});
if (result[0]) {
dispatch(unsetOnboarding());
setSelectedCourse("");
setSelectedYearLevel("");
setSelectedSemester("");
setError("Success!");
dispatch(setUser(result[1]));
navigation.navigate("Home");
} else {
setError(result[1]);
}
}}
color={colors.blue_3}
>
<Text style={styles.text_white_small}>Proceed</Text>
</Button>
</MotiView>
</View>
</View>

View file

@ -159,7 +159,7 @@ export default function Register() {
} else {
setUser({
...user,
feedback: ParseError(JSON.stringify(result[2])),
feedback: ParseError(JSON.stringify(result[1])),
});
}
});

View file

@ -7,8 +7,13 @@ import { colors } from "../../styles";
import { useEffect, useState } from "react";
import { useNavigation } from "@react-navigation/native";
import { RootDrawerParamList } from "../../interfaces/Interfaces";
import { setUser } from "../../features/redux/slices/AuthSlice/AuthSlice";
import {
login,
unsetOnboarding,
} from "../../features/redux/slices/StatusSlice/StatusSlice";
import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer";
import { setUser } from "../../features/redux/slices/UserSlice/UserSlice";
import { setOnboarding } from "../../features/redux/slices/StatusSlice/StatusSlice";
export default function Revalidation() {
const dispatch = useDispatch();
@ -17,14 +22,23 @@ export default function Revalidation() {
useEffect(() => {
setState("Previous session found");
TokenRefresh().then(async (response) => {
if (response[0]) {
let user_info = await UserInfo();
await dispatch(setUser(user_info));
if (!(user_info.year_level || user_info.course || user_info.semester)) {
let user_info = await UserInfo();
if (response && user_info[0]) {
dispatch(login());
dispatch(setUser(user_info[1]));
if (
!(
user_info[1].year_level ||
user_info[1].course ||
user_info[1].semester
)
) {
dispatch(setOnboarding());
await setTimeout(() => {
navigation.navigate("Onboarding");
}, 700);
} else {
dispatch(unsetOnboarding());
await setTimeout(() => {
navigation.navigate("Home");
}, 700);

View file

@ -11,6 +11,7 @@ export const colors = {
text_error: "#e32d1e",
text_success: "green",
icon_color: "white",
blue_disabled: "#C07624",
};
export const font_sizes = {
@ -98,6 +99,11 @@ const styles = StyleSheet.create({
padding: 10,
borderRadius: 8,
},
dropdown_template: {
borderRadius: 16,
width: "70%",
marginVertical: 6,
},
});
export default styles;