diff --git a/frontend/src/i18n/locale/de.json b/frontend/src/i18n/locale/de.json index 15975e97..3988575d 100644 --- a/frontend/src/i18n/locale/de.json +++ b/frontend/src/i18n/locale/de.json @@ -82,5 +82,7 @@ "reject": "zurückweisen", "areusure": "Sind Sie sicher?", "yes": "ja", - "teachers": "Lehrer" + "teachers": "Lehrer", + "rejected": "abgelehnt", + "accepted": "akzeptiert" } diff --git a/frontend/src/i18n/locale/en.json b/frontend/src/i18n/locale/en.json index 76a9bcf2..630790ad 100644 --- a/frontend/src/i18n/locale/en.json +++ b/frontend/src/i18n/locale/en.json @@ -82,5 +82,7 @@ "reject": "reject", "areusure": "Are you sure?", "yes": "yes", - "teachers": "teachers" + "teachers": "teachers", + "accepted": "accepted", + "rejected": "rejected" } diff --git a/frontend/src/i18n/locale/fr.json b/frontend/src/i18n/locale/fr.json index d5eed78a..777d0a9a 100644 --- a/frontend/src/i18n/locale/fr.json +++ b/frontend/src/i18n/locale/fr.json @@ -82,5 +82,7 @@ "reject": "rejeter", "areusure": "Êtes-vous sûr?", "yes": "oui", - "teachers": "enseignants" + "teachers": "enseignants", + "accepted": "acceptée", + "rejected": "rejetée" } diff --git a/frontend/src/i18n/locale/nl.json b/frontend/src/i18n/locale/nl.json index 1c2b2b37..754592cd 100644 --- a/frontend/src/i18n/locale/nl.json +++ b/frontend/src/i18n/locale/nl.json @@ -82,5 +82,7 @@ "reject": "weiger", "areusure": "Bent u zeker?", "yes": "ja", - "teachers": "leerkrachten" + "teachers": "leerkrachten", + "accepted": "geaccepteerd", + "rejected": "geweigerd" } diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 23695680..0277dd64 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -3,7 +3,6 @@ import SingleAssignment from "@/views/assignments/SingleAssignment.vue"; import SingleClass from "@/views/classes/SingleClass.vue"; import SingleDiscussion from "@/views/discussions/SingleDiscussion.vue"; import NotFound from "@/components/errors/NotFound.vue"; -import CreateClass from "@/views/classes/CreateClass.vue"; import CreateAssignment from "@/views/assignments/CreateAssignment.vue"; import CreateDiscussion from "@/views/discussions/CreateDiscussion.vue"; import CallbackPage from "@/views/CallbackPage.vue"; @@ -84,12 +83,6 @@ const router = createRouter({ component: SingleAssignment, meta: { requiresAuth: true }, }, - { - path: "/class/create", - name: "CreateClass", - component: CreateClass, - meta: { requiresAuth: true }, - }, { path: "/class/:id", name: "SingleClass", diff --git a/frontend/src/views/classes/CreateClass.vue b/frontend/src/views/classes/CreateClass.vue deleted file mode 100644 index 1a35a59f..00000000 --- a/frontend/src/views/classes/CreateClass.vue +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/frontend/src/views/classes/SingleClass.vue b/frontend/src/views/classes/SingleClass.vue index ea413282..f058d6be 100644 --- a/frontend/src/views/classes/SingleClass.vue +++ b/frontend/src/views/classes/SingleClass.vue @@ -2,72 +2,82 @@ import { useI18n } from "vue-i18n"; import authState from "@/services/auth/auth-service.ts"; import { onMounted, ref } from "vue"; - import type { ClassDTO } from "@dwengo-1/common/interfaces/class"; import { useRoute } from "vue-router"; - import { ClassController, type ClassResponse } from "@/controllers/classes"; + import type { ClassResponse } from "@/controllers/classes"; import type { JoinRequestsResponse, StudentsResponse } from "@/controllers/students"; import type { StudentDTO } from "@dwengo-1/common/interfaces/student"; import UsingQueryResult from "@/components/UsingQueryResult.vue"; import { useTeacherJoinRequestsQuery, useUpdateJoinRequestMutation } from "@/queries/teachers"; import type { ClassJoinRequestDTO } from "@dwengo-1/common/interfaces/class-join-request"; + import { useClassDeleteStudentMutation, useClassQuery, useClassStudentsQuery } from "@/queries/classes"; const { t } = useI18n(); - // Username of logged in teacher - const username = ref(undefined); - const classController: ClassController = new ClassController(); - - // Find class id from route const route = useRoute(); const classId: string = route.params.id as string; + const username = ref(undefined); + const isLoading = ref(false); + const isError = ref(false); + const errorMessage = ref(""); - const isLoading = ref(true); - const currentClass = ref(undefined); - const students = ref([]); + // Queries used to access the backend and catch loading or errors + // Gets the class a teacher wants to manage + const getClass = useClassQuery(classId); + // Get all students part of the class + const getStudents = useClassStudentsQuery(classId); + // Get all join requests for this class const joinRequestsQuery = useTeacherJoinRequestsQuery(username, classId); + // Handle accepting or rejecting join requests const { mutate } = useUpdateJoinRequestMutation(); + // Handle deletion of a student from the class + const { mutate: deleteStudentMutation } = useClassDeleteStudentMutation(); - // Find the username of the logged in user so it can be used to fetch other information - // When loading the page + // Load current user before rendering the page onMounted(async () => { - const userObject = await authState.loadUser(); - username.value = userObject?.profile?.preferred_username ?? undefined; - - // Get class of which information should be shown - const classResponse: ClassResponse = await classController.getById(classId); - if (classResponse && classResponse.class) { - currentClass.value = classResponse.class; + isLoading.value = true; + try { + const userObject = await authState.loadUser(); + username.value = userObject!.profile!.preferred_username; + } catch (error) { + isError.value = true; + errorMessage.value = error instanceof Error ? error.message : String(error); + } finally { isLoading.value = false; } - - // Fetch all students of the class - const studentsResponse: StudentsResponse = await classController.getStudents(classId); - if (studentsResponse && studentsResponse.students) students.value = studentsResponse.students as StudentDTO[]; }); - // TODO: Boolean that handles visibility for dialogs - // Popup to verify removing student + // Used to set the visibility of the dialog const dialog = ref(false); + // Student selected for deletion const selectedStudent = ref(null); + // Let the teacher verify deletion of a student function showPopup(s: StudentDTO): void { selectedStudent.value = s; dialog.value = true; } - // Remove student from class async function removeStudentFromclass(): Promise { - // TODO: replace by query - if (selectedStudent.value) await classController.deleteStudent(classId, selectedStudent.value.username); - dialog.value = false; - - selectedStudent.value = null; - //TODO when query; reload table so student not longer in table + // Delete student from class + deleteStudentMutation( + { id: classId, username: selectedStudent.value!.username }, + { + onSuccess: async () => { + dialog.value = false; + await getStudents.refetch(); + showSnackbar(t("success"), "success"); + }, + onError: (e) => { + dialog.value = false; + showSnackbar(t("failed") + ": " + e.message, "error"); + }, + }, + ); } - // TODO: query + relaoding - function handleJoinRequest(c: ClassJoinRequestDTO, accepted: boolean) : void { + function handleJoinRequest(c: ClassJoinRequestDTO, accepted: boolean): void { + // Handle acception or rejection of a join request mutate( { teacherUsername: username.value!, @@ -76,22 +86,32 @@ accepted: accepted, }, { - onSuccess: () => { - showSnackbar(t("sent"), "success"); + onSuccess: async () => { + if (accepted) { + await joinRequestsQuery.refetch(); + await getStudents.refetch(); + + showSnackbar(t("accepted"), "success"); + } else { + await joinRequestsQuery.refetch(); + showSnackbar(t("rejected"), "success"); + } }, onError: (e) => { - // ShowSnackbar(t("failed") + ": " + e.message, "error"); - throw e; + showSnackbar(t("failed") + ": " + e.message, "error"); }, }, ); } + + // Default of snackbar values const snackbar = ref({ visible: false, message: "", color: "success", }); + // Function to show snackbar on success or failure function showSnackbar(message: string, color: string): void { snackbar.value.message = message; snackbar.value.color = color; @@ -101,131 +121,144 @@