Skip to content

Commit

Permalink
Course views for teacher (#161)
Browse files Browse the repository at this point in the history
* teacher detailed course base project list

* basic

* scrollers

* doc

* backtick strings

* base all courses page

* changed height

* first finished version of teacher course views

* teacher all course page reviewed

* display time until deadline

* more searchbars and translations

* changed file structure for maintainability

* titles and absolute create button

* searchbars update url and remain on reload

* user and admin listing

* reworked form and gave unique ids to all list items

* whoops linter ssst

* added .env to gitignore

* placeholder for loading courses

* debounce seachbars

* better debouncing more hooks

* setup for loader but doesnt work yet bc bad router

* loaders

* course detail teacher course join codes menu

* join code link points to app host now

* accident

* fixed empty filters, placeholders and teacher in admin list

* delete

* removed wannabe popup errorer

* unuserud

* package

* fix

* uhh

* u
  • Loading branch information
JibrilExe authored Apr 19, 2024
1 parent 9b61937 commit 89d357a
Show file tree
Hide file tree
Showing 23 changed files with 1,011 additions and 54 deletions.
5 changes: 0 additions & 5 deletions backend/project/endpoints/authentication/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ def microsoft_authentication():
res = requests.post(f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token",
data=data,
timeout=5)
if res.status_code != 200:
abort(make_response((
{"message":
"An error occured while trying to authenticate your authorization code"},
500)))
token = res.json()["access_token"]
profile_res = requests.get("https://graph.microsoft.com/v1.0/me",
headers={"Authorization":f"Bearer {token}"},
Expand Down
2 changes: 1 addition & 1 deletion backend/project/endpoints/authentication/me.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def get(self):
"""
Will return all user data associated with the access token in the request
"""
uid = get_jwt_identity
uid = get_jwt_identity()

return query_by_id_from_model(User,
"uid",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def get(self, course_id):
return query_selected_from_model(
CourseShareCode,
urljoin(f"{RESPONSE_URL}/", f"{str(course_id)}/", "join_codes"),
select_values=["join_code", "expiry_time"],
select_values=["join_code", "expiry_time", "for_admins"],
filters={"course_id": course_id}
)

Expand Down
4 changes: 2 additions & 2 deletions backend/project/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ def to_dict(self):
"""
return {
'uid': self.uid,
'role': self.role.name, # Convert the enum to a string
'display_name': self.display_name
'display_name': self.display_name,
'role': self.role.name # Convert the enum to a string
}
2 changes: 1 addition & 1 deletion backend/project/utils/models/user_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def get_user(user_id):
db.session.rollback()
abort(make_response(({"message": "An error occurred while fetching the user"}
, 500)))
if not user:
if user is None:
abort(make_response(({"message":f"User with id: {user_id} not found"}, 404)))
return user

Expand Down
2 changes: 1 addition & 1 deletion backend/project/utils/query_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def query_by_id_from_model(model: DeclarativeMeta,
if not result:
return {"message": "Resource not found", "url": base_url}, 404
return {
"data": result,
"data": result.to_dict() if hasattr(result, "to_dict") else result,
"message": "Resource fetched correctly",
"url": urljoin(f"{base_url}/", str(column_id))}, 200
except SQLAlchemyError:
Expand Down
2 changes: 2 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ dist
dist-ssr
*.local

.env

# Editor directories and files
.vscode/*
!.vscode/extensions.json
Expand Down
22 changes: 17 additions & 5 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@
"test": "npm run cypress:test"
},
"dependencies": {
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.15.10",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@mui/icons-material": "^5.15.15",
"@mui/material": "^5.15.15",
"@mui/lab": "^5.0.0-alpha.170",
"@mui/material": "^5.15.10",
"@mui/styled-engine-sc": "^6.0.0-alpha.16",
"@mui/x-data-grid": "^7.1.1",
"@mui/x-date-pickers": "^7.1.1",
"axios": "^1.6.8",
"dayjs": "^1.11.10",
"debounce": "^2.0.0",
"downloadjs": "^1.4.7",
"i18next-browser-languagedetector": "^7.2.0",
"i18next-http-backend": "^2.5.0",
Expand Down
44 changes: 43 additions & 1 deletion frontend/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,43 @@
"welcomeDescription": "Welcome to Peristerónas, the online submission platform of UGent",
"login": "Login"
},
"courseDetailTeacher": {
"title": "Course Details",
"deleteCourse": "Delete Course",
"unauthorizedDelete": "You are unauthorized to delete this course",
"noCoursesFound": "No courses found",
"noProjects": "No projects",
"noStudents": "No students in this course",
"joinCodes": "Join Codes",
"forAdmins": "For Admins",
"forStudents": "For Students",
"noExpiryDate": "No expiry date",
"expiryDate": "Expiry Date",
"newJoinCode": "New Join Code",
"deleteSelected": "Delete Selected Students",
"projects": "Projects",
"newProject": "New Project",
"assistantList": "List of co-teachers/assistants",
"newTeacher": "New teacher",
"studentList": "List of students",
"newStudent": "New student(s)",
"deadline": "Deadline",
"teacher": "Teacher",
"view": "View",
"admins": "Admins",
"students": "Students"
},
"allCoursesTeacher": {
"title": "All Courses",
"courseForm": "Course Form",
"courseName": "Course Name",
"submit": "Submit",
"emptyCourseNameError": "Course name should not be empty",
"cancel": "Cancel",
"create": "Create",
"activeCourses": "Active Courses",
"archivedCourses":"Archived Courses"
},
"courseForm": {
"courseName": "Course Name",
"submit": "Submit",
Expand All @@ -42,7 +79,12 @@
"daysAgo": "days ago",
"hoursAgo": "hours ago",
"minutesAgo": "minutes ago",
"justNow": "just now"
"justNow": "just now",
"yearsLater": "years later",
"monthsLater": "months later",
"daysLater": "days later",
"hoursLater": "hours later",
"minutesLater": "minutes later"
},
"error": {
"pageNotFound": "Page Not Found",
Expand Down
44 changes: 43 additions & 1 deletion frontend/public/locales/nl/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,43 @@
"welcomeDescription": "Welkom bij Peristerónas, het online indieningsplatform van UGent",
"login": "Aanmelden"
},
"courseDetailTeacher": {
"title": "Vak Details",
"noCoursesFound": "Geen vakken gevonden",
"unauthorizedDelete": "U heeft niet de juiste rechten om dit vak te verwijderen",
"noProjects": "Geen projecten",
"noStudents": "Geen studenten voor dit vak",
"deleteCourse": "Verwijder vak",
"joinCodes": "Join Codes",
"forAdmins": "Voor Admins",
"forStudents": "Voor Studenten",
"noExpiryDate": "Geen vervaldatum",
"expiryDate": "Vervaldatum",
"newJoinCode": "Nieuwe Join Code",
"deleteSelected": "Verwijder geselecteerde studenten",
"projects": "Projecten",
"newProject": "Nieuw Project",
"assistantList": "Lijst co-leerkrachten/assistenten",
"newTeacher": "Nieuwe leerkracht",
"studentList": "Lijst studenten",
"newStudent": "Nieuwe student(en)",
"deadline": "Deadline",
"teacher": "Leerkracht",
"view": "Bekijk",
"admins": "Admins",
"students": "Studenten"
},
"allCoursesTeacher": {
"title": "Alle Vakken",
"courseForm": "Vak Form",
"courseName": "Vak Naam",
"submit": "Opslaan",
"emptyCourseNameError": "Vak naam mag niet leeg zijn",
"cancel": "Annuleer",
"create": "Nieuw Vak",
"activeCourses": "Actieve Vakken",
"archivedCourses":"Gearchiveerde Vakken"
},
"courseForm": {
"courseName": "Vak Naam",
"submit": "Opslaan",
Expand Down Expand Up @@ -74,7 +111,12 @@
"daysAgo": "dagen geleden",
"hoursAgo": "uur geleden",
"minutesAgo": "minuten geleden",
"justNow": "Zonet"
"justNow": "Zonet",
"yearsLater": "jaren later",
"monthsLater": "maanden later",
"daysLater": "dagen later",
"hoursLater": "uur later",
"minutesLater": "minuten later"
},
"projectsOverview": {
"past_deadline": "Verlopen Projecten",
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Route, RouterProvider, createBrowserRouter, createRoutesFromElements } from "react-router-dom";
import Layout from "./components/Header/Layout";
import { AllCoursesTeacher } from "./components/Courses/AllCoursesTeacher";
import { CourseDetailTeacher } from "./components/Courses/CourseDetailTeacher";
import { dataLoaderCourseDetail, dataLoaderCourses } from "./components/Courses/CourseUtils";
import LanguagePath from "./components/LanguagePath";
import ProjectView from "./pages/project/projectView/ProjectView";
import { ErrorBoundary } from "./pages/error/ErrorBoundary.tsx";
Expand All @@ -20,6 +23,10 @@ const router = createBrowserRouter(
<Route path=":projectId" element={<ProjectView />}>
</Route>
</Route>
<Route path="courses">
<Route index element={<AllCoursesTeacher />} loader={dataLoaderCourses}/>
<Route path=":courseId" element={<CourseDetailTeacher />} loader={dataLoaderCourseDetail} />
</Route>
<Route path="projects">
<Route index element={<ProjectOverView/>} loader={fetchProjectPage}/>
<Route path="create" element={<ProjectCreateHome />} />
Expand Down
79 changes: 79 additions & 0 deletions frontend/src/components/Courses/AllCoursesTeacher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Button, Dialog, DialogActions, DialogTitle, FormControl, FormHelperText, Grid, Input, InputLabel } from "@mui/material";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { SideScrollableCourses } from "./CourseUtilComponents";
import { Course, callToApiToCreateCourse } from "./CourseUtils";
import { Title } from "../Header/Title";
import { useLoaderData } from "react-router-dom";

/**
* @returns A jsx component representing all courses for a teacher
*/
export function AllCoursesTeacher(): JSX.Element {
const [open, setOpen] = useState(false);
const courses = (useLoaderData() as Course[]);

const [courseName, setCourseName] = useState('');
const [error, setError] = useState('');

const navigate = useNavigate();

const { t } = useTranslation('translation', { keyPrefix: 'allCoursesTeacher' });

const handleClickOpen = () => {
setOpen(true);
};

const handleClose = () => {
setOpen(false);
};

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setCourseName(event.target.value);
setError(''); // Clearing error message when user starts typing
};

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); // Prevents the default form submission behaviour

if (!courseName.trim()) {
setError(t('emptyCourseNameError'));
return;
}

const data = { name: courseName };
callToApiToCreateCourse(JSON.stringify(data), navigate);
};
return (
<>
<Title title={t('title')}></Title>
<Grid container direction={'column'} style={{marginTop: '1rem', width:'100vw', height: '80vh'}}>
<SideScrollableCourses courses={courses}></SideScrollableCourses>
<Dialog open={open} onClose={handleClose}>
<DialogTitle>{t('courseForm')}</DialogTitle>
<form style={{ margin: "2rem" }} onSubmit={handleSubmit}>
<FormControl>
<InputLabel htmlFor="course-name">{t('courseName')}</InputLabel>
<Input
id="course-name"
value={courseName}
onChange={handleInputChange}
error={!!error}
aria-describedby="my-helper-text"
/>
{error && <FormHelperText id="my-helper-text">{error}</FormHelperText>}
</FormControl>
<DialogActions>
<Button onClick={handleClose}>{t('cancel')}</Button>
<Button type="submit">{t('submit')}</Button>
</DialogActions>
</form>
</Dialog>
<Grid item style={{position: "absolute", left: "2rem", bottom: "5rem"}}>
<Button onClick={handleClickOpen} >{t('create')}</Button>
</Grid>
</Grid>
</>
);
}
Loading

0 comments on commit 89d357a

Please sign in to comment.