diff --git a/src/pages/course/index.tsx b/src/pages/course/index.tsx index c82ac25..b862c4a 100644 --- a/src/pages/course/index.tsx +++ b/src/pages/course/index.tsx @@ -2,28 +2,13 @@ import { useState, useEffect } from "react"; import RootPage from "../root"; import Container from "@mui/material/Container"; import Typography from "@mui/material/Typography"; -import Cookies from "js-cookie"; import "./course.css"; // Import CSS file for additional styling -import { - IsStudent, - IsAdmin, - backend_get, - deleteAuthCookies, - useAxiosRequest, - backend_post -} from "../../utils"; +import { IsStudent, IsAdmin, useAxiosRequest, backend_post } from "../../utils"; import { useNavigate, useParams } from "react-router-dom"; -import { JwtPayload, jwtDecode } from "jwt-decode"; -import { - CookieJWT, - Empty, - FullCourse, - FullCourseUser -} from "../../types/common"; +import { Empty, FullCourse, FullCourseUser } from "../../types/common"; import { Avatar, Button, IconButton, capitalize, styled } from "@mui/material"; -import MoreVertIcon from "@mui/icons-material/MoreVert"; -import { render } from "@testing-library/react"; +import CancelIcon from "@mui/icons-material/Cancel"; const Course = () => { const { response, error, loading, sendRequest } = useAxiosRequest< @@ -99,9 +84,10 @@ const Course = () => { ); }; - const handleEnrollment = (enroll: boolean) => { + const handleEnrollment = (enroll: boolean, username: string = "") => { let url = "course/enroll/" + id; if (!enroll) url = "course/disenroll/" + id; + if (username != "") url = `${url}/${username}`; backend_post(url, "", true).then((resp) => { if (resp.status == 200) { getCourseData(); @@ -151,6 +137,32 @@ const Course = () => { marginRight: "5%" }} > + + + {!IsStudent() ? ( + + ) : null} + {!IsAdmin() ? ( courseData?.enrolled == true ? ( ) ) : null} - - @@ -208,7 +210,9 @@ const Course = () => { Name Role {IsAdmin() ? ( - Actions + + Disenroll + ) : null} @@ -235,6 +239,15 @@ const Course = () => { textTransform: "none", fontSize: "1em" }} + sx={{ + "&.MuiButtonBase-root:hover": + { + bgcolor: + "transparent", + textDecoration: + "underline" + } + }} onClick={() => handleProfileClick( user.username @@ -249,8 +262,23 @@ const Course = () => { {IsAdmin() ? ( - - + { + handleEnrollment( + false, + user.username + ); + }} + sx={{ + "&.MuiButtonBase-root:hover": + { + bgcolor: + "transparent", + color: "red" + } + }} + > + ) : null} diff --git a/src/pages/courses/index.tsx b/src/pages/courses/index.tsx index 6a7c95e..550849a 100644 --- a/src/pages/courses/index.tsx +++ b/src/pages/courses/index.tsx @@ -3,20 +3,20 @@ import Container from "@mui/material/Container"; import "./courses.css"; // Import CSS file for additional styling import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { backend_post, useAxiosRequest } from "../../utils"; +import { backend_delete, backend_post, useAxiosRequest } from "../../utils"; import { Course, Empty } from "../../types/common"; import { IsAdmin } from "../../utils"; -import { User } from "../../types/common"; import { Button, Checkbox, + Icon, IconButton, Typography, styled } from "@mui/material"; -import MoreVertIcon from "@mui/icons-material/MoreVert"; import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; -import Avatar from "@mui/material/Avatar"; +import CheckCircleIcon from "@mui/icons-material/CheckCircle"; +import CancelIcon from "@mui/icons-material/Cancel"; type ResponseData = Course[]; @@ -39,6 +39,7 @@ const Courses = () => { }, [sendRequest]); useEffect(() => { + console.log(response); if (response) setCourseData(response); }, [response]); @@ -92,20 +93,21 @@ const Courses = () => { navigate(`/course/${course_id}`); }; - const handleEnrollment = - (courseId: number, enroll: boolean) => - (event: React.ChangeEvent) => { - if (enroll) { - backend_post("course/enroll/" + courseId, "", true); - if (response == null) return; - const new_data = course_data?.map((course: Course) => - course.id == courseId - ? { ...course, enrolled: true } - : course - ); - setCourseData(new_data); + const deleteCourse = (course_id: number) => { + backend_delete("/course/delete/" + course_id, true).then((resp) => { + if (resp.ok) { + if (course_data == undefined) return; + let tempCourses: Course[] = [...course_data]; + for (let i = 0; i < tempCourses.length; i++) { + if (tempCourses[i].id === course_id) { + tempCourses.splice(i, 1); + break; + } + } + setCourseData(tempCourses); } - }; + }); + }; return ( @@ -131,10 +133,11 @@ const Courses = () => { Course Name Students + Teachers {IsAdmin() ? ( Actions ) : ( - Enroll + Enrolled )} @@ -153,6 +156,12 @@ const Courses = () => { textTransform: "none", fontSize: "1em" }} + sx={{ + "&.MuiButtonBase-root:hover": { + bgcolor: "transparent", + textDecoration: "underline" + } + }} onClick={() => handleCourseClick(course.id) } @@ -160,21 +169,31 @@ const Courses = () => { {`${course.course_name}`} - + {course.num_students} + {course.num_teachers} {IsAdmin() ? ( - - + { + deleteCourse(course.id); + }} + sx={{ + "&.MuiButtonBase-root:hover": { + bgcolor: "transparent", + color: "red" + } + }} + > + ) : ( } + checkedIcon={} className="actions-icon" checked={course.enrolled} - onChange={handleEnrollment( - course.id, - !course.enrolled - )} + disabled={true} sx={{ "& .MuiSvgIcon-root": { color: "white" diff --git a/src/pages/create_course/index.tsx b/src/pages/create_course/index.tsx index 4a60c6d..58b221e 100644 --- a/src/pages/create_course/index.tsx +++ b/src/pages/create_course/index.tsx @@ -5,7 +5,7 @@ import TextField from "@mui/material/TextField"; import Container from "@mui/material/Container"; import Avatar from "@mui/material/Avatar"; import Typography from "@mui/material/Typography"; -import { Box, MenuItem, Select } from "@mui/material"; +import { Box } from "@mui/material"; import AppRegistrationIcon from "@mui/icons-material/AppRegistration"; import RootPage from "../root"; @@ -17,10 +17,7 @@ const CreateCourse = () => { // const navigate = useNavigate(); const [regStatus, setRegStatus] = useState(""); - const handleResponse = ( - data: any, - event: React.FormEvent - ) => { + const handleResponse = (data: any) => { console.log(data); if ( !("course_name" in data) || @@ -46,7 +43,7 @@ const CreateCourse = () => { }) ) .then((resp) => resp.json()) - .then((data) => handleResponse(data, event)) + .then((data) => handleResponse(data)) .catch((error) => console.log(error)); }; diff --git a/src/pages/create_lecture/index.tsx b/src/pages/create_lecture/index.tsx index 08d3a8b..4833422 100644 --- a/src/pages/create_lecture/index.tsx +++ b/src/pages/create_lecture/index.tsx @@ -1,7 +1,6 @@ import React, { useState } from "react"; import Button from "@mui/material/Button"; -import TextField from "@mui/material/TextField"; import Container from "@mui/material/Container"; import Avatar from "@mui/material/Avatar"; import Typography from "@mui/material/Typography"; @@ -21,10 +20,7 @@ const CreateLecture = () => { const [regStatus, setRegStatus] = useState(""); const { id } = useParams(); - const handleResponse = ( - data: any, - event: React.FormEvent - ) => { + const handleResponse = (data: any) => { console.log(data); if ("ok" in data) { setRegStatus("success"); @@ -56,7 +52,7 @@ const CreateLecture = () => { }) ) .then((resp) => resp.json()) - .then((data) => handleResponse(data, event)) + .then((data) => handleResponse(data)) .catch((error) => console.log(error)); }; diff --git a/src/pages/create_user/index.tsx b/src/pages/create_user/index.tsx index 9e5c2c3..0de506b 100644 --- a/src/pages/create_user/index.tsx +++ b/src/pages/create_user/index.tsx @@ -16,10 +16,7 @@ const CreateUser = () => { // const navigate = useNavigate(); const [regStatus, setRegStatus] = useState(""); - const handleTokenResponse = ( - data: any, - event: React.FormEvent - ) => { + const handleTokenResponse = (data: any) => { console.log(data); if (!("username" in data) || typeof data["username"] !== "string") { setRegStatus("failed"); @@ -61,7 +58,7 @@ const CreateUser = () => { }) ) .then((resp) => resp.json()) - .then((data) => handleTokenResponse(data, event)) + .then((data) => handleTokenResponse(data)) .catch((error) => console.log(error)); }; diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 32959ed..624fed2 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -1,7 +1,6 @@ import { FC } from "react"; import Rootpage from "../root"; import { Box, Typography } from "@mui/material"; -import backgroundImage from "../../assets/logo-clear.png"; const HomePage: FC = () => ( diff --git a/src/pages/people/index.tsx b/src/pages/people/index.tsx index fe0890e..e52c975 100644 --- a/src/pages/people/index.tsx +++ b/src/pages/people/index.tsx @@ -2,14 +2,20 @@ import RootPage from "../root"; import Container from "@mui/material/Container"; import "./people.css"; // Import CSS file for additional styling import { useEffect, useState } from "react"; -import { deleteAuthCookies, backend_get, IsAdmin } from "../../utils"; +import { + deleteAuthCookies, + backend_get, + IsAdmin, + json_request, + backend_delete +} from "../../utils"; import { useNavigate } from "react-router-dom"; import { User } from "../../types/common"; import { Button, IconButton, Typography, capitalize } from "@mui/material"; import { styled } from "@mui/material/styles"; -import MoreVertIcon from "@mui/icons-material/MoreVert"; import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; import Avatar from "@mui/material/Avatar"; +import CancelIcon from "@mui/icons-material/Cancel"; const People = () => { const navigate = useNavigate(); @@ -21,36 +27,36 @@ const People = () => { return resp.json(); }; - useEffect(() => { - const fetchUsers = async () => { - try { - const students_query = await getUserRole("student"); - const teachers_query = await getUserRole("teacher"); - if ("code" in students_query || "code" in teachers_query) { - // Not authenticated anymore - deleteAuthCookies(); - navigate("/login"); - navigate(0); - return; - } - - const students = !( - "error" in students_query || "detail" in students_query - ) - ? students_query - : []; - const teachers = !( - "error" in teachers_query || "detail" in teachers_query - ) - ? teachers_query - : []; - setAllUsers(teachers.concat(students)); - } catch (error) { - console.error("Error fetching profile:", error); - // Show error on frontend + const fetchUsers = async () => { + try { + const students_query = await getUserRole("student"); + const teachers_query = await getUserRole("teacher"); + if ("code" in students_query || "code" in teachers_query) { + // Not authenticated anymore + deleteAuthCookies(); + navigate("/login"); + navigate(0); + return; } - }; + const students = !( + "error" in students_query || "detail" in students_query + ) + ? students_query + : []; + const teachers = !( + "error" in teachers_query || "detail" in teachers_query + ) + ? teachers_query + : []; + setAllUsers(teachers.concat(students)); + } catch (error) { + console.error("Error fetching profile:", error); + // Show error on frontend + } + }; + + useEffect(() => { fetchUsers(); }, [navigate]); @@ -96,6 +102,22 @@ const People = () => { navigate(`/profile/${username}`); }; + const deleteUser = (username: string) => { + backend_delete("/user/delete/" + username, true).then((resp) => { + if (resp.ok) { + if (allUsers == undefined) return; + let tempUsers: User[] = [...allUsers]; + for (let i = 0; i < tempUsers.length; i++) { + if (tempUsers[i].username === username) { + tempUsers.splice(i, 1); + break; + } + } + setAllUsers(tempUsers); + } + }); + }; + return ( @@ -146,6 +168,12 @@ const People = () => { textTransform: "none", fontSize: "1em" }} + sx={{ + "&.MuiButtonBase-root:hover": { + bgcolor: "transparent", + textDecoration: "underline" + } + }} onClick={() => handleProfileClick(user.username) } @@ -158,8 +186,18 @@ const People = () => { {IsAdmin() ? ( - - + { + deleteUser(user.username); + }} + sx={{ + "&.MuiButtonBase-root:hover": { + bgcolor: "transparent", + color: "red" + } + }} + > + ) : null} diff --git a/src/pages/profile/index.tsx b/src/pages/profile/index.tsx index e8b3219..a93c2f7 100644 --- a/src/pages/profile/index.tsx +++ b/src/pages/profile/index.tsx @@ -7,7 +7,7 @@ import "./profile.css"; // Import CSS file for additional styling import { backend_get, deleteAuthCookies } from "../../utils"; import { useNavigate, useParams } from "react-router-dom"; -import { JwtPayload, jwtDecode } from "jwt-decode"; +import { jwtDecode } from "jwt-decode"; import { CookieJWT } from "../../types/common"; import { Button, capitalize } from "@mui/material"; @@ -39,10 +39,6 @@ const Profile = () => { return resp.json(); }; - const updateProfilePicture = async () => { - console.log("Update Profile Picture function triggered"); - }; - // const [loggedInUsername, setLoggedInUsername] = useState(""); //store image inside a storage solution (S3 bucket)! // After, we collect the url from the s3 diff --git a/src/pages/schedule/index.tsx b/src/pages/schedule/index.tsx index d42d313..29bdf9c 100644 --- a/src/pages/schedule/index.tsx +++ b/src/pages/schedule/index.tsx @@ -7,14 +7,26 @@ import ListItemText from "@mui/material/ListItemText"; import Checkbox from "@mui/material/Checkbox"; import { useNavigate, useParams } from "react-router-dom"; import { ScheduleLecture, Empty } from "../../types/common"; -import { backend_post, useAxiosRequest } from "../../utils"; +import { + IsStudent, + backend_delete, + backend_post, + useAxiosRequest +} from "../../utils"; import moment from "moment"; -import { Button, Divider, capitalize } from "@mui/material"; +import { + Button, + Divider, + IconButton, + Tooltip, + capitalize +} from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; import IndeterminateCheckBoxIcon from "@mui/icons-material/IndeterminateCheckBox"; import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank"; -import CheckBoxIcon from "@mui/icons-material/CheckBox"; +import CancelIcon from "@mui/icons-material/Cancel"; +import GroupAddIcon from "@mui/icons-material/GroupAdd"; import "./schedule.css"; // Set monday to the first day of the week @@ -32,16 +44,17 @@ const AttendancePage: React.FC = () => { const { id } = useParams(); const updateSchedule = () => { + let url = `/schedule/get/${schedule_date.year()}/${schedule_date.week()}`; + if (id != undefined) url += "/" + id; sendRequest({ method: "GET", - route: `/schedule/get/${schedule_date.year()}/${schedule_date.week()}`, + route: url, useJWT: true }); }; useEffect(() => { updateSchedule(); - console.log(id); }, [sendRequest]); useEffect(() => { @@ -67,34 +80,32 @@ const AttendancePage: React.FC = () => { setLectures(new_lectures); }; - const handleAttendanceChange = - (lecture: ScheduleLecture) => - (event: React.ChangeEvent) => { - var url = `lecture/${lecture.id}/student_set_att`; - var attended = true; + const handleAttendanceChange = (lecture: ScheduleLecture) => { + let url = `lecture/${lecture.id}/student_set_att`; + let attended = true; - if (lecture.attended_student == true) { - url = `lecture/${lecture.id}/student_unset_att`; - attended = false; - } + if (lecture.attended_student == true) { + url = `lecture/${lecture.id}/student_unset_att`; + attended = false; + } - backend_post(url, "", true) - .then((resp) => { - if (resp.status == 200) - setStudentAttendence(lecture.id, attended); - }) - .catch((error) => console.log(error)); - }; + backend_post(url, "", true) + .then((resp) => { + if (resp.status == 200) + setStudentAttendence(lecture.id, attended); + }) + .catch((error) => console.log(error)); + }; - function goBackWeek() { + const goBackWeek = () => { setScheduleDate(schedule_date.subtract(7, "days")); updateSchedule(); - } + }; - function goForwardWeek() { + const goForwardWeek = () => { setScheduleDate(schedule_date.add(7, "days")); updateSchedule(); - } + }; const dayConvert = [ "Monday", @@ -147,6 +158,23 @@ const AttendancePage: React.FC = () => { return ""; }; + const deleteLecture = (lecture_id: number) => { + backend_delete(`course/lecture/${lecture_id}/delete`, true).then( + (resp) => { + if (resp.ok) { + const new_lectures = lectures?.map( + (lectureGroup: ScheduleLecture[]) => + lectureGroup.filter( + (lecture: ScheduleLecture) => + lecture.id !== lecture_id + ) + ); + setLectures(new_lectures); + } + } + ); + }; + return ( { ) : null} - {day.map((lecture, lectureIndex) => ( + {day.map((lecture) => ( { - - - } - icon={} - indeterminate={ - lecture.attended_student == null - } - checked={ - lecture.attended_student == true - } - onChange={handleAttendanceChange( - lecture - )} - sx={{ - "& .MuiSvgIcon-root": { - color: getCheckboxColor( - lecture, - lecture.attended_student - ) - } - }} - /> - - } - icon={} - indeterminate={ - lecture.attended_teacher == null - } - checked={ - lecture.attended_teacher == true - } - disabled={true} - sx={{ - "& .MuiSvgIcon-root": { - color: getCheckboxColor( - lecture, - lecture.attended_teacher - ) - } - }} - /> + {IsStudent() ? ( + <> + + } + icon={ + + } + indeterminate={ + lecture.attended_student == + null + } + checked={ + lecture.attended_student == + true + } + onChange={() => + handleAttendanceChange( + lecture + ) + } + sx={{ + "& .MuiSvgIcon-root": { + color: getCheckboxColor( + lecture, + lecture.attended_student + ) + } + }} + /> + + } + icon={ + + } + indeterminate={ + lecture.attended_teacher == + null + } + checked={ + lecture.attended_teacher == + true + } + disabled={true} + sx={{ + "& .MuiSvgIcon-root": { + color: getCheckboxColor( + lecture, + lecture.attended_teacher + ) + } + }} + /> + + ) : ( + <> + + { + // ( + // false, + // user.username + // ); + // }} + sx={{ + "&.MuiButtonBase-root:hover": + { + bgcolor: + "transparent", + color: "gray" + } + }} + > + + + + + { + deleteLecture( + lecture.id + ); + }} + sx={{ + "&.MuiButtonBase-root:hover": + { + bgcolor: + "transparent", + color: "red" + } + }} + > + + + + + )} ))} diff --git a/src/types/common.ts b/src/types/common.ts index d3f4591..1494b0c 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -9,6 +9,8 @@ export interface Course { id: number; course_name: string; enrolled: boolean; + num_students: number; + num_teachers: number; } export interface FullCourseUser { diff --git a/src/utils/index.ts b/src/utils/index.ts index 6e1f2ed..50877ff 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -106,6 +106,9 @@ export const json_request = ( export const backend_get = (endpoint: string, useJWT: boolean = false) => json_request(backend_url + endpoint, "GET", "", useJWT); +export const backend_delete = (endpoint: string, useJWT: boolean = false) => + json_request(backend_url + endpoint, "DELETE", "", useJWT); + export const backend_post = ( endpoint: string, body_json: string = "",