diff --git a/App.tsx b/App.tsx
index 66975af..bf69a4b 100644
--- a/App.tsx
+++ b/App.tsx
@@ -16,9 +16,11 @@ import Register from "./src/routes/Register/Register";
import Onboarding from "./src/routes/Onboarding/Onboarding";
import Revalidation from "./src/routes/Revalidation/Revalidation";
import Activation from "./src/routes/Activation/Activation";
-import UserInfo from "./src/routes/UserInfo/UserInfo";
import { useState, useEffect } from "react";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
+import { StatusBar } from "expo-status-bar";
+import UserInfoPage from "./src/routes/UserInfoPage/UserInfoPage";
+import SubjectsPage from "./src/routes/SubjectsPage/SubjectsPage";
const Drawer = createDrawerNavigator();
@@ -53,8 +55,10 @@ export default function App() {
}
}, [initialRoute]);
return (
-
-
+
+
+
+
-
+
+
-
-
+
+
);
}
diff --git a/app.json b/app.json
index 4802c78..9e8bd9c 100644
--- a/app.json
+++ b/app.json
@@ -1,6 +1,6 @@
{
"expo": {
- "name": "StudE_Frontend",
+ "name": "StudE",
"scheme": "stude",
"slug": "StudE_Frontend",
"version": "1.0.0",
@@ -10,7 +10,7 @@
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
- "backgroundColor": "#ffffff"
+ "backgroundColor": "#1C2C3F"
},
"assetBundlePatterns": [
"**/*"
@@ -21,8 +21,14 @@
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
- "backgroundColor": "#ffffff"
- }
+ "backgroundColor": "#FFFF"
+ },
+ "package": "com.teamblackpink.stude",
+ "permissions": [
+ "android.permission.ACCESS_COARSE_LOCATION",
+ "android.permission.ACCESS_FINE_LOCATION",
+ "android.permission.FOREGROUND_SERVICE"
+ ]
},
"web": {
"favicon": "./assets/favicon.png"
@@ -34,6 +40,11 @@
"locationAlwaysAndWhenInUsePermission": "Allow Stud-E to use your location."
}
]
- ]
+ ],
+ "extra": {
+ "eas": {
+ "projectId": "614fd93f-345c-4d72-a9e7-592f1ba0c6e8"
+ }
+ }
}
}
diff --git a/assets/adaptive-icon.png b/assets/adaptive-icon.png
index 03d6f6b..ebae40b 100644
Binary files a/assets/adaptive-icon.png and b/assets/adaptive-icon.png differ
diff --git a/assets/favicon.png b/assets/favicon.png
index e75f697..c34a8f6 100644
Binary files a/assets/favicon.png and b/assets/favicon.png differ
diff --git a/assets/icon.png b/assets/icon.png
index a0b1526..af59510 100644
Binary files a/assets/icon.png and b/assets/icon.png differ
diff --git a/assets/splash.png b/assets/splash.png
index 0e89705..c933124 100644
Binary files a/assets/splash.png and b/assets/splash.png differ
diff --git a/eas.json b/eas.json
new file mode 100644
index 0000000..02060ed
--- /dev/null
+++ b/eas.json
@@ -0,0 +1,19 @@
+{
+ "build": {
+ "preview": {
+ "android": {
+ "buildType": "apk"
+ }
+ },
+ "preview2": {
+ "android": {
+ "gradleCommand": ":app:assembleRelease"
+ }
+ },
+ "preview3": {
+ "developmentClient": true
+ },
+ "production": {}
+ }
+ }
+
\ No newline at end of file
diff --git a/package.json b/package.json
index 60f4b66..9dbbe39 100644
--- a/package.json
+++ b/package.json
@@ -33,10 +33,10 @@
"react-native-safe-area-context": "4.5.0",
"react-native-screens": "~3.20.0",
"react-native-select-dropdown": "^3.3.4",
- "react-native-svg": "13.4.0",
"react-query": "^3.39.3",
"react-redux": "^8.1.1",
- "redux": "^4.2.1"
+ "redux": "^4.2.1",
+ "react-native-svg": "13.4.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
diff --git a/src/components/AnimatedContainer/AnimatedContainer.tsx b/src/components/AnimatedContainer/AnimatedContainer.tsx
index 8f5960d..1a92823 100644
--- a/src/components/AnimatedContainer/AnimatedContainer.tsx
+++ b/src/components/AnimatedContainer/AnimatedContainer.tsx
@@ -13,21 +13,23 @@ export default function AnimatedContainer(props: props) {
contentContainerStyle={styles.container}
from={{
borderRadius: 0,
- backgroundColor: colors.orange_2,
+ opacity: 0,
+ backgroundColor: colors.secondary_2,
paddingTop: 4,
paddingBottom: 4,
marginHorizontal: "4%",
- marginVertical: "5%",
+ marginVertical: "10%",
}}
animate={{
borderRadius: 15,
- backgroundColor: colors.blue_2,
+ opacity: 1,
+ backgroundColor: colors.secondary_2,
paddingTop: 16,
paddingBottom: 16,
marginHorizontal: "4%",
marginVertical: "5%",
}}
- transition={{ type: "timing", duration: 300 }}
+ transition={{ type: "timing", duration: 700 }}
>
{props.children}
diff --git a/src/components/AnimatedContainer/AnimatedContainerNoScroll.tsx b/src/components/AnimatedContainer/AnimatedContainerNoScroll.tsx
new file mode 100644
index 0000000..a3f7c35
--- /dev/null
+++ b/src/components/AnimatedContainer/AnimatedContainerNoScroll.tsx
@@ -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 (
+
+ {props.children}
+
+ );
+}
diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx
index 5a5ddcb..58cd659 100644
--- a/src/components/Api/Api.tsx
+++ b/src/components/Api/Api.tsx
@@ -4,7 +4,9 @@ import {
ActivationParams,
LoginParams,
OnboardingParams,
+ PatchStudentData,
RegistrationParams,
+ StudentData,
} from "../../interfaces/Interfaces";
let debug = true;
@@ -43,6 +45,16 @@ export async function setRefreshToken(refresh: string) {
return true;
}
+// Header Config Template for REST
+export async function GetConfig() {
+ const accessToken = await getAccessToken();
+ return {
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ },
+ };
+}
+
// User APIs
export function UserRegister(register: RegistrationParams) {
console.log(JSON.stringify(register));
@@ -77,6 +89,7 @@ export function UserLogin(user: LoginParams) {
let error_message = "";
if (error.response) error_message = error.response.data;
else error_message = "Unable to reach servers";
+ // console.log(error_message);
return [false, error_message];
});
}
@@ -105,13 +118,9 @@ export async function TokenRefresh() {
});
}
export async function UserInfo() {
- const accessToken = await getAccessToken();
+ const config = await GetConfig();
return instance
- .get("/api/v1/accounts/users/me/", {
- headers: {
- Authorization: `Bearer ${accessToken}`,
- },
- })
+ .get("/api/v1/accounts/users/me/", config)
.then((response) => {
// console.log(JSON.stringify(response.data));
return [true, response.data];
@@ -124,6 +133,23 @@ export async function UserInfo() {
});
}
+export async function PatchUserInfo(info: PatchStudentData) {
+ const config = await GetConfig();
+ return instance
+ .patch("/api/v1/accounts/users/me/", info, 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";
+ // console.log(error_message);
+ return [false, error_message];
+ });
+}
+
export function UserActivate(activation: ActivationParams) {
return instance
.post("/api/v1/accounts/users/activation/", activation)
@@ -147,14 +173,13 @@ export async function GetCourses() {
})
.then((response) => {
// console.log(JSON.stringify(response.data));
- return 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 getting courses", error_message);
- return false;
+ return [false, error_message];
});
}
@@ -168,45 +193,59 @@ export async function GetSemesters() {
})
.then((response) => {
// console.log(JSON.stringify(response.data));
- return 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 getting semesters", error_message);
- return false;
+ return [false, error_message];
});
}
export async function GetYearLevels() {
- const accessToken = await getAccessToken();
+ const config = await GetConfig();
return instance
- .get("/api/v1/year_levels/", {
- headers: {
- Authorization: `Bearer ${accessToken}`,
- },
- })
+ .get("/api/v1/year_levels/", config)
.then((response) => {
// console.log(JSON.stringify(response.data));
- return 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 getting year levels", error_message);
- return false;
+ return [false, error_message];
+ });
+}
+
+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) {
- const accessToken = await getAccessToken();
- const headers = {
- Authorization: `Bearer ${accessToken}`,
- };
+ const config = await GetConfig();
return instance
- .patch("/api/v1/accounts/users/me/", info, { headers })
+ .patch("/api/v1/accounts/users/me/", info, config)
.then((response) => {
console.log(JSON.stringify(response.data));
return [true, response.data];
diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx
index 43aaf84..ee36630 100644
--- a/src/components/Button/Button.tsx
+++ b/src/components/Button/Button.tsx
@@ -1,30 +1,23 @@
import * as React from "react";
import { Pressable, GestureResponderEvent } from "react-native";
import styles from "../../styles";
-
+import { colors } from "../../styles";
export interface props {
children: React.ReactNode;
onPress: (event: GestureResponderEvent) => void;
- color: string;
+ color?: string;
disabled?: boolean;
}
export default function Button({ disabled = false, ...props }: props) {
- const rgb = props.color.match(/\d+/g);
+ if (!props.color) {
+ props.color = colors.secondary_3;
+ }
return (
{props.children}
diff --git a/src/components/Button/DrawerButton.tsx b/src/components/Button/DrawerButton.tsx
index 5708d6e..8f81da3 100644
--- a/src/components/Button/DrawerButton.tsx
+++ b/src/components/Button/DrawerButton.tsx
@@ -1,5 +1,5 @@
import * as React from "react";
-import { Text, Pressable, GestureResponderEvent } from "react-native";
+import { Pressable, GestureResponderEvent } from "react-native";
import styles from "../../styles";
export interface props {
diff --git a/src/components/DrawerSettings/CustomDrawerContent.tsx b/src/components/DrawerSettings/CustomDrawerContent.tsx
index 845d732..49c1054 100644
--- a/src/components/DrawerSettings/CustomDrawerContent.tsx
+++ b/src/components/DrawerSettings/CustomDrawerContent.tsx
@@ -31,12 +31,12 @@ export default function CustomDrawerContent(props: {}) {
...{ justifyContent: "center" },
}}
>
-
+
Stud-E
{
dispatch(logout());
await AsyncStorage.clear();
@@ -57,11 +57,11 @@ export default function CustomDrawerContent(props: {}) {
...{ justifyContent: "center" },
}}
>
-
+
Stud-E
{
navigation.navigate("Home");
}}
@@ -70,16 +70,25 @@ export default function CustomDrawerContent(props: {}) {
Home
{
- navigation.navigate("UserInfo");
+ navigation.navigate("User Info");
}}
>
- UserInfo
+ User Info
{
+ navigation.navigate("Subjects");
+ }}
+ >
+
+ Subjects
+
+ {
dispatch(logout());
await AsyncStorage.clear();
@@ -100,11 +109,11 @@ export default function CustomDrawerContent(props: {}) {
...{ justifyContent: "center" },
}}
>
-
+
Stud-E
{
navigation.navigate("Login");
}}
@@ -113,7 +122,7 @@ export default function CustomDrawerContent(props: {}) {
Login
{
navigation.navigate("Register");
}}
@@ -125,7 +134,7 @@ export default function CustomDrawerContent(props: {}) {
{/*
Debug buttons for accessing revalidation and activation page
{
navigation.navigate("Revalidation");
}}
@@ -133,7 +142,7 @@ export default function CustomDrawerContent(props: {}) {
Revalidation
{
navigation.navigate("Activation");
}}
diff --git a/src/components/DrawerSettings/DrawerScreenSettings.tsx b/src/components/DrawerSettings/DrawerScreenSettings.tsx
index 2a3ec73..3aaf1c7 100644
--- a/src/components/DrawerSettings/DrawerScreenSettings.tsx
+++ b/src/components/DrawerSettings/DrawerScreenSettings.tsx
@@ -10,21 +10,19 @@ const DrawerScreenSettings: DrawerNavigationOptions = {
fontSize: font_sizes.medium,
},
unmountOnBlur: true,
- headerStyle: { backgroundColor: colors.login_color},
+ headerStyle: { backgroundColor: colors.primary_1 },
headerTintColor: colors.text_default,
drawerType: "slide",
drawerLabelStyle: {
color: colors.text_default,
},
drawerStyle: {
- backgroundColor: colors.login_color,
+ backgroundColor: colors.primary_1,
width: 260,
},
headerRight: () => (
-
-
+
+
),
};
diff --git a/src/icons/AppIcon/AppIcon.tsx b/src/icons/AppIcon/AppIcon.tsx
index 8901270..ef45c52 100644
--- a/src/icons/AppIcon/AppIcon.tsx
+++ b/src/icons/AppIcon/AppIcon.tsx
@@ -1,20 +1,13 @@
import * as React from "react";
import { IconProps } from "../../interfaces/Interfaces";
-
-import { Svg, Path } from "react-native-svg";
-import { colors } from "../../styles";
-
+import { Image } from "react-native";
export default function AppIcon(props: IconProps) {
return (
<>
-
+
>
);
}
diff --git a/src/icons/DropdownIcon/DropdownIcon.tsx b/src/icons/DropdownIcon/DropdownIcon.tsx
index 224919e..27851ab 100644
--- a/src/icons/DropdownIcon/DropdownIcon.tsx
+++ b/src/icons/DropdownIcon/DropdownIcon.tsx
@@ -1,9 +1,7 @@
import * as React from "react";
import { IconProps } from "../../interfaces/Interfaces";
-
import { Svg, Path } from "react-native-svg";
import { colors } from "../../styles";
-import finalPropsSelectorFactory from "react-redux/es/connect/selectorFactory";
export default function DropdownIcon(props: IconProps) {
return (
diff --git a/src/img/app_icon_dark.png b/src/img/app_icon_dark.png
new file mode 100644
index 0000000..a7e0cd0
Binary files /dev/null and b/src/img/app_icon_dark.png differ
diff --git a/src/img/app_icon_light.png b/src/img/app_icon_light.png
new file mode 100644
index 0000000..22e9cec
Binary files /dev/null and b/src/img/app_icon_light.png differ
diff --git a/src/routes/UserInfo/image/3135715.png b/src/img/user_profile_placeholder.png
similarity index 100%
rename from src/routes/UserInfo/image/3135715.png
rename to src/img/user_profile_placeholder.png
diff --git a/src/interfaces/Interfaces.tsx b/src/interfaces/Interfaces.tsx
index d9671ec..2b6e526 100644
--- a/src/interfaces/Interfaces.tsx
+++ b/src/interfaces/Interfaces.tsx
@@ -47,26 +47,86 @@ export interface ActivationParams {
token: string;
}
-export interface SemesterParams {
+export interface OptionType {
+ label: string;
+ value: string;
+}
+
+// Semester
+export interface Semester {
id: string;
name: string;
shortname: string;
}
-export interface YearLevelParams {
+export type Semesters = Array;
+
+export type SemesterParams = [boolean, Semesters];
+
+// Year Level
+export interface YearLevel {
id: string;
name: string;
shortname: string;
}
-export interface CourseParams {
+export type YearLevels = Array;
+
+export type YearLevelParams = [boolean, YearLevels];
+
+// Course
+export interface Course {
id: string;
name: string;
shortname: string;
}
+export type Courses = Array;
+export type CourseParams = [boolean, Courses];
+
+// Subject
+export interface Subject {
+ name: string;
+ code: string;
+ // courses: any[]; // To-do
+ // year_levels: any[]; // To-do
+ // semesters: any[]; // To-do
+}
+
+export type Subjects = Array;
+export type SubjectParams = [boolean, Subjects];
+
+// For dropdown menu
export interface OnboardingParams {
year_level: string;
course: string;
semester: string;
}
+
+export interface PatchStudentData {
+ course?: string | null;
+ first_name?: string | null;
+ last_name?: string | null;
+ semester?: string | null;
+ subjects?: any[] | null; // To-do, replace 'any' with your actual type
+ year_level?: string | null;
+}
+
+export interface StudentData {
+ first_name: string;
+ last_name: string;
+ email: string;
+ avatar: string;
+ student_id_number: string;
+ is_banned: boolean;
+ semester: string;
+ semester_shortname: string;
+ course: string;
+ course_shortname: string;
+ year_level: string;
+ yearlevel_shortname: string;
+ subjects: any[]; // To-do
+ username: string;
+}
+
+export type UserInfoParams = [boolean, StudentData];
diff --git a/src/routes/Activation/Activation.tsx b/src/routes/Activation/Activation.tsx
index dce7f24..10aab6a 100644
--- a/src/routes/Activation/Activation.tsx
+++ b/src/routes/Activation/Activation.tsx
@@ -53,7 +53,7 @@ export default function Activation() {
marginBottom: 16,
borderRadius: 4,
width: "90%",
- backgroundColor: colors.blue_1,
+ backgroundColor: colors.secondary_1,
}}
/>
Activation
@@ -61,7 +61,7 @@ export default function Activation() {
{state}
{uid + "\n" + token}
diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx
index 5805287..7cc911a 100644
--- a/src/routes/Home/Home.tsx
+++ b/src/routes/Home/Home.tsx
@@ -1,4 +1,4 @@
-import styles from "../../styles";
+import styles, { Viewport } from "../../styles";
import { View, Text } from "react-native";
import { useSelector } from "react-redux";
import { RootState } from "../../features/redux/Store/Store";
@@ -9,7 +9,6 @@ import * as Location from "expo-location";
import GetDistance from "../../components/GetDistance/GetDistance";
import Button from "../../components/Button/Button";
import { colors } from "../../styles";
-import { startActivityAsync, ActivityAction } from "expo-intent-launcher";
type LocationType = Location.LocationObject;
export default function Home() {
@@ -18,47 +17,59 @@ export default function Home() {
const [feedback, setFeedback] = useState(
"To continue, please allow Stud-E permission to location services"
);
-
- async function requestLocation() {
- let { status } = await Location.requestForegroundPermissionsAsync();
- if (status === "granted") {
- getLocation();
- return;
- } else if (status === "denied") {
- setFeedback("Stud-E requires location services to function");
- setTimeout(() => {
- startActivityAsync(ActivityAction.LOCATION_SOURCE_SETTINGS);
- }, 3000);
- console.log("Location Permission denied");
- }
- }
-
- async function getLocation() {
- let location = await Location.getCurrentPositionAsync({});
- setLocation(location);
- let dist = GetDistance(
- location.coords.latitude,
- location.coords.longitude,
- 8.4857,
- 124.6565
- );
- setDist(Math.round(dist));
- }
- useEffect(() => {
- requestLocation();
- }, []);
-
const ustpCoords = {
latitude: 8.4857,
longitude: 124.6565,
latitudeDelta: 0.000235,
longitudeDelta: 0.000067,
};
+
+ async function requestLocation() {
+ let { status } = await Location.requestForegroundPermissionsAsync();
+ if (status !== "granted") {
+ setFeedback(
+ "Permission to access location was denied. Please allow permission"
+ );
+ return;
+ }
+ if (status == "granted") {
+ let location = await Location.getCurrentPositionAsync({});
+ if (location) {
+ setLocation(location);
+ getDistance(location);
+ }
+ }
+ }
+ useEffect(() => {
+ requestLocation();
+ }, [location]);
+
+ async function getDistance(location: LocationType) {
+ let dist = GetDistance(
+ location.coords.latitude,
+ location.coords.longitude,
+ 8.4857, // LatitudeDelta
+ 124.6565 // LongitudeDelta
+ );
+ setDist(Math.round(dist));
+ }
+
function CustomMap() {
- if (dist !== null && location !== null) {
+ if (dist && location) {
if (dist <= 1.5) {
// Just switch this condition for map debugging
- return ;
+ return (
+
+ );
} else {
return (
@@ -68,8 +79,8 @@ export default function Home() {
{feedback}
-
diff --git a/src/routes/Revalidation/Revalidation.tsx b/src/routes/Revalidation/Revalidation.tsx
index e2f099e..ce5b1dc 100644
--- a/src/routes/Revalidation/Revalidation.tsx
+++ b/src/routes/Revalidation/Revalidation.tsx
@@ -58,7 +58,7 @@ export default function Revalidation() {
-
+
{state}
diff --git a/src/routes/SubjectsPage/SubjectsPage.tsx b/src/routes/SubjectsPage/SubjectsPage.tsx
new file mode 100644
index 0000000..51d36cb
--- /dev/null
+++ b/src/routes/SubjectsPage/SubjectsPage.tsx
@@ -0,0 +1,223 @@
+import * as React from "react";
+import styles from "../../styles";
+import {
+ View,
+ Text,
+ TextInput,
+ NativeSyntheticEvent,
+ TextInputChangeEventData,
+} from "react-native";
+import { useState } from "react";
+import {
+ SemesterParams,
+ UserInfoParams,
+ Semester,
+ SubjectParams,
+ Subject,
+ YearLevel,
+ Course,
+ OptionType,
+ Subjects,
+} from "../../interfaces/Interfaces";
+import Button from "../../components/Button/Button";
+import { Image } from "react-native";
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
+import {
+ GetCourses,
+ GetSemesters,
+ GetSubjects,
+ GetYearLevels,
+ PatchUserInfo,
+ UserInfo,
+} from "../../components/Api/Api";
+import { colors } from "../../styles";
+import DropDownPicker from "react-native-dropdown-picker";
+import { ValueType } from "react-native-dropdown-picker";
+import AnimatedContainerNoScroll from "../../components/AnimatedContainer/AnimatedContainerNoScroll";
+
+export default function SubjectsPage() {
+ const queryClient = useQueryClient();
+ // User Info
+ const [user, setUser] = useState({
+ first_name: "",
+ last_name: "",
+ year_level: "",
+ yearlevel_shortname: "",
+ semester: "",
+ semester_shortname: "",
+ course: "",
+ course_shortname: "",
+ avatar: "",
+ student_id_number: "",
+ subjects: [] as Subjects,
+ });
+ const [displayName, setDisplayName] = useState({
+ first_name: "",
+ last_name: "",
+ });
+ const StudentInfo = useQuery({
+ queryKey: ["user"],
+ queryFn: UserInfo,
+ onSuccess: (data: UserInfoParams) => {
+ // console.log(data[1]);
+ setUser({
+ ...user,
+ first_name: data[1].first_name,
+ last_name: data[1].last_name,
+ year_level: data[1].year_level,
+ semester: data[1].semester,
+ course: data[1].course,
+ avatar: data[1].avatar,
+ student_id_number: data[1].student_id_number,
+ subjects: data[1].subjects,
+ });
+ setDisplayName({
+ first_name: data[1].first_name,
+ last_name: data[1].last_name,
+ });
+ setSelectedSubjects(user.subjects);
+ },
+ });
+ const mutation = useMutation({
+ mutationFn: PatchUserInfo,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["user"] });
+ queryClient.invalidateQueries({ queryKey: ["subjects"] });
+ setSelectedSubjects([]);
+ },
+ });
+
+ // Subjects
+
+ const [selected_subjects, setSelectedSubjects] = useState([]);
+ const [subjectsOpen, setSubjectsOpen] = useState(false);
+ const [subjects, setSubjects] = useState([]);
+
+ const Subjects = useQuery({
+ enabled: StudentInfo.isFetched,
+ queryKey: ["subjects"],
+ queryFn: async () => {
+ let data;
+ if (StudentInfo.data) {
+ if (
+ StudentInfo.data[1].course_shortname &&
+ StudentInfo.data[1].yearlevel_shortname &&
+ StudentInfo.data[1].semester_shortname
+ ) {
+ data = await GetSubjects(
+ StudentInfo.data[1].course_shortname,
+ StudentInfo.data[1].yearlevel_shortname,
+ StudentInfo.data[1].semester_shortname
+ );
+ }
+ }
+ if (data) {
+ if (!data[0]) {
+ throw new Error("Error with query" + data[1]);
+ }
+ if (!data[1]) {
+ throw new Error("User has no course, year level, or semester!");
+ }
+ // console.log("Subjects available:", data[1]);
+ }
+ return data;
+ },
+ onSuccess: (data: SubjectParams) => {
+ let subjectsData = data[1].map((subject: Subject) => ({
+ label: subject.name,
+ value: subject.name,
+ }));
+ // Update the 'subjects' state
+ setSelectedSubjects(user.subjects);
+ setSubjects(subjectsData);
+ },
+ });
+
+ // Profile photo
+ function Avatar() {
+ if (user.avatar) {
+ return ;
+ } else {
+ return (
+
+ );
+ }
+ }
+
+ return (
+
+
+
+
+
+ {(displayName.first_name || "Undefined") +
+ " " +
+ (displayName.last_name || "User") +
+ "\n" +
+ user.student_id_number}
+
+
+
+
+
+
+ Subjects
+
+
+ {
+ setSubjectsOpen(open);
+ }}
+ setValue={setSelectedSubjects}
+ placeholderStyle={{
+ ...styles.text_white_tiny_bold,
+ ...{ textAlign: "left" },
+ }}
+ placeholder="Select subjects"
+ multipleText="Select subjects"
+ style={styles.input}
+ textStyle={{
+ ...styles.text_white_tiny_bold,
+ ...{ textAlign: "left" },
+ }}
+ dropDownContainerStyle={{
+ backgroundColor: colors.primary_2,
+ borderWidth: 0,
+ zIndex: 1000,
+ maxHeight: 512,
+ }}
+ dropDownDirection="TOP"
+ />
+
+
+
+ {
+ if (subjectsOpen) {
+ setSelectedSubjects([]);
+ setSubjectsOpen(false);
+ mutation.mutate({
+ subjects: selected_subjects,
+ });
+ }
+ }}
+ >
+
+ {subjectsOpen && StudentInfo.isSuccess ? "Save" : "Edit Subjects"}
+
+
+
+
+
+ );
+}
diff --git a/src/routes/UserInfo/UserInfo.tsx b/src/routes/UserInfo/UserInfo.tsx
deleted file mode 100644
index 856f817..0000000
--- a/src/routes/UserInfo/UserInfo.tsx
+++ /dev/null
@@ -1,216 +0,0 @@
-import * as React from "react";
-import styles from "../../styles";
-import {
- View,
- Text,
- TextInput,
- NativeSyntheticEvent,
- TextInputChangeEventData,
-} from "react-native";
-import { colors } from "../../styles";
-import { useState, useEffect } from "react";
-import Button from "../../components/Button/Button";
-import { useNavigation } from "@react-navigation/native";
-import { RootDrawerParamList } from "../../interfaces/Interfaces";
-import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer";
-import { TouchableOpacity, Image } from "react-native";
-import { ScrollView } from "react-native-gesture-handler";
-import SelectDropdown from "react-native-select-dropdown";
-import DropdownIcon from "../../icons/DropdownIcon/DropdownIcon";
-import { useQuery } from "react-query";
-import { UserInfo as GetUserInfo } from "../../components/Api/Api";
-import { err } from "react-native-svg/lib/typescript/xml";
-
-export default function UserInfo() {
- const navigation = useNavigation();
- const [isEditable, setIsEditable] = useState(false);
- const options = ["", "", "", ""];
- const [isActive, setIsActive] = useState(false);
- const toggleUserActive = () => {
- setIsActive(!isActive);
- };
- //const dispatch = useDispatch();
- // const creds = useSelector((state: RootState) => state.auth.creds);
- const [user, setUser] = useState({
- first_name: "",
- last_name: "",
- year_level: "",
- semester: "",
- course: "",
- });
- const { data, isLoading, error } = useQuery("user", UserInfo, {
- retry: 0,
- onSuccess: (data) => console.log(data),
- });
- if (!isLoading && !error) {
- return (
-
-
-
- Kurt Toledo
-
-
-
-
-
- Student {isActive ? "Active" : "Inactive"}
-
-
-
-
-
-
- First Name
-
-
-
- ): void => {
- setUser({ ...user, first_name: e.nativeEvent.text });
- }}
- />
-
-
-
-
- Last Name
-
-
-
- ): void => {
- setUser({ ...user, first_name: e.nativeEvent.text });
- }}
- />
-
-
-
-
-
- Year Level
-
-
-
- ): void => {
- setUser({ ...user, first_name: e.nativeEvent.text });
- }}
- />
-
-
-
-
- Semester
-
-
-
- ): void => {
- setUser({ ...user, first_name: e.nativeEvent.text });
- }}
- />
-
-
-
-
- Course
-
-
-
- ): void => {
- setUser({ ...user, first_name: e.nativeEvent.text });
- }}
- />
-
-
-
-
-
- Subject
-
-
- {
- console.log(selectedItem, index);
- }}
- renderDropdownIcon={() => }
- buttonTextStyle={{
- color: "white",
- }}
- dropdownStyle={{
- backgroundColor: "#E3963E",
- }}
- data={options}
- buttonStyle={{
- width: "90%",
- marginLeft: 10,
- backgroundColor: "#E3963E",
- borderRadius: 8,
- }}
- />
-
-
- setIsEditable(!isEditable)}
- >
-
- {isEditable ? "Save" : "Edit Profile"}
-
-
-
-
- );
- }
-}
diff --git a/src/routes/UserInfoPage/UserInfoPage.tsx b/src/routes/UserInfoPage/UserInfoPage.tsx
new file mode 100644
index 0000000..5f39149
--- /dev/null
+++ b/src/routes/UserInfoPage/UserInfoPage.tsx
@@ -0,0 +1,339 @@
+import * as React from "react";
+import styles from "../../styles";
+import {
+ View,
+ Text,
+ TextInput,
+ NativeSyntheticEvent,
+ TextInputChangeEventData,
+} from "react-native";
+import { useState } from "react";
+import {
+ SemesterParams,
+ UserInfoParams,
+ Semester,
+ SubjectParams,
+ Subject,
+ YearLevel,
+ Course,
+ OptionType,
+ Subjects,
+} from "../../interfaces/Interfaces";
+import Button from "../../components/Button/Button";
+import { Image } from "react-native";
+import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
+import {
+ GetCourses,
+ GetSemesters,
+ GetSubjects,
+ GetYearLevels,
+ PatchUserInfo,
+ UserInfo,
+} from "../../components/Api/Api";
+import { colors } from "../../styles";
+import DropDownPicker from "react-native-dropdown-picker";
+import { ValueType } from "react-native-dropdown-picker";
+import AnimatedContainerNoScroll from "../../components/AnimatedContainer/AnimatedContainerNoScroll";
+
+export default function UserInfoPage() {
+ const queryClient = useQueryClient();
+ // User Info
+ const [user, setUser] = useState({
+ first_name: "",
+ last_name: "",
+ year_level: "",
+ yearlevel_shortname: "",
+ semester: "",
+ semester_shortname: "",
+ course: "",
+ course_shortname: "",
+ avatar: "",
+ student_id_number: "",
+ });
+ const [displayName, setDisplayName] = useState({
+ first_name: "",
+ last_name: "",
+ });
+ const StudentInfo = useQuery({
+ queryKey: ["user"],
+ queryFn: UserInfo,
+ onSuccess: (data: UserInfoParams) => {
+ // console.log(data[1]);
+ setUser({
+ ...user,
+ first_name: data[1].first_name,
+ last_name: data[1].last_name,
+ year_level: data[1].year_level,
+ semester: data[1].semester,
+ course: data[1].course,
+ avatar: data[1].avatar,
+ student_id_number: data[1].student_id_number,
+ });
+ setDisplayName({
+ first_name: data[1].first_name,
+ last_name: data[1].last_name,
+ });
+ setSelectedCourse(data[1].course);
+ setSelectedSemester(data[1].semester);
+ setSelectedYearLevel(data[1].year_level);
+ },
+ });
+ const mutation = useMutation({
+ mutationFn: PatchUserInfo,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["user"] });
+ queryClient.invalidateQueries({ queryKey: ["subjects"] });
+ },
+ });
+
+ // Semester
+ const [selected_semester, setSelectedSemester] = useState("");
+ const [semesterOpen, setSemesterOpen] = useState(false);
+ const [semesters, setSemesters] = useState([]);
+ const Semesters = useQuery({
+ queryKey: ["semesters"],
+ queryFn: GetSemesters,
+ onSuccess: (data: SemesterParams) => {
+ let semestersData = data[1].map((semester: Semester) => ({
+ label: semester.name,
+ value: semester.name,
+ shortname: semester.shortname,
+ }));
+ // Update the 'semesters' state
+ setSemesters(semestersData);
+ },
+ });
+
+ // Year Level
+ const [selected_yearlevel, setSelectedYearLevel] = useState("");
+ const [yearLevelOpen, setYearLevelOpen] = useState(false);
+ const [year_levels, setYearLevels] = useState([]);
+ const yearlevel_query = useQuery({
+ queryKey: ["year_levels"],
+ queryFn: GetYearLevels,
+ onSuccess: (data) => {
+ let year_levels = data[1].map((yearlevel: YearLevel) => ({
+ label: yearlevel.name,
+ value: yearlevel.name,
+ }));
+ setYearLevels(year_levels);
+ },
+ });
+
+ // Course
+ const [selected_course, setSelectedCourse] = useState("");
+ const [courseOpen, setCourseOpen] = useState(false);
+ const [courses, setCourses] = useState([]);
+ const course_query = useQuery({
+ queryKey: ["courses"],
+ queryFn: GetCourses,
+ onSuccess: (data) => {
+ let courses = data[1].map((course: Course) => ({
+ label: course.name,
+ value: course.name,
+ }));
+ setCourses(courses);
+ },
+ });
+ // Toggle editing of profile
+ const [isEditable, setIsEditable] = useState(false);
+ // Profile photo
+ function Avatar() {
+ if (user.avatar) {
+ return ;
+ } else {
+ return (
+
+ );
+ }
+ }
+
+ return (
+
+
+
+
+
+ {(displayName.first_name || "Undefined") +
+ " " +
+ (displayName.last_name || "User") +
+ "\n" +
+ user.student_id_number}
+
+
+
+
+
+
+ First Name
+
+
+
+ ): void => {
+ setUser({ ...user, first_name: e.nativeEvent.text });
+ }}
+ value={user.first_name}
+ />
+
+
+
+
+ Last Name
+
+
+
+ ): void => {
+ setUser({ ...user, last_name: e.nativeEvent.text });
+ }}
+ value={user.last_name}
+ />
+
+
+
+
+ Year Level
+
+
+ {
+ setYearLevelOpen(open);
+ setSemesterOpen(false);
+ setCourseOpen(false);
+ }}
+ setValue={setSelectedYearLevel}
+ placeholder={user.year_level}
+ placeholderStyle={{
+ ...styles.text_white_tiny_bold,
+ ...{ textAlign: "left" },
+ }}
+ style={styles.input}
+ textStyle={{
+ ...styles.text_white_tiny_bold,
+ ...{ textAlign: "left" },
+ }}
+ dropDownContainerStyle={{
+ backgroundColor: colors.primary_2,
+ zIndex: 4000,
+ borderWidth: 0,
+ }}
+ dropDownDirection="TOP"
+ />
+
+
+
+
+ Semester
+
+
+ {
+ setYearLevelOpen(false);
+ setSemesterOpen(open);
+ setCourseOpen(false);
+ }}
+ setValue={setSelectedSemester}
+ placeholder={user.semester}
+ placeholderStyle={{
+ ...styles.text_white_tiny_bold,
+ ...{ textAlign: "left" },
+ }}
+ style={styles.input}
+ textStyle={{
+ ...styles.text_white_tiny_bold,
+ ...{ textAlign: "left" },
+ }}
+ dropDownContainerStyle={{
+ backgroundColor: colors.primary_2,
+ zIndex: 3000,
+ borderWidth: 0,
+ }}
+ dropDownDirection="TOP"
+ />
+
+
+
+
+ Course
+
+
+ {
+ setYearLevelOpen(false);
+ setSemesterOpen(false);
+ setCourseOpen(open);
+ }}
+ setValue={setSelectedCourse}
+ placeholder={user.course}
+ placeholderStyle={{
+ ...styles.text_white_tiny_bold,
+ ...{ textAlign: "left" },
+ }}
+ style={styles.input}
+ textStyle={{
+ ...styles.text_white_tiny_bold,
+ ...{ textAlign: "left" },
+ }}
+ dropDownContainerStyle={{
+ backgroundColor: colors.primary_2,
+ zIndex: 2000,
+ borderWidth: 0,
+ }}
+ dropDownDirection="TOP"
+ />
+
+
+
+
+ {
+ if (isEditable) {
+ setYearLevelOpen(false);
+ setSemesterOpen(false);
+ setCourseOpen(false);
+ mutation.mutate({
+ first_name: user.first_name,
+ last_name: user.last_name,
+ course: selected_course,
+ semester: selected_semester,
+ year_level: selected_yearlevel,
+ });
+ }
+ setIsEditable(!isEditable);
+ }}
+ >
+
+ {isEditable && StudentInfo.isSuccess ? "Save" : "Edit Profile"}
+
+
+
+
+
+ );
+}
diff --git a/src/styles.tsx b/src/styles.tsx
index 88d451e..0767444 100644
--- a/src/styles.tsx
+++ b/src/styles.tsx
@@ -1,39 +1,39 @@
import { StyleSheet, Dimensions } from "react-native";
-const width = Dimensions.get("window").width;
-const height = Dimensions.get("window").height;
-
-const containerWidth = width - width * 0.08;
-const containerHeight = height - height * 0.01;
+export const Viewport = {
+ width: Dimensions.get("window").width,
+ height: Dimensions.get("window").height,
+};
export const colors = {
- orange_1: "#FFDEAD",
- orange_2: "#FFE2C1",
- orange_3: "#C07624",
- blue_1: "#E3963E",
- blue_2: "#FFAC1C",
- blue_3: "#FFAC1C",
- text_default: "white",
+ primary_1: "#1C2C3F",
+ primary_2: "#445467",
+ primary_3: "#606F81",
+ primary_4: "#b4d0f3",
+ secondary_1: "#1E1F3D",
+ secondary_2: "#626297",
+ secondary_3: "#7a7abd",
+ secondary_4: "#FFE9CE",
+ secondary_5: "#FFF5E9",
+ text_default: "#FFFF",
text_error: "#e32d1e",
- text_success: "green",
- icon_color: "white",
- login_color: "#0047AB",
- reg_color: "#0096FF",
- head: "white",
- blue_disabled: "#C07624",
+ text_success: "#2ecc71",
+ icon_color: "#FFFF",
+ head: "#FFFF",
};
export const font_sizes = {
tiny: 12,
small: 16,
medium: 24,
+ medium_large: 30,
large: 36,
xl: 48,
};
const styles = StyleSheet.create({
background: {
- backgroundColor: colors.orange_1,
+ backgroundColor: colors.secondary_1,
height: "100%",
width: "100%",
},
@@ -75,6 +75,12 @@ const styles = StyleSheet.create({
fontWeight: "bold",
textAlign: "center",
},
+ text_white_medium_large: {
+ color: colors.text_default,
+ fontSize: font_sizes.medium_large,
+ fontWeight: "bold",
+ textAlign: "center",
+ },
text_white_large: {
color: colors.text_default,
fontSize: font_sizes.large,
@@ -87,6 +93,18 @@ const styles = StyleSheet.create({
fontWeight: "bold",
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: {
justifyContent: "center",
alignSelf: "center",
@@ -98,63 +116,47 @@ const styles = StyleSheet.create({
marginHorizontal: 8,
padding: 8,
borderRadius: 16,
- width: width * 0.4,
+ width: Viewport.width * 0.4,
},
text_input: {
color: colors.text_default,
- backgroundColor: colors.blue_1,
+ backgroundColor: colors.primary_2,
+ borderColor: colors.primary_4,
+ borderWidth: 1,
padding: 10,
borderRadius: 8,
- width: width * 0.5,
+ width: Viewport.width * 0.5,
},
dropdown_template: {
- borderRadius: 16,
+ backgroundColor: colors.primary_2,
+ containerStyle: colors.primary_2,
+ borderRadius: 8,
width: "70%",
- marginVertical: 6,
},
map: {
- flex: 1,
- height: containerHeight,
- width: containerWidth,
+ height: Viewport.height * 0.8,
+ width: Viewport.width * 0.8,
alignSelf: "center",
},
profile: {
- height: 80,
- width: 80,
- alignSelf: 'center',
- },
+ height: 64,
+ width: 64,
+ alignSelf: "center",
+ borderRadius: 150 / 2,
+ overflow: "hidden",
+ padding: 0,
+ },
input: {
- height: 40,
- margin: 12,
- marginRight: 30,
+ paddingHorizontal: 8,
+ marginVertical: 2,
borderWidth: 1,
color: colors.text_default,
- backgroundColor: colors.blue_1,
+ backgroundColor: colors.primary_2,
borderRadius: 8,
- borderColor: '#FFAC1C',
- padding: 8,
+ borderColor: colors.primary_3,
},
- formGroup: {
- display: "flex",
- flexDirection: "row",
- alignItems: "center",
- },
- text: {
- marginLeft: 5,
- color: colors.text_default,
- fontSize: font_sizes.small,
- fontWeight: "bold",
- },
- button: {
- padding: 10,
- backgroundColor: colors.blue_2,
- borderRadius: 5,
- },
- activeText: {
- color: 'green',
- },
- inactiveText: {
- color: 'white',
+ padding: {
+ paddingVertical: 8,
},
});
export default styles;