Skip to content

Commit c1e1f7a

Browse files
committed
add profile page
1 parent 5f2f2a7 commit c1e1f7a

File tree

6 files changed

+218
-9
lines changed

6 files changed

+218
-9
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: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,21 @@ import { collection, getDocs } from "firebase/firestore";
55
interface Response {
66
type: "success" | "error" | undefined;
77
message: any;
8-
result: 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;
917
}
1018

1119
interface DataContextData {
1220
loading: boolean;
1321
response: Response;
22+
questions: Question[];
1423
getQuestions: () => void;
1524
}
1625

@@ -21,44 +30,53 @@ interface DataContextProviderProps {
2130
const emptyResponse: Response = {
2231
type: undefined,
2332
message: "",
24-
result: undefined,
2533
};
2634

2735
const DataContext = createContext<DataContextData>({
2836
loading: false,
2937
response: emptyResponse,
38+
questions: [],
3039
getQuestions: () => undefined,
3140
});
3241

3342
export function DataContextProvider({ children }: DataContextProviderProps) {
3443
const [loading, setLoading] = useState(false);
3544
const [response, setResponse] = useState<Response>(emptyResponse);
45+
const [questions, setQuestions] = useState<Question[]>([]);
3646

3747
const getQuestions = async () => {
3848
try {
39-
let questions: any[] = [];
4049
setLoading(true);
4150
const query = await getDocs(collection(db, "questions"));
42-
query.forEach((doc) => questions.push(doc.data()));
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+
});
4362
setLoading(false);
63+
setQuestions(result);
4464
setResponse({
4565
type: "success",
4666
message: "successfully retreived questions",
47-
result: questions,
4867
});
4968
} catch (e) {
5069
setLoading(false);
5170
setResponse({
5271
type: "error",
5372
message: e,
54-
result: undefined,
5573
});
5674
}
5775
};
5876

5977
const dataContextProviderValue = useMemo(
60-
() => ({ loading, response, getQuestions }),
61-
[loading, response]
78+
() => ({ loading, response, questions, getQuestions }),
79+
[loading, response, questions]
6280
);
6381

6482
return (

src/index.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Login from "./pages/login";
77
import SignUp from "./pages/signup";
88
import AuthGuard from "./auth/AuthGuard";
99
import { DataContextProvider } from "./data/data.context";
10+
import Profile from "./pages/profile";
1011

1112
const root = ReactDOM.createRoot(
1213
document.getElementById("root") as HTMLElement
@@ -28,6 +29,14 @@ root.render(
2829
</AuthGuard>
2930
}
3031
/>
32+
<Route
33+
path="/profile"
34+
element={
35+
<AuthGuard>
36+
<Profile />
37+
</AuthGuard>
38+
}
39+
/>
3140
</Routes>
3241
</DataContextProvider>
3342
</AuthContextProvider>

src/pages/profile.tsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { useEffect, useState } from "react";
2+
import {
3+
Avatar,
4+
Box,
5+
Button,
6+
Chip,
7+
Container,
8+
CssBaseline,
9+
Grid,
10+
} from "@mui/material";
11+
import Navbar from "../components/Navbar";
12+
import ProfileField from "../components/ProfileField";
13+
import { useAuth } from "../auth/auth.context";
14+
import { Values } from "../components/DropDownOrTextField";
15+
16+
export default function Profile() {
17+
const { user } = useAuth();
18+
const [edit, setEdit] = useState(false);
19+
const [value, setValue] = useState<Values>({
20+
email: user?.email ? user?.email : "",
21+
name: user?.displayName ? user?.displayName : "",
22+
year: "",
23+
major: "",
24+
});
25+
26+
const profileFields = [
27+
{ title: "Email", data: "email" },
28+
{ title: "Display Name", data: "name" },
29+
{ title: "Year of Study", data: "year" },
30+
{ title: "Major", data: "major" },
31+
];
32+
33+
const toggleEdit = () => {
34+
setEdit(!edit);
35+
};
36+
37+
useEffect(() => {
38+
console.log(user);
39+
});
40+
41+
return (
42+
<Box height="100vh">
43+
<CssBaseline />
44+
<Navbar />
45+
<Container sx={{ mt: 5 }}>
46+
<Grid container spacing={2}>
47+
<Grid item xs={3}>
48+
<Avatar sx={{ height: 192, width: 192, mb: 2 }}>R</Avatar>
49+
<Chip label="Verified User" color="primary" />
50+
</Grid>
51+
<Grid item xs={9}>
52+
<Box display="flex" justifyContent="flex-end" width="100%">
53+
<Button
54+
variant="contained"
55+
onClick={toggleEdit}
56+
color={edit ? "success" : "primary"}
57+
>
58+
{edit ? "Save Details" : "Edit Details"}
59+
</Button>
60+
</Box>
61+
{profileFields.map((field) => (
62+
<ProfileField
63+
title={field.title}
64+
data={field.data}
65+
edit={edit}
66+
value={value}
67+
setValue={setValue}
68+
/>
69+
))}
70+
</Grid>
71+
</Grid>
72+
</Container>
73+
</Box>
74+
);
75+
}

0 commit comments

Comments
 (0)