Skip to content

Commit 433301f

Browse files
authored
Merge pull request #19 from CS3219-AY2324S1/feat/profile-page
Implement user's profile page
2 parents d492468 + 20de4e3 commit 433301f

25 files changed

+1536
-236
lines changed

frontend/package-lock.json

Lines changed: 50 additions & 51 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
"@emotion/react": "^11.11.1",
1414
"@emotion/styled": "^11.11.0",
1515
"@mui/icons-material": "^5.14.13",
16-
"@mui/material": "^5.14.9",
17-
"axios": "^1.5.0",
16+
"@mui/material": "^5.14.10",
17+
"axios": "^1.5.1",
1818
"react": "^18.2.0",
1919
"react-dom": "^18.2.0",
2020
"react-quill": "^2.0.0",

frontend/src/App.jsx

Lines changed: 31 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,42 @@
1-
import { useState, useEffect } from "react";
2-
import QuestionList from "./components/QuestionList";
31
import "./App.css";
4-
import Box from "@mui/material/Box";
5-
import axios from "axios";
6-
import AddQuestionDialog from "./components/AddQuestionDialog";
2+
import { useState } from "react";
3+
import { Grid } from "@mui/material";
74
import Theme from "./themes/Theme";
8-
import { reverseCategoryMapping } from "./utils/QuestionUtil";
5+
import ProfilePage from "./pages/ProfilePage/ProfilePage";
6+
import Home from "./pages/Home";
7+
import QuestionsRepo from "./pages/QuestionsRepo";
8+
import LoginPage from "./pages/LoginPage";
9+
import RegisterPage from "./pages/RegisterPage";
10+
import { Route, Routes } from "react-router-dom";
11+
import UserContextProvider from "./utils/UserContextUtil";
12+
import SnackbarProvider from "./utils/SnackbarContextUtil";
913

1014
function App() {
11-
const databaseURL = import.meta.env.VITE_DATABASE_URL;
12-
const [questions, setQuestions] = useState([]);
13-
const loadQuestions = async () => {
14-
const questions = await axios.get(`${databaseURL}/question`);
15-
for (let i = 0; i < questions.data.length; i++) {
16-
let question = questions.data[i];
17-
for (let j = 0; j < question.categories.length; j++) {
18-
question.categories[j] = reverseCategoryMapping[question.categories[j]];
19-
}
20-
}
21-
setQuestions(questions.data);
22-
};
23-
24-
const handleDelete = async (id) => {
25-
await axios.delete(`${databaseURL}/question/${id}`);
26-
loadQuestions();
27-
};
28-
29-
const handleEdit = async (id, fieldsToUpdate) => {
30-
await axios.patch(`${databaseURL}/question/${id}`, fieldsToUpdate);
31-
loadQuestions();
32-
};
33-
34-
useEffect(() => {
35-
loadQuestions();
36-
}, []);
15+
const user = JSON.parse(localStorage.getItem("user"));
16+
const [userContext, setUserContext] = useState({
17+
username: user ? user.username : null,
18+
userId: user ? user.id : null,
19+
});
20+
const value = { userContext, setUserContext };
3721

3822
return (
3923
<>
4024
<Theme>
41-
<h1>Peerprep</h1>
42-
<Box direction="column">
43-
<Box mb={2}>
44-
<AddQuestionDialog
45-
questions={questions}
46-
onAddQuestion={loadQuestions}
47-
/>
48-
</Box>
49-
<Box mb={2}>
50-
<QuestionList
51-
questions={questions}
52-
onDelete={handleDelete}
53-
onEdit={handleEdit}
54-
/>
55-
</Box>
56-
</Box>
25+
<Grid container>
26+
<Grid item xs={12}>
27+
<UserContextProvider.Provider value={value}>
28+
<SnackbarProvider>
29+
<Routes>
30+
<Route path="/" element={<LoginPage />} />
31+
<Route path="/register" element={<RegisterPage />} />
32+
<Route path="/home" element={<Home />} />
33+
<Route path="/profile" element={<ProfilePage />} />
34+
<Route path="/questions" element={<QuestionsRepo />} />
35+
</Routes>
36+
</SnackbarProvider>
37+
</UserContextProvider.Provider>
38+
</Grid>
39+
</Grid>
5740
</Theme>
5841
</>
5942
);

frontend/src/components/AddQuestionDialog.jsx

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useRef, useEffect } from "react";
1+
import { useState, useRef, useEffect, useContext } from "react";
22
import {
33
Button,
44
TextField,
@@ -14,7 +14,7 @@ import {
1414
Select,
1515
Chip,
1616
} from "@mui/material";
17-
import CustomSnackbar from "./CustomSnackbar";
17+
import { SnackbarContext } from "../utils/SnackbarContextUtil";
1818
import axios from "axios";
1919
import ReactQuill from "react-quill";
2020
import "react-quill/dist/quill.snow.css";
@@ -40,8 +40,7 @@ function AddQuestionDialog({ questions, onAddQuestion }) {
4040
const [description, setDescription] = useState("");
4141
const [category, setCategory] = useState([]);
4242
const [openDialog, setOpenDialog] = useState(false);
43-
const [isSnackbarOpen, setSnackbarOpen] = useState(false);
44-
const [snackbarMessage, setsnackbarMessage] = useState("");
43+
const { snack, setSnack } = useContext(SnackbarContext);
4544

4645
const handleSelectChange = (event, id) => {
4746
const {
@@ -64,29 +63,12 @@ function AddQuestionDialog({ questions, onAddQuestion }) {
6463
setOpenDialog(false);
6564
};
6665

67-
const handleDuplicateQuestion = () => {
68-
setsnackbarMessage("Duplicate question detected!");
69-
setSnackbarOpen(true);
70-
};
71-
72-
const handleEmptyInputField = () => {
73-
setsnackbarMessage("Missing fields detected!");
74-
setSnackbarOpen(true);
75-
};
76-
77-
const handleSnackbarClose = (event, reason) => {
78-
if (reason == "clickaway") {
79-
return;
80-
}
81-
setSnackbarOpen(false);
82-
};
83-
8466
const handleSubmit = async (evt) => {
8567
evt.preventDefault();
8668

87-
const title = inputRefs["title"].current.value.trim();
69+
const title = inputRefs["title"].current.value.trimEnd();
8870
const categories = category;
89-
const descriptionClean = description.replace(/<(?!img)[^>]*>/g, "").trim();
71+
const descriptionClean = description.replace(/<(?!img)[^>]*>/g, "").trimEnd();
9072

9173
const isDuplicateQuestion = questions.some(
9274
(question) => question.title.toLowerCase() === title.toLowerCase()
@@ -99,9 +81,17 @@ function AddQuestionDialog({ questions, onAddQuestion }) {
9981
descriptionClean.length == 0;
10082

10183
if (isDuplicateQuestion) {
102-
handleDuplicateQuestion();
84+
setSnack({
85+
message: "Duplicate question detected!",
86+
open: true,
87+
severity: "warning",
88+
});
10389
} else if (isInputFieldEmpty) {
104-
handleEmptyInputField();
90+
setSnack({
91+
message: "Missing fields detected!",
92+
open: true,
93+
severity: "warning",
94+
});
10595
} else {
10696
for (let i = 0; i < categories.length; i++) {
10797
categories[i] = categoryMapping[categories[i]];
@@ -224,12 +214,6 @@ function AddQuestionDialog({ questions, onAddQuestion }) {
224214
<Button onClick={handleSubmit}>Add Question</Button>
225215
</DialogActions>
226216
</Dialog>
227-
<CustomSnackbar
228-
open={isSnackbarOpen}
229-
onClose={handleSnackbarClose}
230-
message={snackbarMessage}
231-
severity="warning"
232-
></CustomSnackbar>
233217
</div>
234218
);
235219
}

0 commit comments

Comments
 (0)