Skip to content

Commit c527f12

Browse files
authored
Merge pull request #33 from CS3219-AY2324S1/auth
add profile page
2 parents 3e32a6a + 1eb7b8e commit c527f12

File tree

6 files changed

+283
-92
lines changed

6 files changed

+283
-92
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Dispatch, SetStateAction } from "react";
2+
import {
3+
FormControl,
4+
InputLabel,
5+
MenuItem,
6+
Select,
7+
TextField,
8+
} from "@mui/material";
9+
10+
export interface Values {
11+
[key: string]: string;
12+
}
13+
14+
interface DropDownOrTextFieldProps {
15+
title: string;
16+
data: string;
17+
value: Values;
18+
setValue: Dispatch<SetStateAction<Values>>;
19+
}
20+
21+
interface DropDownValues {
22+
[key: string]: string[];
23+
}
24+
25+
export function DropDownOrTextField({
26+
title,
27+
data,
28+
value,
29+
setValue,
30+
}: DropDownOrTextFieldProps) {
31+
const dropdowns = ["Year of Study", "Major"];
32+
const dropDownValues: DropDownValues = {
33+
"Year of Study": ["1", "2", "3", "4", ">5"],
34+
Major: [
35+
"Computer Science",
36+
"Business Analytics",
37+
"Information Systems",
38+
"Data Science",
39+
],
40+
};
41+
42+
return (
43+
<>
44+
{dropdowns.includes(title) ? (
45+
<FormControl sx={{ width: "23.5ch" }}>
46+
<InputLabel id="demo-simple-select-label">{title}</InputLabel>
47+
<Select
48+
labelId="demo-simple-select-label"
49+
id="demo-simple-select"
50+
value={value[data]}
51+
label={title}
52+
onChange={(e) => setValue({ ...value, [data]: e.target.value })}
53+
>
54+
{dropDownValues[title].map((val: string) => (
55+
<MenuItem value={val}>{val}</MenuItem>
56+
))}
57+
</Select>
58+
</FormControl>
59+
) : (
60+
<TextField label={title} variant="outlined" value={value[data]} />
61+
)}
62+
</>
63+
);
64+
}

src/components/Navbar.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import MenuIcon from "@mui/icons-material/Menu";
1616
import AdbIcon from "@mui/icons-material/Adb";
1717
import { useAuth } from "../auth/auth.context";
18+
import { useNavigate } from "react-router-dom";
1819

1920
const pages = ["Products", "Pricing", "Blog"];
2021
const authPages = [
@@ -30,6 +31,7 @@ const authPages = [
3031

3132
export default function Navbar() {
3233
const { user, logout } = useAuth();
34+
const navigate = useNavigate();
3335
const [anchorElNav, setAnchorElNav] = useState<null | HTMLElement>(null);
3436
const [anchorElUser, setAnchorElUser] = useState<null | HTMLElement>(null);
3537

@@ -50,7 +52,7 @@ export default function Navbar() {
5052
};
5153

5254
const settings = [
53-
{ name: "Profile", onclick: handleCloseUserMenu },
55+
{ name: "Profile", onclick: () => navigate("/profile", { replace: true }) },
5456
{ name: "Account", onclick: handleCloseUserMenu },
5557
{ name: "Dashboard", onclick: handleCloseUserMenu },
5658
{ name: "Logout", onclick: logout },

src/components/ProfileField.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Dispatch, SetStateAction } from "react";
2+
import { Box, Grid, Typography } from "@mui/material";
3+
import { DropDownOrTextField, Values } from "./DropDownOrTextField";
4+
5+
interface ProfileFieldProps {
6+
title: string;
7+
data: string;
8+
edit: boolean;
9+
value: Values;
10+
setValue: Dispatch<SetStateAction<Values>>;
11+
}
12+
13+
export default function ProfileField({
14+
title,
15+
data,
16+
edit,
17+
value,
18+
setValue,
19+
}: ProfileFieldProps) {
20+
return (
21+
<Box my={1}>
22+
{edit ? (
23+
<DropDownOrTextField
24+
title={title}
25+
data={data}
26+
value={value}
27+
setValue={setValue}
28+
/>
29+
) : (
30+
<Grid container>
31+
<Grid item xs={3}>
32+
<Typography fontWeight={600}>{title}:</Typography>
33+
</Grid>
34+
<Grid item xs={9}>
35+
{value[data]}
36+
</Grid>
37+
</Grid>
38+
)}
39+
</Box>
40+
);
41+
}

src/data/data.context.tsx

Lines changed: 91 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,91 @@
1-
import { createContext, ReactNode, useContext, useMemo, useState } from "react";
2-
import { db } from "../firebase/firebase";
3-
import { collection, getDocs } from "firebase/firestore";
4-
5-
interface Response {
6-
type: "success" | "error" | undefined;
7-
message: any;
8-
}
9-
10-
interface Question {
11-
title: string;
12-
tags: string[];
13-
categories: string[];
14-
constraints: string[];
15-
difficulty: "Easy" | "Medium" | "Hard";
16-
description: string;
17-
}
18-
19-
interface DataContextData {
20-
loading: boolean;
21-
response: Response;
22-
questions: Question[];
23-
getQuestions: () => void;
24-
}
25-
26-
interface DataContextProviderProps {
27-
children: ReactNode;
28-
}
29-
30-
const emptyResponse: Response = {
31-
type: undefined,
32-
message: "",
33-
};
34-
35-
const DataContext = createContext<DataContextData>({
36-
loading: false,
37-
response: emptyResponse,
38-
questions: [],
39-
getQuestions: () => undefined,
40-
});
41-
42-
export function DataContextProvider({ children }: DataContextProviderProps) {
43-
const [loading, setLoading] = useState(false);
44-
const [response, setResponse] = useState<Response>(emptyResponse);
45-
const [questions, setQuestions] = useState<Question[]>([]);
46-
47-
const getQuestions = async () => {
48-
try {
49-
setLoading(true);
50-
const query = await getDocs(collection(db, "questions"));
51-
const result = query.docs.map((d) => {
52-
const q = d.data();
53-
return {
54-
title: q.title,
55-
tags: q.tags,
56-
categories: q.categories,
57-
constraints: q.constraints,
58-
difficulty: q.difficulty,
59-
description: q.description,
60-
};
61-
});
62-
setLoading(false);
63-
setQuestions(result);
64-
setResponse({
65-
type: "success",
66-
message: "successfully retreived questions",
67-
});
68-
} catch (e) {
69-
setLoading(false);
70-
setResponse({
71-
type: "error",
72-
message: e,
73-
});
74-
}
75-
};
76-
77-
const dataContextProviderValue = useMemo(
78-
() => ({ loading, response, questions, getQuestions }),
79-
[loading, response, questions]
80-
);
81-
82-
return (
83-
<DataContext.Provider value={dataContextProviderValue}>
84-
{children}
85-
</DataContext.Provider>
86-
);
87-
}
88-
89-
export const useData = () => {
90-
return useContext(DataContext);
91-
};
1+
import { createContext, ReactNode, useContext, useMemo, useState } from "react";
2+
import { db } from "../firebase/firebase";
3+
import { collection, getDocs } from "firebase/firestore";
4+
5+
interface Response {
6+
type: "success" | "error" | undefined;
7+
message: any;
8+
}
9+
10+
interface Question {
11+
title: string;
12+
tags: string[];
13+
categories: string[];
14+
constraints: string[];
15+
difficulty: "Easy" | "Medium" | "Hard";
16+
description: string;
17+
}
18+
19+
interface DataContextData {
20+
loading: boolean;
21+
response: Response;
22+
questions: Question[];
23+
getQuestions: () => void;
24+
}
25+
26+
interface DataContextProviderProps {
27+
children: ReactNode;
28+
}
29+
30+
const emptyResponse: Response = {
31+
type: undefined,
32+
message: "",
33+
};
34+
35+
const DataContext = createContext<DataContextData>({
36+
loading: false,
37+
response: emptyResponse,
38+
questions: [],
39+
getQuestions: () => undefined,
40+
});
41+
42+
export function DataContextProvider({ children }: DataContextProviderProps) {
43+
const [loading, setLoading] = useState(false);
44+
const [response, setResponse] = useState<Response>(emptyResponse);
45+
const [questions, setQuestions] = useState<Question[]>([]);
46+
47+
const getQuestions = async () => {
48+
try {
49+
setLoading(true);
50+
const query = await getDocs(collection(db, "questions"));
51+
const result = query.docs.map((d) => {
52+
const q = d.data();
53+
return {
54+
title: q.title,
55+
tags: q.tags,
56+
categories: q.categories,
57+
constraints: q.constraints,
58+
difficulty: q.difficulty,
59+
description: q.description,
60+
};
61+
});
62+
setLoading(false);
63+
setQuestions(result);
64+
setResponse({
65+
type: "success",
66+
message: "successfully retreived questions",
67+
});
68+
} catch (e) {
69+
setLoading(false);
70+
setResponse({
71+
type: "error",
72+
message: e,
73+
});
74+
}
75+
};
76+
77+
const dataContextProviderValue = useMemo(
78+
() => ({ loading, response, questions, getQuestions }),
79+
[loading, response, questions]
80+
);
81+
82+
return (
83+
<DataContext.Provider value={dataContextProviderValue}>
84+
{children}
85+
</DataContext.Provider>
86+
);
87+
}
88+
89+
export const useData = () => {
90+
return useContext(DataContext);
91+
};

src/index.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import SignUp from "./pages/signup";
88
import AuthGuard from "./auth/AuthGuard";
99
import { DataContextProvider } from "./data/data.context";
1010
import Landing from "./pages/landing";
11+
import Profile from "./pages/profile";
1112

1213
const root = ReactDOM.createRoot(
1314
document.getElementById("root") as HTMLElement
@@ -30,6 +31,14 @@ root.render(
3031
</AuthGuard>
3132
}
3233
/>
34+
<Route
35+
path="/profile"
36+
element={
37+
<AuthGuard>
38+
<Profile />
39+
</AuthGuard>
40+
}
41+
/>
3342
</Routes>
3443
</DataContextProvider>
3544
</AuthContextProvider>

0 commit comments

Comments
 (0)