Fixed onboarding page

This commit is contained in:
Keannu Bernasol 2023-07-17 22:44:50 +08:00
parent 8de8e67070
commit e67485d247
8 changed files with 201 additions and 110 deletions

11
App.tsx
View file

@ -16,10 +16,10 @@ import Register from "./src/routes/Register/Register";
import Onboarding from "./src/routes/Onboarding/Onboarding"; import Onboarding from "./src/routes/Onboarding/Onboarding";
import Revalidation from "./src/routes/Revalidation/Revalidation"; import Revalidation from "./src/routes/Revalidation/Revalidation";
import Activation from "./src/routes/Activation/Activation"; import Activation from "./src/routes/Activation/Activation";
import UserInfo from "./src/routes/UserInfo/UserInfo";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { QueryClientProvider, QueryClient } from "react-query"; import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { StatusBar } from "expo-status-bar"; import { StatusBar } from "expo-status-bar";
import UserInfoPage from "./src/routes/UserInfoPage/UserInfoPage";
const Drawer = createDrawerNavigator(); const Drawer = createDrawerNavigator();
@ -54,9 +54,10 @@ export default function App() {
} }
}, [initialRoute]); }, [initialRoute]);
return ( return (
<QueryClientProvider client={queryClient}>
<Provider store={store}> <Provider store={store}>
<StatusBar style="light" /> <StatusBar style="light" />
<QueryClientProvider client={queryClient}>
<NavigationContainer linking={linking}> <NavigationContainer linking={linking}>
<Drawer.Navigator <Drawer.Navigator
initialRouteName="Revalidation" initialRouteName="Revalidation"
@ -69,10 +70,10 @@ export default function App() {
<Drawer.Screen name="Onboarding" component={Onboarding} /> <Drawer.Screen name="Onboarding" component={Onboarding} />
<Drawer.Screen name="Revalidation" component={Revalidation} /> <Drawer.Screen name="Revalidation" component={Revalidation} />
<Drawer.Screen name="Activation" component={Activation} /> <Drawer.Screen name="Activation" component={Activation} />
<Drawer.Screen name="User Info" component={UserInfo} /> <Drawer.Screen name="User Info" component={UserInfoPage} />
</Drawer.Navigator> </Drawer.Navigator>
</NavigationContainer> </NavigationContainer>
</QueryClientProvider>
</Provider> </Provider>
</QueryClientProvider>
); );
} }

View file

@ -0,0 +1,37 @@
import * as React from "react";
import { View, Text, ScrollView } from "react-native";
import styles from "../../styles";
import { colors } from "../../styles";
import { MotiView, MotiScrollView } from "moti";
export interface props {
children: React.ReactNode;
}
export default function AnimatedContainerNoScroll(props: props) {
return (
<MotiView
style={styles.container}
from={{
borderRadius: 0,
opacity: 0,
backgroundColor: colors.secondary_2,
paddingTop: 4,
paddingBottom: 4,
marginHorizontal: "4%",
marginVertical: "10%",
}}
animate={{
borderRadius: 15,
opacity: 1,
backgroundColor: colors.secondary_2,
paddingTop: 16,
paddingBottom: 16,
marginHorizontal: "4%",
marginVertical: "5%",
}}
transition={{ type: "timing", duration: 700 }}
>
{props.children}
</MotiView>
);
}

View file

@ -218,6 +218,29 @@ export async function GetYearLevels() {
}); });
} }
export async function GetSubjects(
course: string,
year_level: string,
semester: string
) {
const config = await GetConfig();
return instance
.get(
"/api/v1/subjects/" + course + "/" + year_level + "/" + semester,
config
)
.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";
return [false, error_message];
});
}
export async function OnboardingUpdateStudentInfo(info: OnboardingParams) { export async function OnboardingUpdateStudentInfo(info: OnboardingParams) {
const config = await GetConfig(); const config = await GetConfig();
return instance return instance

View file

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

View file

@ -53,7 +53,9 @@ export interface Semester {
shortname: string; shortname: string;
} }
export type SemesterParams = [boolean, Semester]; export type Semesters = Array<Semester>;
export type SemesterParams = [boolean, Semesters];
export interface YearLevel { export interface YearLevel {
id: string; id: string;

View file

@ -23,6 +23,8 @@ import {
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { unsetOnboarding } from "../../features/redux/slices/StatusSlice/StatusSlice"; import { unsetOnboarding } from "../../features/redux/slices/StatusSlice/StatusSlice";
import { setUser } from "../../features/redux/slices/UserSlice/UserSlice"; import { setUser } from "../../features/redux/slices/UserSlice/UserSlice";
import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer";
import AnimatedContainerNoScroll from "../../components/AnimatedContainer/AnimatedContainerNoScroll";
export default function Onboarding() { export default function Onboarding() {
const navigation = useNavigation<RootDrawerParamList>(); const navigation = useNavigation<RootDrawerParamList>();
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -96,7 +98,7 @@ export default function Onboarding() {
} }
return ( return (
<View style={styles.background}> <View style={styles.background}>
<View style={styles.container}> <AnimatedContainerNoScroll>
<MotiView <MotiView
from={{ opacity: 0 }} from={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
@ -146,31 +148,13 @@ export default function Onboarding() {
setValue={setSelectedCourse} setValue={setSelectedCourse}
placeholder="Choose your course" placeholder="Choose your course"
containerStyle={{ containerStyle={{
...styles.dropdown_template, ...styles.input,
...{ zIndex: 3000 }, ...{ zIndex: 3000 },
}} }}
dropDownContainerStyle={{ backgroundColor: "white" }} dropDownContainerStyle={{ backgroundColor: "white" }}
/> />
<DropDownPicker <DropDownPicker
zIndex={2000} 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} open={yearLevelOpen}
value={selected_yearlevel} value={selected_yearlevel}
items={year_levels} items={year_levels}
@ -182,19 +166,46 @@ export default function Onboarding() {
setValue={setSelectedYearLevel} setValue={setSelectedYearLevel}
placeholder="Your Year Level" placeholder="Your Year Level"
containerStyle={{ containerStyle={{
...styles.dropdown_template, ...styles.input,
...{ zIndex: 2000 },
}}
dropDownContainerStyle={{ backgroundColor: "white" }}
/>
<DropDownPicker
zIndex={1000}
open={semesterOpen}
value={selected_semester}
items={semesters}
setOpen={(open) => {
setSemesterOpen(open);
setCourseOpen(false);
setYearLevelOpen(false);
}}
setValue={setSelectedSemester}
placeholder="Current semester"
containerStyle={{
...styles.input,
...{ zIndex: 1000 }, ...{ zIndex: 1000 },
}} }}
dropDownContainerStyle={{ backgroundColor: "white" }} dropDownContainerStyle={{ backgroundColor: "white" }}
/> />
</MotiView> </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> <Text style={styles.text_white_small}>{error}</Text>
<MotiView
from={{
opacity: 0,
zIndex: -1,
}}
animate={{
opacity: 1,
zIndex: -1,
}}
transition={{ type: "timing", duration: 400, delay: 1700 }}
style={{
...styles.button_template,
...{ padding: 0, backgroundColor: colors.secondary_3 },
}}
>
<Button <Button
disabled={ disabled={
!selected_yearlevel || !selected_course || !selected_semester !selected_yearlevel || !selected_course || !selected_semester
@ -217,12 +228,11 @@ export default function Onboarding() {
setError(result[1]); setError(result[1]);
} }
}} }}
color={colors.secondary_3}
> >
<Text style={styles.text_white_small}>Proceed</Text> <Text style={styles.text_white_small}>Proceed</Text>
</Button> </Button>
</MotiView> </MotiView>
</View> </AnimatedContainerNoScroll>
</View> </View>
); );
} }

View file

@ -11,23 +11,40 @@ import { useState } from "react";
import { useNavigation } from "@react-navigation/native"; import { useNavigation } from "@react-navigation/native";
import { import {
RootDrawerParamList, RootDrawerParamList,
SemesterParams,
UserInfoParams, UserInfoParams,
Semester,
} from "../../interfaces/Interfaces"; } from "../../interfaces/Interfaces";
import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer";
import { TouchableOpacity, Image } from "react-native"; import { TouchableOpacity, Image } from "react-native";
import { ScrollView } from "react-native-gesture-handler"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import SelectDropdown from "react-native-select-dropdown";
import DropdownIcon from "../../icons/DropdownIcon/DropdownIcon";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { import {
UserInfo as GetUserInfo, GetSemesters,
GetSubjects,
PatchUserInfo, PatchUserInfo,
UserInfo,
} from "../../components/Api/Api"; } from "../../components/Api/Api";
import { colors } from "../../styles"; import { colors } from "../../styles";
import DropDownPicker from "react-native-dropdown-picker";
import AnimatedContainerNoScroll from "../../components/AnimatedContainer/AnimatedContainerNoScroll";
export default function UserInfo() { export default function UserInfoPage() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const UserInfo = useQuery("user", GetUserInfo, { const [user, setUser] = useState({
first_name: "",
last_name: "",
year_level: "",
semester: "",
course: "",
avatar: "",
});
const [displayName, setDisplayName] = useState({
first_name: "",
last_name: "",
});
const StudentInfo = useQuery({
queryKey: ["user"],
queryFn: UserInfo,
onSuccess: (data: UserInfoParams) => { onSuccess: (data: UserInfoParams) => {
setUser({ setUser({
...user, ...user,
@ -47,23 +64,29 @@ export default function UserInfo() {
const mutation = useMutation({ const mutation = useMutation({
mutationFn: PatchUserInfo, mutationFn: PatchUserInfo,
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries("user"); queryClient.invalidateQueries({ queryKey: ["user"] });
},
});
const [semesters, setSemesters] = useState([
{ label: "", value: "", shortname: "" },
]);
const Semesters = useQuery({
queryKey: ["semesters"],
queryFn: GetSemesters,
onSuccess: (data: SemesterParams) => {
let semestersData = data[1].map((semester: Semester) => ({
label: semester.name,
value: semester.id,
shortname: semester.shortname,
}));
// Update the 'semesters' state
setSemesters(semestersData);
}, },
}); });
const [isEditable, setIsEditable] = useState(false); const [isEditable, setIsEditable] = useState(false);
const subjectOptions = ["", "", "", ""]; const subjectOptions = ["", "", "", ""];
const [user, setUser] = useState({
first_name: "",
last_name: "",
year_level: "",
semester: "",
course: "",
avatar: "",
});
const [displayName, setDisplayName] = useState({
first_name: "",
last_name: "",
});
function Avatar() { function Avatar() {
if (user.avatar) { if (user.avatar) {
return <Image source={{ uri: user.avatar }} style={styles.profile} />; return <Image source={{ uri: user.avatar }} style={styles.profile} />;
@ -76,9 +99,12 @@ export default function UserInfo() {
); );
} }
} }
const [selected_subjects, setSelectedSubjects] = useState([]);
const [subjectsOpen, setSubjectsOpen] = useState(false);
const [subjects, setSubjects] = useState([{ label: "", value: "" }]);
return ( return (
<ScrollView style={styles.background}> <View style={styles.background}>
<AnimatedContainer> <AnimatedContainerNoScroll>
<Text style={styles.text_white_medium_large}> <Text style={styles.text_white_medium_large}>
{(displayName.first_name || "Undefined") + {(displayName.first_name || "Undefined") +
" " + " " +
@ -90,7 +116,7 @@ export default function UserInfo() {
<View style={styles.padding} /> <View style={styles.padding} />
<View style={styles.flex_row}> <View style={styles.flex_row}>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<Text style={styles.text}>First Name</Text> <Text style={styles.text_white_small_bold}>First Name</Text>
</View> </View>
<View style={{ flex: 3 }}> <View style={{ flex: 3 }}>
<TextInput <TextInput
@ -107,7 +133,7 @@ export default function UserInfo() {
</View> </View>
<View style={styles.flex_row}> <View style={styles.flex_row}>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<Text style={styles.text}>Last Name</Text> <Text style={styles.text_white_small_bold}>Last Name</Text>
</View> </View>
<View style={{ flex: 3 }}> <View style={{ flex: 3 }}>
<TextInput <TextInput
@ -125,7 +151,7 @@ export default function UserInfo() {
<View style={styles.padding} /> <View style={styles.padding} />
<View style={styles.flex_row}> <View style={styles.flex_row}>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<Text style={styles.text}>Year Level</Text> <Text style={styles.text_white_small_bold}>Year Level</Text>
</View> </View>
<View style={{ flex: 3 }}> <View style={{ flex: 3 }}>
<TextInput <TextInput
@ -142,7 +168,7 @@ export default function UserInfo() {
</View> </View>
<View style={styles.flex_row}> <View style={styles.flex_row}>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<Text style={styles.text}>Semester</Text> <Text style={styles.text_white_small_bold}>Semester</Text>
</View> </View>
<View style={{ flex: 3 }}> <View style={{ flex: 3 }}>
<TextInput <TextInput
@ -159,7 +185,7 @@ export default function UserInfo() {
</View> </View>
<View style={styles.flex_row}> <View style={styles.flex_row}>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<Text style={styles.text}>Course</Text> <Text style={styles.text_white_small_bold}>Course</Text>
</View> </View>
<View style={{ flex: 3 }}> <View style={{ flex: 3 }}>
<TextInput <TextInput
@ -177,27 +203,21 @@ export default function UserInfo() {
<View style={styles.padding} /> <View style={styles.padding} />
<View style={styles.flex_row}> <View style={styles.flex_row}>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<Text style={styles.text}>Subject</Text> <Text style={styles.text_white_small_bold}>Subjects</Text>
</View> </View>
<View style={{ flex: 3 }}> <View style={{ flex: 3 }}>
<SelectDropdown <DropDownPicker
onSelect={(selectedItem, index) => { multiple={true}
console.log(selectedItem, index); max={16}
}} open={subjectsOpen}
renderDropdownIcon={() => <DropdownIcon size={32} />} value={selected_subjects}
buttonTextStyle={{ items={subjects}
color: "white", setOpen={(open) => setSubjectsOpen(open)}
}} setValue={setSelectedSubjects}
dropdownStyle={{ placeholder="Subjects"
backgroundColor: colors.primary_2, style={styles.input}
}} textStyle={styles.text_white_small_bold}
data={subjectOptions} dropDownContainerStyle={{ backgroundColor: "white" }}
buttonStyle={{
width: "90%",
marginLeft: 10,
backgroundColor: colors.primary_2,
borderRadius: 8,
}}
/> />
</View> </View>
</View> </View>
@ -218,10 +238,10 @@ export default function UserInfo() {
}} }}
> >
<Text style={styles.text_white_small}> <Text style={styles.text_white_small}>
{isEditable && UserInfo.isSuccess ? "Save" : "Edit Profile"} {isEditable && StudentInfo.isSuccess ? "Save" : "Edit Profile"}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
</AnimatedContainer> </AnimatedContainerNoScroll>
</ScrollView> </View>
); );
} }

View file

@ -94,6 +94,18 @@ const styles = StyleSheet.create({
fontWeight: "bold", fontWeight: "bold",
textAlign: "center", textAlign: "center",
}, },
text_white_small_bold: {
color: colors.text_default,
fontSize: font_sizes.small,
fontWeight: "bold",
textAlign: "center",
},
text_white_tiny_bold: {
color: colors.text_default,
fontSize: font_sizes.tiny,
fontWeight: "bold",
textAlign: "center",
},
button_template: { button_template: {
justifyContent: "center", justifyContent: "center",
alignSelf: "center", alignSelf: "center",
@ -117,9 +129,10 @@ const styles = StyleSheet.create({
width: width * 0.5, width: width * 0.5,
}, },
dropdown_template: { dropdown_template: {
borderRadius: 16, backgroundColor: colors.primary_2,
containerStyle: colors.primary_2,
borderRadius: 8,
width: "70%", width: "70%",
marginVertical: 6,
}, },
map: { map: {
flex: 1, flex: 1,
@ -135,21 +148,12 @@ const styles = StyleSheet.create({
overflow: "hidden", overflow: "hidden",
}, },
input: { input: {
height: 40, marginVertical: 12,
margin: 12,
marginRight: 30,
borderWidth: 1, borderWidth: 1,
color: colors.text_default, color: colors.text_default,
backgroundColor: colors.primary_2, backgroundColor: colors.primary_2,
borderRadius: 8, borderRadius: 8,
borderColor: colors.primary_3, borderColor: colors.primary_3,
padding: 8,
},
text: {
marginLeft: 5,
color: colors.text_default,
fontSize: font_sizes.small,
fontWeight: "bold",
}, },
padding: { padding: {
paddingVertical: 8, paddingVertical: 8,