From 1c99b03554c02869a5ec69c7645de744fce619ce Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 19:26:38 +0200 Subject: [PATCH 01/26] feat: class queries useClass en useClasses --- frontend/src/queries/classes.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 frontend/src/queries/classes.ts diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts new file mode 100644 index 00000000..68e07cc0 --- /dev/null +++ b/frontend/src/queries/classes.ts @@ -0,0 +1,30 @@ +import { ClassController, type ClassesResponse, type ClassResponse } from "@/controllers/classes"; +import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; +import { computed, toValue, type MaybeRefOrGetter } from "vue"; + +const classController = new ClassController(); + +function classesQueryKey() { + return ["students"]; +} +function classQueryKey(classid: string) { + return ["student", classid]; +} + + +export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => (classesQueryKey())), + queryFn: async () => classController.getAll(toValue(full)), + }); +} + +export function useClassQuery( + id: MaybeRefOrGetter, +): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => classQueryKey(toValue(id)!)), + queryFn: async () => classController.getById(toValue(id)!), + enabled: () => Boolean(toValue(id)), + }); +} \ No newline at end of file From c05ad9a702c61e3fe60011c37095676eec0891a0 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 19:42:02 +0200 Subject: [PATCH 02/26] feat: class queries students, teachers, teacher invitations GET --- frontend/src/queries/classes.ts | 48 ++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 68e07cc0..897d7e9e 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -1,16 +1,25 @@ import { ClassController, type ClassesResponse, type ClassResponse } from "@/controllers/classes"; +import type { StudentsResponse } from "@/controllers/students"; import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; const classController = new ClassController(); function classesQueryKey() { - return ["students"]; + return ["classes"]; } function classQueryKey(classid: string) { - return ["student", classid]; + return ["class", classid]; +} +function classStudentsKey(classid: string) { + return ["class-students", classid]; +} +function classTeachersKey(classid: string) { + return ["class-teachers", classid]; +} +function classTeacherInvitationsKey(classid: string) { + return ["class-teacher-invitations", classid]; } - export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { return useQuery({ @@ -27,4 +36,37 @@ export function useClassQuery( queryFn: async () => classController.getById(toValue(id)!), enabled: () => Boolean(toValue(id)), }); +} + +export function useClassStudentsQuery( + id: MaybeRefOrGetter, + full: MaybeRefOrGetter = true +): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => classStudentsKey(toValue(id)!)), + queryFn: async () => classController.getStudents(toValue(id)!, toValue(full)!), + enabled: () => Boolean(toValue(id)), + }) +} + +export function useClassTeachersQuery( + id: MaybeRefOrGetter, + full: MaybeRefOrGetter = true +): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => classTeachersKey(toValue(id)!)), + queryFn: async () => classController.getTeachers(toValue(id)!, toValue(full)!), + enabled: () => Boolean(toValue(id)), + }); +} + +export function useClassTeacherInvitationsQuery( + id: MaybeRefOrGetter, + full: MaybeRefOrGetter = true +): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => classTeacherInvitationsKey(toValue(id)!)), + queryFn: async () => classController.getTeacherInvitations(toValue(id)!, toValue(full)!), + enabled: () => Boolean(toValue(id)), + }); } \ No newline at end of file From a2ae4319577e28e8c9f8c2fad47dafca49c8e346 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 19:46:05 +0200 Subject: [PATCH 03/26] feat: class queries assignments GET --- frontend/src/queries/classes.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 897d7e9e..00eb3bf9 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -20,6 +20,9 @@ function classTeachersKey(classid: string) { function classTeacherInvitationsKey(classid: string) { return ["class-teacher-invitations", classid]; } +function classAssignmentsKey(classid: string) { + return ["class-assignments", classid]; +} export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { return useQuery({ @@ -69,4 +72,15 @@ export function useClassTeacherInvitationsQuery( queryFn: async () => classController.getTeacherInvitations(toValue(id)!, toValue(full)!), enabled: () => Boolean(toValue(id)), }); +} + +export function useClassAssignmentsQuery( + id: MaybeRefOrGetter, + full: MaybeRefOrGetter = true +): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => classAssignmentsKey(toValue(id)!)), + queryFn: async () => classController.getAssignments(toValue(id)!, toValue(full)!), + enabled: () => Boolean(toValue(id)), + }); } \ No newline at end of file From 45a36a94a58f5df8548a17111e80156f26532f5b Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 21:16:28 +0200 Subject: [PATCH 04/26] feat: class query get, delete, put geimplementeerd --- frontend/src/queries/classes.ts | 45 ++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 00eb3bf9..6aca5e7f 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -1,6 +1,7 @@ import { ClassController, type ClassesResponse, type ClassResponse } from "@/controllers/classes"; import type { StudentsResponse } from "@/controllers/students"; -import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; +import type { ClassDTO } from "@dwengo-1/common/interfaces/class"; +import { QueryClient, useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; const classController = new ClassController(); @@ -24,6 +25,15 @@ function classAssignmentsKey(classid: string) { return ["class-assignments", classid]; } +async function invalidateAll(classid: string, queryClient: QueryClient): Promise { + await queryClient.invalidateQueries({ queryKey: ["classes"] }); + await queryClient.invalidateQueries({ queryKey: classQueryKey(classid) }); + await queryClient.invalidateQueries({ queryKey: classStudentsKey(classid) }); + await queryClient.invalidateQueries({ queryKey: classTeachersKey(classid) }); + await queryClient.invalidateQueries({ queryKey: classAssignmentsKey(classid) }); + await queryClient.invalidateQueries({ queryKey: classTeacherInvitationsKey(classid) }); +} + export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { return useQuery({ queryKey: computed(() => (classesQueryKey())), @@ -41,6 +51,39 @@ export function useClassQuery( }); } +export function useCreateClassMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (data) => classController.createClass(data), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ["classes"] }); + }, + }); +} + +export function useDeleteClassMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (id) => classController.deleteClass(id), + onSuccess: async (data) => { + await invalidateAll(data.class.id, queryClient); + }, + }); +} + +export function useUpdateClassMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (data) => classController.updateClass(data.id, data), + onSuccess: async (data) => { + await invalidateAll(data.class.id, queryClient); + }, + }); +} + export function useClassStudentsQuery( id: MaybeRefOrGetter, full: MaybeRefOrGetter = true From 02076212fee06958bbf2dd650277414ed8789694 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 21:23:17 +0200 Subject: [PATCH 05/26] feat: class query student en teacher POST --- frontend/src/queries/classes.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 6aca5e7f..30ca7bee 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -6,6 +6,7 @@ import { computed, toValue, type MaybeRefOrGetter } from "vue"; const classController = new ClassController(); +/* Query cache keys */ function classesQueryKey() { return ["classes"]; } @@ -25,6 +26,7 @@ function classAssignmentsKey(classid: string) { return ["class-assignments", classid]; } +/* Function to invalidate all caches with certain class id */ async function invalidateAll(classid: string, queryClient: QueryClient): Promise { await queryClient.invalidateQueries({ queryKey: ["classes"] }); await queryClient.invalidateQueries({ queryKey: classQueryKey(classid) }); @@ -34,6 +36,7 @@ async function invalidateAll(classid: string, queryClient: QueryClient): Promise await queryClient.invalidateQueries({ queryKey: classTeacherInvitationsKey(classid) }); } +/* Queries */ export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { return useQuery({ queryKey: computed(() => (classesQueryKey())), @@ -95,6 +98,18 @@ export function useClassStudentsQuery( }) } +export function useCreateClassStudentMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ id, username }) => classController.addStudent(id, username), + onSuccess: async (data) => { + await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classStudentsKey(data.class.id) }); + }, + }); +} + export function useClassTeachersQuery( id: MaybeRefOrGetter, full: MaybeRefOrGetter = true @@ -106,6 +121,18 @@ export function useClassTeachersQuery( }); } +export function useCreateClassTeacherMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ id, username }) => classController.addTeacher(id, username), + onSuccess: async (data) => { + await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classTeachersKey(data.class.id) }); + }, + }); +} + export function useClassTeacherInvitationsQuery( id: MaybeRefOrGetter, full: MaybeRefOrGetter = true From 77ca390bd2ea1bc5cbeabf2f23df2a3c00164bc2 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 21:28:04 +0200 Subject: [PATCH 06/26] feat: class queries student & teacher DELETE --- frontend/src/queries/classes.ts | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 30ca7bee..1ecc0bce 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -98,7 +98,7 @@ export function useClassStudentsQuery( }) } -export function useCreateClassStudentMutation(): UseMutationReturnType { +export function useClassAddStudentMutation(): UseMutationReturnType { const queryClient = useQueryClient(); return useMutation({ @@ -110,6 +110,18 @@ export function useCreateClassStudentMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ id, username }) => classController.deleteStudent(id, username), + onSuccess: async (data) => { + await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classStudentsKey(data.class.id) }); + }, + }); +} + export function useClassTeachersQuery( id: MaybeRefOrGetter, full: MaybeRefOrGetter = true @@ -121,7 +133,7 @@ export function useClassTeachersQuery( }); } -export function useCreateClassTeacherMutation(): UseMutationReturnType { +export function useClassAddTeacherMutation(): UseMutationReturnType { const queryClient = useQueryClient(); return useMutation({ @@ -133,6 +145,18 @@ export function useCreateClassTeacherMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ id, username }) => classController.deleteTeacher(id, username), + onSuccess: async (data) => { + await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classTeachersKey(data.class.id) }); + }, + }); +} + export function useClassTeacherInvitationsQuery( id: MaybeRefOrGetter, full: MaybeRefOrGetter = true From 5c9314aa5968a37ecfda9ba868899c65ef87c8c4 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 21:53:41 +0200 Subject: [PATCH 07/26] feat: group queries voor alle GETs --- frontend/src/controllers/groups.ts | 6 ++- frontend/src/queries/groups.ts | 74 ++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 frontend/src/queries/groups.ts diff --git a/frontend/src/controllers/groups.ts b/frontend/src/controllers/groups.ts index de6592b5..361dd9d3 100644 --- a/frontend/src/controllers/groups.ts +++ b/frontend/src/controllers/groups.ts @@ -16,11 +16,15 @@ export class GroupController extends BaseController { super(`class/${classid}/assignments/${assignmentNumber}/groups`); } + update(classid: string, assignmentNumber: number) { + this.basePath = `class/${classid}/assignments/${assignmentNumber}/groups`; + } + async getAll(full = true): Promise { return this.get(`/`, { full }); } - async getByNumber(num: number): Promise { + async getByNumber(num: number | string): Promise { return this.get(`/${num}`); } diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts new file mode 100644 index 00000000..6465ce87 --- /dev/null +++ b/frontend/src/queries/groups.ts @@ -0,0 +1,74 @@ +import type { ClassesResponse } from "@/controllers/classes"; +import { GroupController, type GroupResponse, type GroupsResponse } from "@/controllers/groups"; +import type { QuestionsResponse } from "@/controllers/questions"; +import type { SubmissionsResponse } from "@/controllers/submissions"; +import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; +import { computed, toValue, type MaybeRefOrGetter } from "vue"; + +const groupController = new GroupController('', 0); + +function groupsQueryKey(classid: string, assignmentNumber: number) { + return [ "groups", classid, assignmentNumber ]; +} +function groupQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { + return [ "group", classid, assignmentNumber, groupNumber ]; +} +function groupSubmissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { + return [ "group-submissions", classid, assignmentNumber, groupNumber ]; +} + +export function useGroupsQuery( + classid: string, + assignmentNumber: number, +): UseQueryReturnType { + groupController.update(classid, assignmentNumber); + + return useQuery({ + queryKey: computed(() => (groupsQueryKey(classid, assignmentNumber))), + queryFn: async () => groupController.getAll(), + }); +} + +export function useGroupQuery( + classid: string, + assignmentNumber: number, + groupNumber: MaybeRefOrGetter, +): UseQueryReturnType { + groupController.update(classid, assignmentNumber); + + return useQuery({ + queryKey: computed(() => groupQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryFn: async () => groupController.getByNumber(toValue(groupNumber)!), + enabled: () => !isNaN(Number(toValue(groupNumber))), + }); +} + +export function useGroupSubmissionsQuery( + classid: string, + assignmentNumber: number, + groupNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter = true, +): UseQueryReturnType { + groupController.update(classid, assignmentNumber); + + return useQuery({ + queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), + enabled: () => !isNaN(Number(toValue(groupNumber))), + }); +} + +export function useGroupQuestionsQuery( + classid: string, + assignmentNumber: number, + groupNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter = true, +): UseQueryReturnType { + groupController.update(classid, assignmentNumber); + + return useQuery({ + queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), + enabled: () => !isNaN(Number(toValue(groupNumber))), + }); +} From f8ac37397d63a7d177b8deee897c41b12f5dc7fb Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 21:56:39 +0200 Subject: [PATCH 08/26] fix: foute key in group query questions gefixt --- frontend/src/queries/groups.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index 6465ce87..a0acd37a 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -16,6 +16,9 @@ function groupQueryKey(classid: string, assignmentNumber: number, groupNumber: n function groupSubmissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { return [ "group-submissions", classid, assignmentNumber, groupNumber ]; } +function groupQuestionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { + return [ "group-questions", classid, assignmentNumber, groupNumber ]; +} export function useGroupsQuery( classid: string, @@ -67,7 +70,7 @@ export function useGroupQuestionsQuery( groupController.update(classid, assignmentNumber); return useQuery({ - queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryKey: computed(() => groupQuestionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), enabled: () => !isNaN(Number(toValue(groupNumber))), }); From d0044fa2195faa5f6ed06e60795950066eb4fc12 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 22:06:52 +0200 Subject: [PATCH 09/26] fix: group query toValue used in wrong places --- frontend/src/queries/groups.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index a0acd37a..dec35013 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -13,11 +13,11 @@ function groupsQueryKey(classid: string, assignmentNumber: number) { function groupQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { return [ "group", classid, assignmentNumber, groupNumber ]; } -function groupSubmissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { - return [ "group-submissions", classid, assignmentNumber, groupNumber ]; +function groupSubmissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number, full: boolean) { + return [ "group-submissions", classid, assignmentNumber, groupNumber, full ]; } -function groupQuestionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { - return [ "group-questions", classid, assignmentNumber, groupNumber ]; +function groupQuestionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number, full: boolean) { + return [ "group-questions", classid, assignmentNumber, groupNumber, full ]; } export function useGroupsQuery( @@ -55,7 +55,7 @@ export function useGroupSubmissionsQuery( groupController.update(classid, assignmentNumber); return useQuery({ - queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!, toValue(full)!)), queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), enabled: () => !isNaN(Number(toValue(groupNumber))), }); @@ -67,10 +67,10 @@ export function useGroupQuestionsQuery( groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { - groupController.update(classid, assignmentNumber); + groupController.update(toValue(classid)!, toValue(assignmentNumber)); return useQuery({ - queryKey: computed(() => groupQuestionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryKey: computed(() => groupQuestionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!, toValue(full)!)), queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), enabled: () => !isNaN(Number(toValue(groupNumber))), }); From 73e3871af0724aa11265bebabea3bfff533e219d Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 22:29:08 +0200 Subject: [PATCH 10/26] fix: groups queries types gefixt --- frontend/src/controllers/groups.ts | 36 ++++++++++--------- frontend/src/queries/groups.ts | 56 +++++++++++++++++++----------- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/frontend/src/controllers/groups.ts b/frontend/src/controllers/groups.ts index 361dd9d3..2ba54cb7 100644 --- a/frontend/src/controllers/groups.ts +++ b/frontend/src/controllers/groups.ts @@ -12,39 +12,43 @@ export interface GroupResponse { } export class GroupController extends BaseController { - constructor(classid: string, assignmentNumber: number) { - super(`class/${classid}/assignments/${assignmentNumber}/groups`); + constructor() { + super(''); } update(classid: string, assignmentNumber: number) { this.basePath = `class/${classid}/assignments/${assignmentNumber}/groups`; } - async getAll(full = true): Promise { - return this.get(`/`, { full }); + protected getBasePath(classid: string, assignmentNumber: number) { + return `class/${classid}/assignments/${assignmentNumber}/groups`; } - async getByNumber(num: number | string): Promise { - return this.get(`/${num}`); + async getAll(classid: string, assignmentNumber: number, full = true): Promise { + return this.get(`${this.getBasePath(classid, assignmentNumber)}/`, { full }); } - async createGroup(data: GroupDTO): Promise { - return this.post(`/`, data); + async getByNumber(classid: string, assignmentNumber: number, num: number | string): Promise { + return this.get(`${this.getBasePath(classid, assignmentNumber)}/${num}`); } - async deleteGroup(num: number): Promise { - return this.delete(`/${num}`); + async createGroup(classid: string, assignmentNumber: number, data: GroupDTO): Promise { + return this.post(`${this.getBasePath(classid, assignmentNumber)}/`, data); } - async updateGroup(num: number, data: Partial): Promise { - return this.put(`/${num}`, data); + async deleteGroup(classid: string, assignmentNumber: number, num: number): Promise { + return this.delete(`${this.getBasePath(classid, assignmentNumber)}/${num}`); } - async getSubmissions(groupNumber: number, full = true): Promise { - return this.get(`/${groupNumber}/submissions`, { full }); + async updateGroup(classid: string, assignmentNumber: number, num: number, data: Partial): Promise { + return this.put(`${this.getBasePath(classid, assignmentNumber)}/${num}`, data); } - async getQuestions(groupNumber: number, full = true): Promise { - return this.get(`/${groupNumber}/questions`, { full }); + async getSubmissions(classid: string, assignmentNumber: number, groupNumber: number, full = true): Promise { + return this.get(`${this.getBasePath(classid, assignmentNumber)}/${groupNumber}/submissions`, { full }); + } + + async getQuestions(classid: string, assignmentNumber: number, groupNumber: number, full = true): Promise { + return this.get(`${this.getBasePath(classid, assignmentNumber)}/${groupNumber}/questions`, { full }); } } diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index dec35013..def17524 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -5,7 +5,7 @@ import type { SubmissionsResponse } from "@/controllers/submissions"; import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; -const groupController = new GroupController('', 0); +const groupController = new GroupController(); function groupsQueryKey(classid: string, assignmentNumber: number) { return [ "groups", classid, assignmentNumber ]; @@ -20,15 +20,31 @@ function groupQuestionsQueryKey(classid: string, assignmentNumber: number, group return [ "group-questions", classid, assignmentNumber, groupNumber, full ]; } +function checkEnabled( + classid: string | undefined, + assignmentNumber: number | undefined, + groupNumber: number | undefined, +): boolean { + return Boolean(classid) && !isNaN(Number(groupNumber)) && !isNaN(Number(assignmentNumber)); +} +function toValues( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, +) { + return { cid: toValue(classid), an: toValue(assignmentNumber), gn: toValue(groupNumber) }; +} + export function useGroupsQuery( - classid: string, - assignmentNumber: number, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, ): UseQueryReturnType { - groupController.update(classid, assignmentNumber); + const { cid, an, gn } = toValues(classid, assignmentNumber, 1); return useQuery({ - queryKey: computed(() => (groupsQueryKey(classid, assignmentNumber))), - queryFn: async () => groupController.getAll(), + queryKey: computed(() => (groupsQueryKey(cid!, an!))), + queryFn: async () => groupController.getAll(cid!, an!), + enabled: () => checkEnabled(cid, an, 1), }); } @@ -37,12 +53,12 @@ export function useGroupQuery( assignmentNumber: number, groupNumber: MaybeRefOrGetter, ): UseQueryReturnType { - groupController.update(classid, assignmentNumber); + const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber); return useQuery({ - queryKey: computed(() => groupQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), - queryFn: async () => groupController.getByNumber(toValue(groupNumber)!), - enabled: () => !isNaN(Number(toValue(groupNumber))), + queryKey: computed(() => groupQueryKey(cid!, an!, gn!)), + queryFn: async () => groupController.getByNumber(cid!, an!, gn!), + enabled: () => checkEnabled(cid, an, gn), }); } @@ -52,26 +68,26 @@ export function useGroupSubmissionsQuery( groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { - groupController.update(classid, assignmentNumber); + const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber); return useQuery({ - queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!, toValue(full)!)), - queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), - enabled: () => !isNaN(Number(toValue(groupNumber))), + queryKey: computed(() => groupSubmissionsQueryKey(cid!, an!, gn!, toValue(full))), + queryFn: async () => groupController.getSubmissions(cid!, an!, gn!, toValue(full)), + enabled: () => checkEnabled(cid, an, gn), }); } export function useGroupQuestionsQuery( - classid: string, - assignmentNumber: number, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { - groupController.update(toValue(classid)!, toValue(assignmentNumber)); + const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber); return useQuery({ - queryKey: computed(() => groupQuestionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!, toValue(full)!)), - queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), - enabled: () => !isNaN(Number(toValue(groupNumber))), + queryKey: computed(() => groupQuestionsQueryKey(cid!, an!, gn!, toValue(full))), + queryFn: async () => groupController.getSubmissions(cid!, an!, gn!, toValue(full)), + enabled: () => checkEnabled(cid, an, gn), }); } From 363eba57451e9da30001c0f9e76548890db4be3b Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sat, 12 Apr 2025 18:21:43 +0200 Subject: [PATCH 11/26] feat: class query full parameter toegevoegd aan keys --- frontend/src/queries/classes.ts | 53 +++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 1ecc0bce..4faf5b19 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -7,22 +7,22 @@ import { computed, toValue, type MaybeRefOrGetter } from "vue"; const classController = new ClassController(); /* Query cache keys */ -function classesQueryKey() { - return ["classes"]; +function classesQueryKey(full: boolean) { + return ["classes", full]; } function classQueryKey(classid: string) { return ["class", classid]; } -function classStudentsKey(classid: string) { - return ["class-students", classid]; +function classStudentsKey(classid: string, full: boolean) { + return ["class-students", classid, full]; } -function classTeachersKey(classid: string) { - return ["class-teachers", classid]; +function classTeachersKey(classid: string, full: boolean) { + return ["class-teachers", classid, full]; } -function classTeacherInvitationsKey(classid: string) { - return ["class-teacher-invitations", classid]; +function classTeacherInvitationsKey(classid: string, full: boolean) { + return ["class-teacher-invitations", classid, full]; } -function classAssignmentsKey(classid: string) { +function classAssignmentsKey(classid: string, full: boolean) { return ["class-assignments", classid]; } @@ -30,16 +30,18 @@ function classAssignmentsKey(classid: string) { async function invalidateAll(classid: string, queryClient: QueryClient): Promise { await queryClient.invalidateQueries({ queryKey: ["classes"] }); await queryClient.invalidateQueries({ queryKey: classQueryKey(classid) }); - await queryClient.invalidateQueries({ queryKey: classStudentsKey(classid) }); - await queryClient.invalidateQueries({ queryKey: classTeachersKey(classid) }); - await queryClient.invalidateQueries({ queryKey: classAssignmentsKey(classid) }); - await queryClient.invalidateQueries({ queryKey: classTeacherInvitationsKey(classid) }); + for (let v of [true, false]) { + await queryClient.invalidateQueries({ queryKey: classStudentsKey(classid, v) }); + await queryClient.invalidateQueries({ queryKey: classTeachersKey(classid, v) }); + await queryClient.invalidateQueries({ queryKey: classAssignmentsKey(classid, v) }); + await queryClient.invalidateQueries({ queryKey: classTeacherInvitationsKey(classid, v) }); + } } /* Queries */ export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { return useQuery({ - queryKey: computed(() => (classesQueryKey())), + queryKey: computed(() => (classesQueryKey(toValue(full)))), queryFn: async () => classController.getAll(toValue(full)), }); } @@ -60,7 +62,8 @@ export function useCreateClassMutation(): UseMutationReturnType classController.createClass(data), onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: ["classes"] }); + await queryClient.invalidateQueries({ queryKey: classesQueryKey(true) }); + await queryClient.invalidateQueries({ queryKey: classesQueryKey(false) }); }, }); } @@ -92,7 +95,7 @@ export function useClassStudentsQuery( full: MaybeRefOrGetter = true ): UseQueryReturnType { return useQuery({ - queryKey: computed(() => classStudentsKey(toValue(id)!)), + queryKey: computed(() => classStudentsKey(toValue(id)!, toValue(full))), queryFn: async () => classController.getStudents(toValue(id)!, toValue(full)!), enabled: () => Boolean(toValue(id)), }) @@ -105,7 +108,8 @@ export function useClassAddStudentMutation(): UseMutationReturnType classController.addStudent(id, username), onSuccess: async (data) => { await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); - await queryClient.invalidateQueries({ queryKey: classStudentsKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classStudentsKey(data.class.id, true) }); + await queryClient.invalidateQueries({ queryKey: classStudentsKey(data.class.id, false) }); }, }); } @@ -117,7 +121,8 @@ export function useClassDeleteStudentMutation(): UseMutationReturnType classController.deleteStudent(id, username), onSuccess: async (data) => { await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); - await queryClient.invalidateQueries({ queryKey: classStudentsKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classStudentsKey(data.class.id, true) }); + await queryClient.invalidateQueries({ queryKey: classStudentsKey(data.class.id, false) }); }, }); } @@ -127,7 +132,7 @@ export function useClassTeachersQuery( full: MaybeRefOrGetter = true ): UseQueryReturnType { return useQuery({ - queryKey: computed(() => classTeachersKey(toValue(id)!)), + queryKey: computed(() => classTeachersKey(toValue(id)!, toValue(full))), queryFn: async () => classController.getTeachers(toValue(id)!, toValue(full)!), enabled: () => Boolean(toValue(id)), }); @@ -140,7 +145,8 @@ export function useClassAddTeacherMutation(): UseMutationReturnType classController.addTeacher(id, username), onSuccess: async (data) => { await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); - await queryClient.invalidateQueries({ queryKey: classTeachersKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classTeachersKey(data.class.id, true) }); + await queryClient.invalidateQueries({ queryKey: classTeachersKey(data.class.id, false) }); }, }); } @@ -152,7 +158,8 @@ export function useClassDeleteTeacherMutation(): UseMutationReturnType classController.deleteTeacher(id, username), onSuccess: async (data) => { await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); - await queryClient.invalidateQueries({ queryKey: classTeachersKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classTeachersKey(data.class.id, true) }); + await queryClient.invalidateQueries({ queryKey: classTeachersKey(data.class.id, false) }); }, }); } @@ -162,7 +169,7 @@ export function useClassTeacherInvitationsQuery( full: MaybeRefOrGetter = true ): UseQueryReturnType { return useQuery({ - queryKey: computed(() => classTeacherInvitationsKey(toValue(id)!)), + queryKey: computed(() => classTeacherInvitationsKey(toValue(id)!, toValue(full))), queryFn: async () => classController.getTeacherInvitations(toValue(id)!, toValue(full)!), enabled: () => Boolean(toValue(id)), }); @@ -173,7 +180,7 @@ export function useClassAssignmentsQuery( full: MaybeRefOrGetter = true ): UseQueryReturnType { return useQuery({ - queryKey: computed(() => classAssignmentsKey(toValue(id)!)), + queryKey: computed(() => classAssignmentsKey(toValue(id)!, toValue(full))), queryFn: async () => classController.getAssignments(toValue(id)!, toValue(full)!), enabled: () => Boolean(toValue(id)), }); From 9bda33123fc311612fc04c5c9402b53855659964 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sat, 12 Apr 2025 18:26:32 +0200 Subject: [PATCH 12/26] feat: group query full parameter toegevoegd aan keys --- frontend/src/queries/groups.ts | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index def17524..5ac9b40b 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -7,8 +7,8 @@ import { computed, toValue, type MaybeRefOrGetter } from "vue"; const groupController = new GroupController(); -function groupsQueryKey(classid: string, assignmentNumber: number) { - return [ "groups", classid, assignmentNumber ]; +function groupsQueryKey(classid: string, assignmentNumber: number, full: boolean) { + return [ "groups", classid, assignmentNumber, full ]; } function groupQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { return [ "group", classid, assignmentNumber, groupNumber ]; @@ -31,18 +31,19 @@ function toValues( classid: MaybeRefOrGetter, assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter, ) { - return { cid: toValue(classid), an: toValue(assignmentNumber), gn: toValue(groupNumber) }; + return { cid: toValue(classid), an: toValue(assignmentNumber), gn: toValue(groupNumber), f: toValue(full) }; } export function useGroupsQuery( classid: MaybeRefOrGetter, assignmentNumber: MaybeRefOrGetter, ): UseQueryReturnType { - const { cid, an, gn } = toValues(classid, assignmentNumber, 1); + const { cid, an, f } = toValues(classid, assignmentNumber, 1, true); return useQuery({ - queryKey: computed(() => (groupsQueryKey(cid!, an!))), + queryKey: computed(() => (groupsQueryKey(cid!, an!, f))), queryFn: async () => groupController.getAll(cid!, an!), enabled: () => checkEnabled(cid, an, 1), }); @@ -53,7 +54,7 @@ export function useGroupQuery( assignmentNumber: number, groupNumber: MaybeRefOrGetter, ): UseQueryReturnType { - const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber); + const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber, true); return useQuery({ queryKey: computed(() => groupQueryKey(cid!, an!, gn!)), @@ -68,11 +69,11 @@ export function useGroupSubmissionsQuery( groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { - const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber); + const { cid, an, gn, f } = toValues(classid, assignmentNumber, groupNumber, full); return useQuery({ - queryKey: computed(() => groupSubmissionsQueryKey(cid!, an!, gn!, toValue(full))), - queryFn: async () => groupController.getSubmissions(cid!, an!, gn!, toValue(full)), + queryKey: computed(() => groupSubmissionsQueryKey(cid!, an!, gn!, f)), + queryFn: async () => groupController.getSubmissions(cid!, an!, gn!, f), enabled: () => checkEnabled(cid, an, gn), }); } @@ -83,11 +84,11 @@ export function useGroupQuestionsQuery( groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { - const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber); + const { cid, an, gn, f } = toValues(classid, assignmentNumber, groupNumber, full); return useQuery({ - queryKey: computed(() => groupQuestionsQueryKey(cid!, an!, gn!, toValue(full))), - queryFn: async () => groupController.getSubmissions(cid!, an!, gn!, toValue(full)), + queryKey: computed(() => groupQuestionsQueryKey(cid!, an!, gn!, f)), + queryFn: async () => groupController.getSubmissions(cid!, an!, gn!, f), enabled: () => checkEnabled(cid, an, gn), }); } From 955be87da31c37dddb61ab081dfa20975c79a3b8 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sat, 12 Apr 2025 19:27:56 +0200 Subject: [PATCH 13/26] feat: group query POST, PUT, DELETE mutations --- frontend/src/queries/groups.ts | 71 +++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index 5ac9b40b..afb1fecb 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -2,7 +2,8 @@ import type { ClassesResponse } from "@/controllers/classes"; import { GroupController, type GroupResponse, type GroupsResponse } from "@/controllers/groups"; import type { QuestionsResponse } from "@/controllers/questions"; import type { SubmissionsResponse } from "@/controllers/submissions"; -import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; +import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; +import { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; const groupController = new GroupController(); @@ -50,8 +51,8 @@ export function useGroupsQuery( } export function useGroupQuery( - classid: string, - assignmentNumber: number, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, ): UseQueryReturnType { const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber, true); @@ -63,9 +64,69 @@ export function useGroupQuery( }); } +// TODO: find way to check if cid and an are not undefined. +// depends on how this function is used. +export function useCreateClassMutation( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, +): UseMutationReturnType { + const queryClient = useQueryClient(); + const { cid, an } = toValues(classid, assignmentNumber, 1, true); + + return useMutation({ + mutationFn: async (data) => groupController.createGroup(cid!, an!, data), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, false) }); + }, + }); +} + +export function useDeleteClassMutation( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, +): UseMutationReturnType { + const queryClient = useQueryClient(); + const { cid, an, gn } = toValues(classid, assignmentNumber, 1, true); + + return useMutation({ + mutationFn: async (id) => groupController.deleteGroup(cid!, an!, id), + onSuccess: async (_) => { + await queryClient.invalidateQueries({ queryKey: groupQueryKey(cid!, an!, gn!) }); + + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, false) }); + + await queryClient.invalidateQueries({ queryKey: groupSubmissionsQueryKey(cid!, an!, gn!, true) }); + await queryClient.invalidateQueries({ queryKey: groupSubmissionsQueryKey(cid!, an!, gn!, false) }); + + await queryClient.invalidateQueries({ queryKey: groupQuestionsQueryKey(cid!, an!, gn!, true) }); + await queryClient.invalidateQueries({ queryKey: groupQuestionsQueryKey(cid!, an!, gn!, false) }); + }, + }); +} + +export function useUpdateClassMutation( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, +): UseMutationReturnType { + const queryClient = useQueryClient(); + const { cid, an, gn } = toValues(classid, assignmentNumber, 1, true); + + return useMutation({ + mutationFn: async (data) => groupController.updateGroup(cid!, an!, gn!, data), + onSuccess: async (data) => { + await queryClient.invalidateQueries({ queryKey: groupQueryKey(cid!, an!, gn!) }); + + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, false) }); + }, + }); +} + export function useGroupSubmissionsQuery( - classid: string, - assignmentNumber: number, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { From 2cda69ef5ef4f79e0d4aeb9b6eed9b10e8bc3c29 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sat, 12 Apr 2025 19:56:04 +0200 Subject: [PATCH 14/26] feat: submission query get all en get --- frontend/src/controllers/submissions.ts | 8 ++- frontend/src/queries/submissions.ts | 66 +++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 frontend/src/queries/submissions.ts diff --git a/frontend/src/controllers/submissions.ts b/frontend/src/controllers/submissions.ts index 837d356c..1f281e62 100644 --- a/frontend/src/controllers/submissions.ts +++ b/frontend/src/controllers/submissions.ts @@ -11,7 +11,11 @@ export interface SubmissionResponse { export class SubmissionController extends BaseController { constructor(classid: string, assignmentNumber: number, groupNumber: number) { - super(`class/${classid}/assignments/${assignmentNumber}/groups/${groupNumber}`); + super(`class/${classid}/assignments/${assignmentNumber}/groups/${groupNumber}/submissions`); + } + + protected getBasePath(classid: string, assignmentNumber: number, groupNumber: number) { + return `class/${classid}/assignments/${assignmentNumber}/groups/${groupNumber}/submissions`; } async getAll(full = true): Promise { @@ -22,7 +26,7 @@ export class SubmissionController extends BaseController { return this.get(`/${submissionNumber}`); } - async createSubmission(data: unknown): Promise { + async createSubmission(data: SubmissionDTO): Promise { return this.post(`/`, data); } diff --git a/frontend/src/queries/submissions.ts b/frontend/src/queries/submissions.ts new file mode 100644 index 00000000..defa6c38 --- /dev/null +++ b/frontend/src/queries/submissions.ts @@ -0,0 +1,66 @@ +import { SubmissionController, type SubmissionResponse, type SubmissionsResponse } from "@/controllers/submissions"; +import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; +import { computed, toValue, type MaybeRefOrGetter } from "vue"; + +function submissionsQueryKey(classid: string, assignmentNumber: number, full: boolean) { + return [ "submissions", classid, assignmentNumber, full ]; +} +function submissionQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { + return [ "submission", classid, assignmentNumber, groupNumber ]; +} + +function checkEnabled( + classid: string | undefined, + assignmentNumber: number | undefined, + groupNumber: number | undefined, + submissionNumber: number | undefined +): boolean { + return Boolean(classid) + && !isNaN(Number(groupNumber)) + && !isNaN(Number(assignmentNumber)) + && !isNaN(Number(submissionNumber)); +} +function toValues( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, + submissionNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter, +) { + return { + cid: toValue(classid), + an: toValue(assignmentNumber), + gn: toValue(groupNumber), + sn: toValue(submissionNumber), + f: toValue(full) + }; +} + +export function useSubmissionsQuery( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter = true, +): UseQueryReturnType { + const { cid, an, gn, sn, f } = toValues(classid, assignmentNumber, groupNumber, 1, full); + + return useQuery({ + queryKey: computed(() => (submissionsQueryKey(cid!, an!, f))), + queryFn: async () => new SubmissionController(cid!, an!, gn!).getAll(f), + enabled: () => checkEnabled(cid, an, gn, sn), + }); +} + +export function useSubmissionQuery( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, +): UseQueryReturnType { + const { cid, an, gn, sn, f } = toValues(classid, assignmentNumber, groupNumber, 1, true); + + return useQuery({ + queryKey: computed(() => submissionQueryKey(cid!, an!, gn!)), + queryFn: async () => new SubmissionController(cid!, an!, gn!).getByNumber(sn!), + enabled: () => checkEnabled(cid, an, gn, sn), + }); +} \ No newline at end of file From 0784db3680d31eda1b52dbe74bd0e810e5ee82c2 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sat, 12 Apr 2025 20:01:53 +0200 Subject: [PATCH 15/26] feat: submission query POST en DELETE --- frontend/src/queries/groups.ts | 8 +++---- frontend/src/queries/submissions.ts | 37 ++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index afb1fecb..8d235d8f 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -66,7 +66,7 @@ export function useGroupQuery( // TODO: find way to check if cid and an are not undefined. // depends on how this function is used. -export function useCreateClassMutation( +export function useCreateGroupMutation( classid: MaybeRefOrGetter, assignmentNumber: MaybeRefOrGetter, ): UseMutationReturnType { @@ -82,7 +82,7 @@ export function useCreateClassMutation( }); } -export function useDeleteClassMutation( +export function useDeleteGroupMutation( classid: MaybeRefOrGetter, assignmentNumber: MaybeRefOrGetter, ): UseMutationReturnType { @@ -91,7 +91,7 @@ export function useDeleteClassMutation( return useMutation({ mutationFn: async (id) => groupController.deleteGroup(cid!, an!, id), - onSuccess: async (_) => { + onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: groupQueryKey(cid!, an!, gn!) }); await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); @@ -106,7 +106,7 @@ export function useDeleteClassMutation( }); } -export function useUpdateClassMutation( +export function useUpdateGroupMutation( classid: MaybeRefOrGetter, assignmentNumber: MaybeRefOrGetter, ): UseMutationReturnType { diff --git a/frontend/src/queries/submissions.ts b/frontend/src/queries/submissions.ts index defa6c38..5c0d74c3 100644 --- a/frontend/src/queries/submissions.ts +++ b/frontend/src/queries/submissions.ts @@ -1,5 +1,6 @@ import { SubmissionController, type SubmissionResponse, type SubmissionsResponse } from "@/controllers/submissions"; -import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; +import type { SubmissionDTO } from "@dwengo-1/common/interfaces/submission"; +import { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; function submissionsQueryKey(classid: string, assignmentNumber: number, full: boolean) { @@ -63,4 +64,38 @@ export function useSubmissionQuery( queryFn: async () => new SubmissionController(cid!, an!, gn!).getByNumber(sn!), enabled: () => checkEnabled(cid, an, gn, sn), }); +} + +export function useCreateSubmissionMutation( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, +): UseMutationReturnType { + const queryClient = useQueryClient(); + const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber, 1, true); + + return useMutation({ + mutationFn: async (data) => new SubmissionController(cid!, an!, gn!).createSubmission(data), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, true) }); + await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, false) }); + }, + }); +} + +export function useDeleteGroupMutation( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, +): UseMutationReturnType { + const queryClient = useQueryClient(); + const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber, 1, true); + + return useMutation({ + mutationFn: async (id) => new SubmissionController(cid!, an!, gn!).deleteSubmission(id), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, true) }); + await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, false) }); + }, + }); } \ No newline at end of file From 9c586143829a1c99dc344906d52ab3d29722be0c Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sat, 12 Apr 2025 20:13:14 +0200 Subject: [PATCH 16/26] refactor: group query gerefactord --- frontend/src/controllers/groups.ts | 40 ++++++++++++------------------ frontend/src/queries/groups.ts | 21 ++++++++-------- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/frontend/src/controllers/groups.ts b/frontend/src/controllers/groups.ts index 2ba54cb7..4c38290f 100644 --- a/frontend/src/controllers/groups.ts +++ b/frontend/src/controllers/groups.ts @@ -12,43 +12,35 @@ export interface GroupResponse { } export class GroupController extends BaseController { - constructor() { - super(''); + constructor(classid: string, assignmentNumber: number) { + super(`class/${classid}/assignments/${assignmentNumber}/groups`); } - update(classid: string, assignmentNumber: number) { - this.basePath = `class/${classid}/assignments/${assignmentNumber}/groups`; + async getAll(full = true): Promise { + return this.get(`/`, { full }); } - protected getBasePath(classid: string, assignmentNumber: number) { - return `class/${classid}/assignments/${assignmentNumber}/groups`; + async getByNumber(num: number): Promise { + return this.get(`/${num}`); } - async getAll(classid: string, assignmentNumber: number, full = true): Promise { - return this.get(`${this.getBasePath(classid, assignmentNumber)}/`, { full }); + async createGroup(data: GroupDTO): Promise { + return this.post(`/`, data); } - async getByNumber(classid: string, assignmentNumber: number, num: number | string): Promise { - return this.get(`${this.getBasePath(classid, assignmentNumber)}/${num}`); + async deleteGroup(num: number): Promise { + return this.delete(`/${num}`); } - async createGroup(classid: string, assignmentNumber: number, data: GroupDTO): Promise { - return this.post(`${this.getBasePath(classid, assignmentNumber)}/`, data); + async updateGroup(num: number, data: Partial): Promise { + return this.put(`/${num}`, data); } - async deleteGroup(classid: string, assignmentNumber: number, num: number): Promise { - return this.delete(`${this.getBasePath(classid, assignmentNumber)}/${num}`); + async getSubmissions(num: number, full = true): Promise { + return this.get(`/${num}/submissions`, { full }); } - async updateGroup(classid: string, assignmentNumber: number, num: number, data: Partial): Promise { - return this.put(`${this.getBasePath(classid, assignmentNumber)}/${num}`, data); - } - - async getSubmissions(classid: string, assignmentNumber: number, groupNumber: number, full = true): Promise { - return this.get(`${this.getBasePath(classid, assignmentNumber)}/${groupNumber}/submissions`, { full }); - } - - async getQuestions(classid: string, assignmentNumber: number, groupNumber: number, full = true): Promise { - return this.get(`${this.getBasePath(classid, assignmentNumber)}/${groupNumber}/questions`, { full }); + async getQuestions(num: number, full = true): Promise { + return this.get(`/${num}/questions`, { full }); } } diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index 8d235d8f..7223ebc6 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -6,8 +6,6 @@ import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; import { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; -const groupController = new GroupController(); - function groupsQueryKey(classid: string, assignmentNumber: number, full: boolean) { return [ "groups", classid, assignmentNumber, full ]; } @@ -40,12 +38,13 @@ function toValues( export function useGroupsQuery( classid: MaybeRefOrGetter, assignmentNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter = true, ): UseQueryReturnType { - const { cid, an, f } = toValues(classid, assignmentNumber, 1, true); + const { cid, an, f } = toValues(classid, assignmentNumber, 1, full); return useQuery({ queryKey: computed(() => (groupsQueryKey(cid!, an!, f))), - queryFn: async () => groupController.getAll(cid!, an!), + queryFn: async () => new GroupController(cid!, an!).getAll(f), enabled: () => checkEnabled(cid, an, 1), }); } @@ -59,7 +58,7 @@ export function useGroupQuery( return useQuery({ queryKey: computed(() => groupQueryKey(cid!, an!, gn!)), - queryFn: async () => groupController.getByNumber(cid!, an!, gn!), + queryFn: async () => new GroupController(cid!, an!).getByNumber(gn!), enabled: () => checkEnabled(cid, an, gn), }); } @@ -74,7 +73,7 @@ export function useCreateGroupMutation( const { cid, an } = toValues(classid, assignmentNumber, 1, true); return useMutation({ - mutationFn: async (data) => groupController.createGroup(cid!, an!, data), + mutationFn: async (data) => new GroupController(cid!, an!).createGroup(data), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, false) }); @@ -90,7 +89,7 @@ export function useDeleteGroupMutation( const { cid, an, gn } = toValues(classid, assignmentNumber, 1, true); return useMutation({ - mutationFn: async (id) => groupController.deleteGroup(cid!, an!, id), + mutationFn: async (id) => new GroupController(cid!, an!).deleteGroup(id), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: groupQueryKey(cid!, an!, gn!) }); @@ -114,8 +113,8 @@ export function useUpdateGroupMutation( const { cid, an, gn } = toValues(classid, assignmentNumber, 1, true); return useMutation({ - mutationFn: async (data) => groupController.updateGroup(cid!, an!, gn!, data), - onSuccess: async (data) => { + mutationFn: async (data) => new GroupController(cid!, an!).updateGroup(gn!, data), + onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: groupQueryKey(cid!, an!, gn!) }); await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); @@ -134,7 +133,7 @@ export function useGroupSubmissionsQuery( return useQuery({ queryKey: computed(() => groupSubmissionsQueryKey(cid!, an!, gn!, f)), - queryFn: async () => groupController.getSubmissions(cid!, an!, gn!, f), + queryFn: async () => new GroupController(cid!, an!).getSubmissions(gn!, f), enabled: () => checkEnabled(cid, an, gn), }); } @@ -149,7 +148,7 @@ export function useGroupQuestionsQuery( return useQuery({ queryKey: computed(() => groupQuestionsQueryKey(cid!, an!, gn!, f)), - queryFn: async () => groupController.getSubmissions(cid!, an!, gn!, f), + queryFn: async () => new GroupController(cid!, an!).getSubmissions(gn!, f), enabled: () => checkEnabled(cid, an, gn), }); } From 802cbfb6e8d5674e86a8b56ccf5c684abbf3bfa7 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sat, 12 Apr 2025 20:14:24 +0200 Subject: [PATCH 17/26] fix: submission query todo toegevoegd --- frontend/src/queries/submissions.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/queries/submissions.ts b/frontend/src/queries/submissions.ts index 5c0d74c3..374d83d6 100644 --- a/frontend/src/queries/submissions.ts +++ b/frontend/src/queries/submissions.ts @@ -66,6 +66,8 @@ export function useSubmissionQuery( }); } +// TODO: find way to check if cid and an are not undefined. +// depends on how this function is used. export function useCreateSubmissionMutation( classid: MaybeRefOrGetter, assignmentNumber: MaybeRefOrGetter, From 3ee66d2f6799290e006c6e92a492791b34e80af0 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sat, 12 Apr 2025 21:26:42 +0200 Subject: [PATCH 18/26] feat: assignment query GET all en GET --- frontend/src/queries/assignments.ts | 54 +++++++++++++++++++++++++++++ frontend/src/queries/submissions.ts | 20 +++++------ 2 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 frontend/src/queries/assignments.ts diff --git a/frontend/src/queries/assignments.ts b/frontend/src/queries/assignments.ts new file mode 100644 index 00000000..a7a8caaf --- /dev/null +++ b/frontend/src/queries/assignments.ts @@ -0,0 +1,54 @@ +import { AssignmentController, type AssignmentsResponse } from "@/controllers/assignments"; +import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; +import { computed, toValue, type MaybeRefOrGetter } from "vue"; + +function assignmentsQueryKey(classid: string, full: boolean) { + return [ "assignments", classid, full ]; +} +function assignmentQueryKey(classid: string, assignmentNumber: number) { + return [ "assignment", classid, assignmentNumber ]; +} + +function checkEnabled( + classid: string | undefined, + assignmentNumber: number | undefined, + groupNumber: number | undefined, +): boolean { + return Boolean(classid) && !isNaN(Number(groupNumber)) && !isNaN(Number(assignmentNumber)); +} +function toValues( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter, +) { + return { cid: toValue(classid), an: toValue(assignmentNumber), gn: toValue(groupNumber), f: toValue(full) }; +} + +export function useAssignmentsQuery( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter = true, +): UseQueryReturnType { + const { cid, an, f } = toValues(classid, assignmentNumber, 1, full); + + return useQuery({ + queryKey: computed(() => (assignmentsQueryKey(cid!, f))), + queryFn: async () => new AssignmentController(cid!).getAll(f), + enabled: () => checkEnabled(cid, an, 1), + }); +} + +export function useAssignmentQuery( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, +): UseQueryReturnType { + const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber, true); + + return useQuery({ + queryKey: computed(() => assignmentQueryKey(cid!, an!)), + queryFn: async () => new AssignmentController(cid!).getByNumber(gn!), + enabled: () => checkEnabled(cid, an, gn), + }); +} \ No newline at end of file diff --git a/frontend/src/queries/submissions.ts b/frontend/src/queries/submissions.ts index 374d83d6..3a15f16e 100644 --- a/frontend/src/queries/submissions.ts +++ b/frontend/src/queries/submissions.ts @@ -3,11 +3,11 @@ import type { SubmissionDTO } from "@dwengo-1/common/interfaces/submission"; import { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; -function submissionsQueryKey(classid: string, assignmentNumber: number, full: boolean) { - return [ "submissions", classid, assignmentNumber, full ]; +function submissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number, full: boolean) { + return [ "submissions", classid, assignmentNumber, groupNumber, full ]; } -function submissionQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { - return [ "submission", classid, assignmentNumber, groupNumber ]; +function submissionQueryKey(classid: string, assignmentNumber: number, groupNumber: number, submissionNumber: number) { + return [ "submission", classid, assignmentNumber, groupNumber, submissionNumber ]; } function checkEnabled( @@ -46,7 +46,7 @@ export function useSubmissionsQuery( const { cid, an, gn, sn, f } = toValues(classid, assignmentNumber, groupNumber, 1, full); return useQuery({ - queryKey: computed(() => (submissionsQueryKey(cid!, an!, f))), + queryKey: computed(() => (submissionsQueryKey(cid!, an!, gn!, f))), queryFn: async () => new SubmissionController(cid!, an!, gn!).getAll(f), enabled: () => checkEnabled(cid, an, gn, sn), }); @@ -60,7 +60,7 @@ export function useSubmissionQuery( const { cid, an, gn, sn, f } = toValues(classid, assignmentNumber, groupNumber, 1, true); return useQuery({ - queryKey: computed(() => submissionQueryKey(cid!, an!, gn!)), + queryKey: computed(() => submissionQueryKey(cid!, an!, gn!, sn!)), queryFn: async () => new SubmissionController(cid!, an!, gn!).getByNumber(sn!), enabled: () => checkEnabled(cid, an, gn, sn), }); @@ -79,8 +79,8 @@ export function useCreateSubmissionMutation( return useMutation({ mutationFn: async (data) => new SubmissionController(cid!, an!, gn!).createSubmission(data), onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, true) }); - await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, false) }); + await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, gn!, true) }); + await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, gn!, false) }); }, }); } @@ -96,8 +96,8 @@ export function useDeleteGroupMutation( return useMutation({ mutationFn: async (id) => new SubmissionController(cid!, an!, gn!).deleteSubmission(id), onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, true) }); - await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, false) }); + await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, gn!, true) }); + await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, gn!, false) }); }, }); } \ No newline at end of file From 10ec9cbb589d8d0901b9b2355b3fdd8822e39c12 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sun, 13 Apr 2025 11:41:34 +0200 Subject: [PATCH 19/26] feat: assignment query submissions, questions en groups geimplementeerd --- frontend/src/queries/assignments.ts | 55 +++++++++++++++++++++++++++++ frontend/src/queries/groups.ts | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/frontend/src/queries/assignments.ts b/frontend/src/queries/assignments.ts index a7a8caaf..bec4e52a 100644 --- a/frontend/src/queries/assignments.ts +++ b/frontend/src/queries/assignments.ts @@ -1,6 +1,10 @@ import { AssignmentController, type AssignmentsResponse } from "@/controllers/assignments"; +import type { QuestionsResponse } from "@/controllers/questions"; +import type { SubmissionsResponse } from "@/controllers/submissions"; import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; +import { groupsQueryKey } from "./groups"; +import type { GroupsResponse } from "@/controllers/groups"; function assignmentsQueryKey(classid: string, full: boolean) { return [ "assignments", classid, full ]; @@ -8,6 +12,12 @@ function assignmentsQueryKey(classid: string, full: boolean) { function assignmentQueryKey(classid: string, assignmentNumber: number) { return [ "assignment", classid, assignmentNumber ]; } +function assignmentSubmissionsQueryKey(classid: string, assignmentNumber: number, full: boolean) { + return [ "assignment-submissions", classid, assignmentNumber, full ]; +} +function assignmentQuestionsQueryKey(classid: string, assignmentNumber: number, full: boolean) { + return [ "assignment-questions", classid, assignmentNumber, full ]; +} function checkEnabled( classid: string | undefined, @@ -51,4 +61,49 @@ export function useAssignmentQuery( queryFn: async () => new AssignmentController(cid!).getByNumber(gn!), enabled: () => checkEnabled(cid, an, gn), }); +} + +export function useAssignmentSubmissionsQuery( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter = true, +): UseQueryReturnType { + const { cid, an, gn, f } = toValues(classid, assignmentNumber, groupNumber, full); + + return useQuery({ + queryKey: computed(() => assignmentSubmissionsQueryKey(cid!, an!, f)), + queryFn: async () => new AssignmentController(cid!).getSubmissions(gn!, f), + enabled: () => checkEnabled(cid, an, gn), + }); +} + +export function useAssignmentQuestionsQuery( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter = true, +): UseQueryReturnType { + const { cid, an, gn, f } = toValues(classid, assignmentNumber, groupNumber, full); + + return useQuery({ + queryKey: computed(() => assignmentQuestionsQueryKey(cid!, an!, f)), + queryFn: async () => new AssignmentController(cid!).getQuestions(gn!, f), + enabled: () => checkEnabled(cid, an, gn), + }); +} + +export function useAssignmentGroupsQuery( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter = true, +): UseQueryReturnType { + const { cid, an, gn, f } = toValues(classid, assignmentNumber, groupNumber, full); + + return useQuery({ + queryKey: computed(() => groupsQueryKey(cid!, an!, f)), + queryFn: async () => new AssignmentController(cid!).getQuestions(gn!, f), + enabled: () => checkEnabled(cid, an, gn), + }); } \ No newline at end of file diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index 7223ebc6..50a5d92a 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -6,7 +6,7 @@ import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; import { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; -function groupsQueryKey(classid: string, assignmentNumber: number, full: boolean) { +export function groupsQueryKey(classid: string, assignmentNumber: number, full: boolean) { return [ "groups", classid, assignmentNumber, full ]; } function groupQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { From 75f1ff013bab439f2cb14205468465686d9bca82 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sun, 13 Apr 2025 12:03:11 +0200 Subject: [PATCH 20/26] feat: assignment query mutation queries geimplementeerd (POST, DELETE, PUT) --- frontend/src/queries/assignments.ts | 79 +++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/frontend/src/queries/assignments.ts b/frontend/src/queries/assignments.ts index bec4e52a..e3bf6d39 100644 --- a/frontend/src/queries/assignments.ts +++ b/frontend/src/queries/assignments.ts @@ -1,10 +1,11 @@ -import { AssignmentController, type AssignmentsResponse } from "@/controllers/assignments"; +import { AssignmentController, type AssignmentResponse, type AssignmentsResponse } from "@/controllers/assignments"; import type { QuestionsResponse } from "@/controllers/questions"; import type { SubmissionsResponse } from "@/controllers/submissions"; -import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; +import { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; import { groupsQueryKey } from "./groups"; import type { GroupsResponse } from "@/controllers/groups"; +import type { AssignmentDTO } from "@dwengo-1/common/interfaces/assignment"; function assignmentsQueryKey(classid: string, full: boolean) { return [ "assignments", classid, full ]; @@ -37,29 +38,89 @@ function toValues( export function useAssignmentsQuery( classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { - const { cid, an, f } = toValues(classid, assignmentNumber, 1, full); + const { cid, f } = toValues(classid, 1, 1, full); return useQuery({ queryKey: computed(() => (assignmentsQueryKey(cid!, f))), queryFn: async () => new AssignmentController(cid!).getAll(f), - enabled: () => checkEnabled(cid, an, 1), + enabled: () => checkEnabled(cid, 1, 1), }); } export function useAssignmentQuery( classid: MaybeRefOrGetter, assignmentNumber: MaybeRefOrGetter, - groupNumber: MaybeRefOrGetter, ): UseQueryReturnType { - const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber, true); + const { cid, an } = toValues(classid, assignmentNumber, 1, true); return useQuery({ queryKey: computed(() => assignmentQueryKey(cid!, an!)), - queryFn: async () => new AssignmentController(cid!).getByNumber(gn!), - enabled: () => checkEnabled(cid, an, gn), + queryFn: async () => new AssignmentController(cid!).getByNumber(an!), + enabled: () => checkEnabled(cid, an, 1), + }); +} + +export function useCreateAssignmentMutation( + classid: MaybeRefOrGetter, +): UseMutationReturnType { + const queryClient = useQueryClient(); + const { cid } = toValues(classid, 1, 1, true); + + return useMutation({ + mutationFn: async (data) => new AssignmentController(cid!).createAssignment(data), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, true) }); + await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, false) }); + }, + }); +} + +export function useDeleteAssignmentMutation( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, +): UseMutationReturnType { + const queryClient = useQueryClient(); + const { cid, an } = toValues(classid, assignmentNumber, 1, true); + + return useMutation({ + mutationFn: async (id) => new AssignmentController(cid!).deleteAssignment(id), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: assignmentQueryKey(cid!, an!) }); + + await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, true) }); + await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, false) }); + + await queryClient.invalidateQueries({ queryKey: assignmentSubmissionsQueryKey(cid!, an!, true) }); + await queryClient.invalidateQueries({ queryKey: assignmentSubmissionsQueryKey(cid!, an!, false) }); + + await queryClient.invalidateQueries({ queryKey: assignmentQuestionsQueryKey(cid!, an!, false) }); + await queryClient.invalidateQueries({ queryKey: assignmentQuestionsQueryKey(cid!, an!, true) }); + + // should probably invalidate all groups related to assignment + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, false) }); + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); + }, + }); +} + +export function useUpdateAssignmentMutation( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, +): UseMutationReturnType, unknown> { + const queryClient = useQueryClient(); + const { cid, an } = toValues(classid, assignmentNumber, 1, true); + + return useMutation({ + mutationFn: async (data) => new AssignmentController(cid!).updateAssignment(an!, data), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, false) }); + + await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, true) }); + await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, false) }); + }, }); } From 7bee08537a67f0a10863d1d21a4b2b807cb999d8 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sun, 13 Apr 2025 14:37:29 +0200 Subject: [PATCH 21/26] fix/refactor: cache keys gefixt, useMutation argumenten rerefactord --- frontend/src/queries/assignments.ts | 77 ++++++++++++++--------------- frontend/src/queries/classes.ts | 38 +++++++++----- frontend/src/queries/groups.ts | 22 ++++++++- frontend/src/queries/submissions.ts | 21 +++++++- 4 files changed, 104 insertions(+), 54 deletions(-) diff --git a/frontend/src/queries/assignments.ts b/frontend/src/queries/assignments.ts index e3bf6d39..ddfe0bc4 100644 --- a/frontend/src/queries/assignments.ts +++ b/frontend/src/queries/assignments.ts @@ -3,9 +3,11 @@ import type { QuestionsResponse } from "@/controllers/questions"; import type { SubmissionsResponse } from "@/controllers/submissions"; import { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; -import { groupsQueryKey } from "./groups"; +import { groupsQueryKey, invalidateAllGroupKeys } from "./groups"; import type { GroupsResponse } from "@/controllers/groups"; import type { AssignmentDTO } from "@dwengo-1/common/interfaces/assignment"; +import type { QueryClient } from "@tanstack/react-query"; +import { invalidateAllSubmissionKeys } from "./submissions"; function assignmentsQueryKey(classid: string, full: boolean) { return [ "assignments", classid, full ]; @@ -20,6 +22,21 @@ function assignmentQuestionsQueryKey(classid: string, assignmentNumber: number, return [ "assignment-questions", classid, assignmentNumber, full ]; } +export async function invalidateAllAssignmentKeys(queryClient: QueryClient, classid?: string, assignmentNumber?: number) { + const keys = [ + "assignment", + "assignment-submissions", + "assignment-questions", + ]; + + for (let key of keys) { + const queryKey = [key, classid, assignmentNumber].filter(arg => arg !== undefined); + await queryClient.invalidateQueries({ queryKey: queryKey }); + } + + await queryClient.invalidateQueries({ queryKey: [ "assignments", classid ].filter(arg => arg !== undefined) }); +} + function checkEnabled( classid: string | undefined, assignmentNumber: number | undefined, @@ -62,64 +79,44 @@ export function useAssignmentQuery( }); } -export function useCreateAssignmentMutation( - classid: MaybeRefOrGetter, -): UseMutationReturnType { +export function useCreateAssignmentMutation(): UseMutationReturnType { const queryClient = useQueryClient(); - const { cid } = toValues(classid, 1, 1, true); return useMutation({ - mutationFn: async (data) => new AssignmentController(cid!).createAssignment(data), - onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, true) }); - await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, false) }); + mutationFn: async ({ cid, data }) => new AssignmentController(cid).createAssignment(data), + onSuccess: async (_) => { + await queryClient.invalidateQueries({ queryKey: [ "assignments" ] }); }, }); } -export function useDeleteAssignmentMutation( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, -): UseMutationReturnType { +export function useDeleteAssignmentMutation(): UseMutationReturnType { const queryClient = useQueryClient(); - const { cid, an } = toValues(classid, assignmentNumber, 1, true); return useMutation({ - mutationFn: async (id) => new AssignmentController(cid!).deleteAssignment(id), - onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: assignmentQueryKey(cid!, an!) }); + mutationFn: async ({ cid, an }) => new AssignmentController(cid).deleteAssignment(an), + onSuccess: async (response) => { + const cid = response.assignment.within; + const an = response.assignment.id; - await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, true) }); - await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, false) }); - - await queryClient.invalidateQueries({ queryKey: assignmentSubmissionsQueryKey(cid!, an!, true) }); - await queryClient.invalidateQueries({ queryKey: assignmentSubmissionsQueryKey(cid!, an!, false) }); - - await queryClient.invalidateQueries({ queryKey: assignmentQuestionsQueryKey(cid!, an!, false) }); - await queryClient.invalidateQueries({ queryKey: assignmentQuestionsQueryKey(cid!, an!, true) }); - - // should probably invalidate all groups related to assignment - await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, false) }); - await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); + await invalidateAllAssignmentKeys(queryClient, cid, an); + await invalidateAllGroupKeys(queryClient, cid, an); + await invalidateAllSubmissionKeys(queryClient, cid, an); }, }); } -export function useUpdateAssignmentMutation( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, -): UseMutationReturnType, unknown> { +export function useUpdateAssignmentMutation(): UseMutationReturnType}, unknown> { const queryClient = useQueryClient(); - const { cid, an } = toValues(classid, assignmentNumber, 1, true); return useMutation({ - mutationFn: async (data) => new AssignmentController(cid!).updateAssignment(an!, data), - onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); - await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, false) }); + mutationFn: async ({ cid, an, data }) => new AssignmentController(cid).updateAssignment(an, data), + onSuccess: async (response) => { + const cid = response.assignment.within; + const an = response.assignment.id; - await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, true) }); - await queryClient.invalidateQueries({ queryKey: assignmentsQueryKey(cid!, false) }); + await invalidateAllGroupKeys(queryClient, cid, an); + await queryClient.invalidateQueries({ queryKey: [ "assignments" ] }); }, }); } diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 4faf5b19..a6d1c157 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -3,6 +3,9 @@ import type { StudentsResponse } from "@/controllers/students"; import type { ClassDTO } from "@dwengo-1/common/interfaces/class"; import { QueryClient, useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; +import { invalidateAllAssignmentKeys } from "./assignments"; +import { invalidateAllGroupKeys } from "./groups"; +import { invalidateAllSubmissionKeys } from "./submissions"; const classController = new ClassController(); @@ -23,19 +26,24 @@ function classTeacherInvitationsKey(classid: string, full: boolean) { return ["class-teacher-invitations", classid, full]; } function classAssignmentsKey(classid: string, full: boolean) { - return ["class-assignments", classid]; + return ["class-assignments", classid, full]; } -/* Function to invalidate all caches with certain class id */ -async function invalidateAll(classid: string, queryClient: QueryClient): Promise { - await queryClient.invalidateQueries({ queryKey: ["classes"] }); - await queryClient.invalidateQueries({ queryKey: classQueryKey(classid) }); - for (let v of [true, false]) { - await queryClient.invalidateQueries({ queryKey: classStudentsKey(classid, v) }); - await queryClient.invalidateQueries({ queryKey: classTeachersKey(classid, v) }); - await queryClient.invalidateQueries({ queryKey: classAssignmentsKey(classid, v) }); - await queryClient.invalidateQueries({ queryKey: classTeacherInvitationsKey(classid, v) }); +export async function invalidateAllClassKeys(queryClient: QueryClient, classid?: string) { + const keys = [ + "class", + "class-students", + "class-teachers", + "class-teacher-invitations", + "class-assignments", + ]; + + for (let key of keys) { + const queryKey = [key, classid].filter(arg => arg !== undefined); + await queryClient.invalidateQueries({ queryKey: queryKey }); } + + await queryClient.invalidateQueries({ queryKey: [ "classes" ] }); } /* Queries */ @@ -74,7 +82,10 @@ export function useDeleteClassMutation(): UseMutationReturnType classController.deleteClass(id), onSuccess: async (data) => { - await invalidateAll(data.class.id, queryClient); + await invalidateAllClassKeys(queryClient, data.class.id); + await invalidateAllAssignmentKeys(queryClient, data.class.id); + await invalidateAllGroupKeys(queryClient, data.class.id); + await invalidateAllSubmissionKeys(queryClient, data.class.id); }, }); } @@ -85,7 +96,10 @@ export function useUpdateClassMutation(): UseMutationReturnType classController.updateClass(data.id, data), onSuccess: async (data) => { - await invalidateAll(data.class.id, queryClient); + await invalidateAllClassKeys(queryClient, data.class.id); + await invalidateAllAssignmentKeys(queryClient, data.class.id); + await invalidateAllGroupKeys(queryClient, data.class.id); + await invalidateAllSubmissionKeys(queryClient, data.class.id); }, }); } diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index 50a5d92a..37270953 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -3,7 +3,7 @@ import { GroupController, type GroupResponse, type GroupsResponse } from "@/cont import type { QuestionsResponse } from "@/controllers/questions"; import type { SubmissionsResponse } from "@/controllers/submissions"; import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; -import { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; +import { QueryClient, useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; export function groupsQueryKey(classid: string, assignmentNumber: number, full: boolean) { @@ -19,6 +19,26 @@ function groupQuestionsQueryKey(classid: string, assignmentNumber: number, group return [ "group-questions", classid, assignmentNumber, groupNumber, full ]; } +export async function invalidateAllGroupKeys( + queryClient: QueryClient, + classid?: string, + assignmentNumber?: number, + groupNumber?: number, +) { + const keys = [ + "group", + "group-submissions", + "group-questions", + ]; + + for (let key of keys) { + const queryKey = [key, classid, assignmentNumber, groupNumber].filter(arg => arg !== undefined); + await queryClient.invalidateQueries({ queryKey: queryKey }); + } + + await queryClient.invalidateQueries({ queryKey: [ "groups", classid, assignmentNumber ].filter(arg => arg !== undefined) }); +} + function checkEnabled( classid: string | undefined, assignmentNumber: number | undefined, diff --git a/frontend/src/queries/submissions.ts b/frontend/src/queries/submissions.ts index 3a15f16e..5b8d5691 100644 --- a/frontend/src/queries/submissions.ts +++ b/frontend/src/queries/submissions.ts @@ -1,6 +1,6 @@ import { SubmissionController, type SubmissionResponse, type SubmissionsResponse } from "@/controllers/submissions"; import type { SubmissionDTO } from "@dwengo-1/common/interfaces/submission"; -import { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; +import { QueryClient, useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; function submissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number, full: boolean) { @@ -10,6 +10,25 @@ function submissionQueryKey(classid: string, assignmentNumber: number, groupNumb return [ "submission", classid, assignmentNumber, groupNumber, submissionNumber ]; } +export async function invalidateAllSubmissionKeys( + queryClient: QueryClient, + classid?: string, + assignmentNumber?: number, + groupNumber?: number, + submissionNumber?: number, +) { + const keys = [ + "submission", + ]; + + for (let key of keys) { + const queryKey = [key, classid, assignmentNumber, groupNumber, submissionNumber].filter(arg => arg !== undefined); + await queryClient.invalidateQueries({ queryKey: queryKey }); + } + + await queryClient.invalidateQueries({ queryKey: [ "submissions", classid, assignmentNumber, groupNumber ].filter(arg => arg !== undefined) }); +} + function checkEnabled( classid: string | undefined, assignmentNumber: number | undefined, From 389ce91b52e555ec013e4662246d2ceeed394cb0 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sun, 13 Apr 2025 14:47:40 +0200 Subject: [PATCH 22/26] refactor/fix: group query argumenten gerefactord, group query cach keys gefixt --- frontend/src/queries/classes.ts | 7 ++-- frontend/src/queries/groups.ts | 62 +++++++++++++-------------------- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index a6d1c157..66be4acf 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -70,8 +70,7 @@ export function useCreateClassMutation(): UseMutationReturnType classController.createClass(data), onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: classesQueryKey(true) }); - await queryClient.invalidateQueries({ queryKey: classesQueryKey(false) }); + await queryClient.invalidateQueries({ queryKey: [ "classes" ] }); }, }); } @@ -90,11 +89,11 @@ export function useDeleteClassMutation(): UseMutationReturnType { +export function useUpdateClassMutation(): UseMutationReturnType}, unknown> { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async (data) => classController.updateClass(data.id, data), + mutationFn: async ({ cid, data }) => classController.updateClass(cid, data), onSuccess: async (data) => { await invalidateAllClassKeys(queryClient, data.class.id); await invalidateAllAssignmentKeys(queryClient, data.class.id); diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index 37270953..dbe4bcd7 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -5,6 +5,8 @@ import type { SubmissionsResponse } from "@/controllers/submissions"; import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; import { QueryClient, useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; +import { invalidateAllAssignmentKeys } from "./assignments"; +import { invalidateAllSubmissionKeys } from "./submissions"; export function groupsQueryKey(classid: string, assignmentNumber: number, full: boolean) { return [ "groups", classid, assignmentNumber, full ]; @@ -83,62 +85,48 @@ export function useGroupQuery( }); } -// TODO: find way to check if cid and an are not undefined. -// depends on how this function is used. -export function useCreateGroupMutation( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, -): UseMutationReturnType { +export function useCreateGroupMutation(): UseMutationReturnType { const queryClient = useQueryClient(); - const { cid, an } = toValues(classid, assignmentNumber, 1, true); return useMutation({ - mutationFn: async (data) => new GroupController(cid!, an!).createGroup(data), - onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); - await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, false) }); + mutationFn: async ({ cid, an, data }) => new GroupController(cid, an).createGroup(data), + onSuccess: async (response) => { + const cid = typeof(response.group.class) === 'string' ? response.group.class : response.group.class.id; + const an = typeof(response.group.assignment) === 'number' ? response.group.assignment : response.group.assignment.id; + + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid, an, true) }); + await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid, an, false) }); }, }); } -export function useDeleteGroupMutation( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, -): UseMutationReturnType { +export function useDeleteGroupMutation(): UseMutationReturnType { const queryClient = useQueryClient(); - const { cid, an, gn } = toValues(classid, assignmentNumber, 1, true); return useMutation({ - mutationFn: async (id) => new GroupController(cid!, an!).deleteGroup(id), - onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: groupQueryKey(cid!, an!, gn!) }); + mutationFn: async ({cid, an, gn}) => new GroupController(cid, an).deleteGroup(gn), + onSuccess: async (response) => { + const cid = typeof(response.group.class) === 'string' ? response.group.class : response.group.class.id; + const an = typeof(response.group.assignment) === 'number' ? response.group.assignment : response.group.assignment.id; + const gn = response.group.groupNumber; - await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); - await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, false) }); - - await queryClient.invalidateQueries({ queryKey: groupSubmissionsQueryKey(cid!, an!, gn!, true) }); - await queryClient.invalidateQueries({ queryKey: groupSubmissionsQueryKey(cid!, an!, gn!, false) }); - - await queryClient.invalidateQueries({ queryKey: groupQuestionsQueryKey(cid!, an!, gn!, true) }); - await queryClient.invalidateQueries({ queryKey: groupQuestionsQueryKey(cid!, an!, gn!, false) }); + await invalidateAllGroupKeys(queryClient, cid, an, gn); + await invalidateAllSubmissionKeys(queryClient, cid, an, gn); }, }); } -export function useUpdateGroupMutation( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, -): UseMutationReturnType { +export function useUpdateGroupMutation(): UseMutationReturnType}, unknown> { const queryClient = useQueryClient(); - const { cid, an, gn } = toValues(classid, assignmentNumber, 1, true); return useMutation({ - mutationFn: async (data) => new GroupController(cid!, an!).updateGroup(gn!, data), - onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: groupQueryKey(cid!, an!, gn!) }); + mutationFn: async ({cid, an, gn, data}) => new GroupController(cid, an).updateGroup(gn, data), + onSuccess: async (response) => { + const cid = typeof(response.group.class) === 'string' ? response.group.class : response.group.class.id; + const an = typeof(response.group.assignment) === 'number' ? response.group.assignment : response.group.assignment.id; + const gn = response.group.groupNumber; - await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, true) }); - await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid!, an!, false) }); + await invalidateAllGroupKeys(queryClient, cid, an, gn); }, }); } From 67b60cadedee4a6576e4805c0d78e80e3a195d3c Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sun, 13 Apr 2025 14:55:18 +0200 Subject: [PATCH 23/26] fix/refactor: submission query argumenten gerefactord, cache keys gefixt --- frontend/src/queries/submissions.ts | 54 +++++++++++++++++------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/frontend/src/queries/submissions.ts b/frontend/src/queries/submissions.ts index 5b8d5691..743b1d09 100644 --- a/frontend/src/queries/submissions.ts +++ b/frontend/src/queries/submissions.ts @@ -27,6 +27,8 @@ export async function invalidateAllSubmissionKeys( } await queryClient.invalidateQueries({ queryKey: [ "submissions", classid, assignmentNumber, groupNumber ].filter(arg => arg !== undefined) }); + await queryClient.invalidateQueries({ queryKey: [ "group-submissions", classid, assignmentNumber, groupNumber ].filter(arg => arg !== undefined) }); + await queryClient.invalidateQueries({ queryKey: [ "assignment-submissions", classid, assignmentNumber ].filter(arg => arg !== undefined) }); } function checkEnabled( @@ -85,38 +87,46 @@ export function useSubmissionQuery( }); } -// TODO: find way to check if cid and an are not undefined. -// depends on how this function is used. -export function useCreateSubmissionMutation( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, - groupNumber: MaybeRefOrGetter, -): UseMutationReturnType { +export function useCreateSubmissionMutation(): UseMutationReturnType { const queryClient = useQueryClient(); - const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber, 1, true); return useMutation({ - mutationFn: async (data) => new SubmissionController(cid!, an!, gn!).createSubmission(data), - onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, gn!, true) }); - await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, gn!, false) }); + mutationFn: async ({cid, an, gn, data}) => new SubmissionController(cid, an, gn).createSubmission(data), + onSuccess: async (response) => { + if (!response.submission.group) { + await invalidateAllSubmissionKeys(queryClient); + } else { + const cls = response.submission.group.class; + const assignment = response.submission.group.assignment; + + const cid = typeof(cls) === 'string' ? cls : cls.id; + const an = typeof(assignment) === 'number' ? assignment : assignment.id; + const gn = response.submission.group.groupNumber; + + await invalidateAllSubmissionKeys(queryClient, cid, an, gn); + } }, }); } -export function useDeleteGroupMutation( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, - groupNumber: MaybeRefOrGetter, -): UseMutationReturnType { +export function useDeleteSubmissionMutation(): UseMutationReturnType { const queryClient = useQueryClient(); - const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber, 1, true); return useMutation({ - mutationFn: async (id) => new SubmissionController(cid!, an!, gn!).deleteSubmission(id), - onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, gn!, true) }); - await queryClient.invalidateQueries({ queryKey: submissionsQueryKey(cid!, an!, gn!, false) }); + mutationFn: async ({cid, an, gn, sn}) => new SubmissionController(cid, an, gn).deleteSubmission(sn), + onSuccess: async (response) => { + if (!response.submission.group) { + await invalidateAllSubmissionKeys(queryClient); + } else { + const cls = response.submission.group.class; + const assignment = response.submission.group.assignment; + + const cid = typeof(cls) === 'string' ? cls : cls.id; + const an = typeof(assignment) === 'number' ? assignment : assignment.id; + const gn = response.submission.group.groupNumber; + + await invalidateAllSubmissionKeys(queryClient, cid, an, gn); + } }, }); } \ No newline at end of file From 8fde49c05123d7f4e65d067800e61bccdfc784e5 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Sun, 13 Apr 2025 13:00:05 +0000 Subject: [PATCH 24/26] style: fix linting issues met ESLint --- frontend/src/queries/assignments.ts | 2 +- frontend/src/queries/classes.ts | 10 +++++----- frontend/src/queries/groups.ts | 2 +- frontend/src/queries/submissions.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/queries/assignments.ts b/frontend/src/queries/assignments.ts index ddfe0bc4..8dd2912b 100644 --- a/frontend/src/queries/assignments.ts +++ b/frontend/src/queries/assignments.ts @@ -29,7 +29,7 @@ export async function invalidateAllAssignmentKeys(queryClient: QueryClient, clas "assignment-questions", ]; - for (let key of keys) { + for (const key of keys) { const queryKey = [key, classid, assignmentNumber].filter(arg => arg !== undefined); await queryClient.invalidateQueries({ queryKey: queryKey }); } diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 66be4acf..e4ecfcb1 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -38,7 +38,7 @@ export async function invalidateAllClassKeys(queryClient: QueryClient, classid?: "class-assignments", ]; - for (let key of keys) { + for (const key of keys) { const queryKey = [key, classid].filter(arg => arg !== undefined); await queryClient.invalidateQueries({ queryKey: queryKey }); } @@ -109,7 +109,7 @@ export function useClassStudentsQuery( ): UseQueryReturnType { return useQuery({ queryKey: computed(() => classStudentsKey(toValue(id)!, toValue(full))), - queryFn: async () => classController.getStudents(toValue(id)!, toValue(full)!), + queryFn: async () => classController.getStudents(toValue(id)!, toValue(full)), enabled: () => Boolean(toValue(id)), }) } @@ -146,7 +146,7 @@ export function useClassTeachersQuery( ): UseQueryReturnType { return useQuery({ queryKey: computed(() => classTeachersKey(toValue(id)!, toValue(full))), - queryFn: async () => classController.getTeachers(toValue(id)!, toValue(full)!), + queryFn: async () => classController.getTeachers(toValue(id)!, toValue(full)), enabled: () => Boolean(toValue(id)), }); } @@ -183,7 +183,7 @@ export function useClassTeacherInvitationsQuery( ): UseQueryReturnType { return useQuery({ queryKey: computed(() => classTeacherInvitationsKey(toValue(id)!, toValue(full))), - queryFn: async () => classController.getTeacherInvitations(toValue(id)!, toValue(full)!), + queryFn: async () => classController.getTeacherInvitations(toValue(id)!, toValue(full)), enabled: () => Boolean(toValue(id)), }); } @@ -194,7 +194,7 @@ export function useClassAssignmentsQuery( ): UseQueryReturnType { return useQuery({ queryKey: computed(() => classAssignmentsKey(toValue(id)!, toValue(full))), - queryFn: async () => classController.getAssignments(toValue(id)!, toValue(full)!), + queryFn: async () => classController.getAssignments(toValue(id)!, toValue(full)), enabled: () => Boolean(toValue(id)), }); } \ No newline at end of file diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index dbe4bcd7..7ed67cbc 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -33,7 +33,7 @@ export async function invalidateAllGroupKeys( "group-questions", ]; - for (let key of keys) { + for (const key of keys) { const queryKey = [key, classid, assignmentNumber, groupNumber].filter(arg => arg !== undefined); await queryClient.invalidateQueries({ queryKey: queryKey }); } diff --git a/frontend/src/queries/submissions.ts b/frontend/src/queries/submissions.ts index 743b1d09..5360afaf 100644 --- a/frontend/src/queries/submissions.ts +++ b/frontend/src/queries/submissions.ts @@ -21,7 +21,7 @@ export async function invalidateAllSubmissionKeys( "submission", ]; - for (let key of keys) { + for (const key of keys) { const queryKey = [key, classid, assignmentNumber, groupNumber, submissionNumber].filter(arg => arg !== undefined); await queryClient.invalidateQueries({ queryKey: queryKey }); } From 49c468d431e42e1dcda6f7fc415d5d16f9b5d6cf Mon Sep 17 00:00:00 2001 From: Lint Action Date: Sun, 13 Apr 2025 13:00:10 +0000 Subject: [PATCH 25/26] style: fix linting issues met Prettier --- frontend/src/queries/assignments.ts | 89 +++++++++++++--------- frontend/src/queries/classes.ts | 76 ++++++++++++------- frontend/src/queries/groups.ts | 107 ++++++++++++++++---------- frontend/src/queries/submissions.ts | 113 +++++++++++++++++----------- 4 files changed, 242 insertions(+), 143 deletions(-) diff --git a/frontend/src/queries/assignments.ts b/frontend/src/queries/assignments.ts index 8dd2912b..3251836a 100644 --- a/frontend/src/queries/assignments.ts +++ b/frontend/src/queries/assignments.ts @@ -1,7 +1,13 @@ import { AssignmentController, type AssignmentResponse, type AssignmentsResponse } from "@/controllers/assignments"; import type { QuestionsResponse } from "@/controllers/questions"; import type { SubmissionsResponse } from "@/controllers/submissions"; -import { useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; +import { + useMutation, + useQuery, + useQueryClient, + type UseMutationReturnType, + type UseQueryReturnType, +} from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; import { groupsQueryKey, invalidateAllGroupKeys } from "./groups"; import type { GroupsResponse } from "@/controllers/groups"; @@ -10,43 +16,43 @@ import type { QueryClient } from "@tanstack/react-query"; import { invalidateAllSubmissionKeys } from "./submissions"; function assignmentsQueryKey(classid: string, full: boolean) { - return [ "assignments", classid, full ]; + return ["assignments", classid, full]; } function assignmentQueryKey(classid: string, assignmentNumber: number) { - return [ "assignment", classid, assignmentNumber ]; + return ["assignment", classid, assignmentNumber]; } function assignmentSubmissionsQueryKey(classid: string, assignmentNumber: number, full: boolean) { - return [ "assignment-submissions", classid, assignmentNumber, full ]; + return ["assignment-submissions", classid, assignmentNumber, full]; } function assignmentQuestionsQueryKey(classid: string, assignmentNumber: number, full: boolean) { - return [ "assignment-questions", classid, assignmentNumber, full ]; + return ["assignment-questions", classid, assignmentNumber, full]; } -export async function invalidateAllAssignmentKeys(queryClient: QueryClient, classid?: string, assignmentNumber?: number) { - const keys = [ - "assignment", - "assignment-submissions", - "assignment-questions", - ]; +export async function invalidateAllAssignmentKeys( + queryClient: QueryClient, + classid?: string, + assignmentNumber?: number, +) { + const keys = ["assignment", "assignment-submissions", "assignment-questions"]; for (const key of keys) { - const queryKey = [key, classid, assignmentNumber].filter(arg => arg !== undefined); + const queryKey = [key, classid, assignmentNumber].filter((arg) => arg !== undefined); await queryClient.invalidateQueries({ queryKey: queryKey }); } - await queryClient.invalidateQueries({ queryKey: [ "assignments", classid ].filter(arg => arg !== undefined) }); + await queryClient.invalidateQueries({ queryKey: ["assignments", classid].filter((arg) => arg !== undefined) }); } function checkEnabled( - classid: string | undefined, - assignmentNumber: number | undefined, + classid: string | undefined, + assignmentNumber: number | undefined, groupNumber: number | undefined, ): boolean { - return Boolean(classid) && !isNaN(Number(groupNumber)) && !isNaN(Number(assignmentNumber)); + return Boolean(classid) && !isNaN(Number(groupNumber)) && !isNaN(Number(assignmentNumber)); } function toValues( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter, ) { @@ -54,21 +60,21 @@ function toValues( } export function useAssignmentsQuery( - classid: MaybeRefOrGetter, + classid: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { const { cid, f } = toValues(classid, 1, 1, full); return useQuery({ - queryKey: computed(() => (assignmentsQueryKey(cid!, f))), + queryKey: computed(() => assignmentsQueryKey(cid!, f)), queryFn: async () => new AssignmentController(cid!).getAll(f), enabled: () => checkEnabled(cid, 1, 1), }); } export function useAssignmentQuery( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, ): UseQueryReturnType { const { cid, an } = toValues(classid, assignmentNumber, 1, true); @@ -79,18 +85,28 @@ export function useAssignmentQuery( }); } -export function useCreateAssignmentMutation(): UseMutationReturnType { +export function useCreateAssignmentMutation(): UseMutationReturnType< + AssignmentResponse, + Error, + { cid: string; data: AssignmentDTO }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ cid, data }) => new AssignmentController(cid).createAssignment(data), onSuccess: async (_) => { - await queryClient.invalidateQueries({ queryKey: [ "assignments" ] }); + await queryClient.invalidateQueries({ queryKey: ["assignments"] }); }, }); } -export function useDeleteAssignmentMutation(): UseMutationReturnType { +export function useDeleteAssignmentMutation(): UseMutationReturnType< + AssignmentResponse, + Error, + { cid: string; an: number }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ @@ -106,7 +122,12 @@ export function useDeleteAssignmentMutation(): UseMutationReturnType}, unknown> { +export function useUpdateAssignmentMutation(): UseMutationReturnType< + AssignmentResponse, + Error, + { cid: string; an: number; data: Partial }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ @@ -116,14 +137,14 @@ export function useUpdateAssignmentMutation(): UseMutationReturnType, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { @@ -137,8 +158,8 @@ export function useAssignmentSubmissionsQuery( } export function useAssignmentQuestionsQuery( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { @@ -152,8 +173,8 @@ export function useAssignmentQuestionsQuery( } export function useAssignmentGroupsQuery( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { @@ -164,4 +185,4 @@ export function useAssignmentGroupsQuery( queryFn: async () => new AssignmentController(cid!).getQuestions(gn!, f), enabled: () => checkEnabled(cid, an, gn), }); -} \ No newline at end of file +} diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index e4ecfcb1..68ba0127 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -1,7 +1,14 @@ import { ClassController, type ClassesResponse, type ClassResponse } from "@/controllers/classes"; import type { StudentsResponse } from "@/controllers/students"; import type { ClassDTO } from "@dwengo-1/common/interfaces/class"; -import { QueryClient, useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; +import { + QueryClient, + useMutation, + useQuery, + useQueryClient, + type UseMutationReturnType, + type UseQueryReturnType, +} from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; import { invalidateAllAssignmentKeys } from "./assignments"; import { invalidateAllGroupKeys } from "./groups"; @@ -30,33 +37,25 @@ function classAssignmentsKey(classid: string, full: boolean) { } export async function invalidateAllClassKeys(queryClient: QueryClient, classid?: string) { - const keys = [ - "class", - "class-students", - "class-teachers", - "class-teacher-invitations", - "class-assignments", - ]; + const keys = ["class", "class-students", "class-teachers", "class-teacher-invitations", "class-assignments"]; for (const key of keys) { - const queryKey = [key, classid].filter(arg => arg !== undefined); + const queryKey = [key, classid].filter((arg) => arg !== undefined); await queryClient.invalidateQueries({ queryKey: queryKey }); } - await queryClient.invalidateQueries({ queryKey: [ "classes" ] }); + await queryClient.invalidateQueries({ queryKey: ["classes"] }); } /* Queries */ export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { return useQuery({ - queryKey: computed(() => (classesQueryKey(toValue(full)))), + queryKey: computed(() => classesQueryKey(toValue(full))), queryFn: async () => classController.getAll(toValue(full)), }); } -export function useClassQuery( - id: MaybeRefOrGetter, -): UseQueryReturnType { +export function useClassQuery(id: MaybeRefOrGetter): UseQueryReturnType { return useQuery({ queryKey: computed(() => classQueryKey(toValue(id)!)), queryFn: async () => classController.getById(toValue(id)!), @@ -70,7 +69,7 @@ export function useCreateClassMutation(): UseMutationReturnType classController.createClass(data), onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: [ "classes" ] }); + await queryClient.invalidateQueries({ queryKey: ["classes"] }); }, }); } @@ -89,7 +88,12 @@ export function useDeleteClassMutation(): UseMutationReturnType}, unknown> { +export function useUpdateClassMutation(): UseMutationReturnType< + ClassResponse, + Error, + { cid: string; data: Partial }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ @@ -105,16 +109,21 @@ export function useUpdateClassMutation(): UseMutationReturnType, - full: MaybeRefOrGetter = true + full: MaybeRefOrGetter = true, ): UseQueryReturnType { return useQuery({ queryKey: computed(() => classStudentsKey(toValue(id)!, toValue(full))), queryFn: async () => classController.getStudents(toValue(id)!, toValue(full)), enabled: () => Boolean(toValue(id)), - }) + }); } -export function useClassAddStudentMutation(): UseMutationReturnType { +export function useClassAddStudentMutation(): UseMutationReturnType< + ClassResponse, + Error, + { id: string; username: string }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ @@ -127,7 +136,12 @@ export function useClassAddStudentMutation(): UseMutationReturnType { +export function useClassDeleteStudentMutation(): UseMutationReturnType< + ClassResponse, + Error, + { id: string; username: string }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ @@ -142,7 +156,7 @@ export function useClassDeleteStudentMutation(): UseMutationReturnType, - full: MaybeRefOrGetter = true + full: MaybeRefOrGetter = true, ): UseQueryReturnType { return useQuery({ queryKey: computed(() => classTeachersKey(toValue(id)!, toValue(full))), @@ -151,7 +165,12 @@ export function useClassTeachersQuery( }); } -export function useClassAddTeacherMutation(): UseMutationReturnType { +export function useClassAddTeacherMutation(): UseMutationReturnType< + ClassResponse, + Error, + { id: string; username: string }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ @@ -164,7 +183,12 @@ export function useClassAddTeacherMutation(): UseMutationReturnType { +export function useClassDeleteTeacherMutation(): UseMutationReturnType< + ClassResponse, + Error, + { id: string; username: string }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ @@ -179,7 +203,7 @@ export function useClassDeleteTeacherMutation(): UseMutationReturnType, - full: MaybeRefOrGetter = true + full: MaybeRefOrGetter = true, ): UseQueryReturnType { return useQuery({ queryKey: computed(() => classTeacherInvitationsKey(toValue(id)!, toValue(full))), @@ -190,11 +214,11 @@ export function useClassTeacherInvitationsQuery( export function useClassAssignmentsQuery( id: MaybeRefOrGetter, - full: MaybeRefOrGetter = true + full: MaybeRefOrGetter = true, ): UseQueryReturnType { return useQuery({ queryKey: computed(() => classAssignmentsKey(toValue(id)!, toValue(full))), queryFn: async () => classController.getAssignments(toValue(id)!, toValue(full)), enabled: () => Boolean(toValue(id)), }); -} \ No newline at end of file +} diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index 7ed67cbc..cdef2899 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -3,54 +3,59 @@ import { GroupController, type GroupResponse, type GroupsResponse } from "@/cont import type { QuestionsResponse } from "@/controllers/questions"; import type { SubmissionsResponse } from "@/controllers/submissions"; import type { GroupDTO } from "@dwengo-1/common/interfaces/group"; -import { QueryClient, useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; +import { + QueryClient, + useMutation, + useQuery, + useQueryClient, + type UseMutationReturnType, + type UseQueryReturnType, +} from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; import { invalidateAllAssignmentKeys } from "./assignments"; import { invalidateAllSubmissionKeys } from "./submissions"; export function groupsQueryKey(classid: string, assignmentNumber: number, full: boolean) { - return [ "groups", classid, assignmentNumber, full ]; + return ["groups", classid, assignmentNumber, full]; } function groupQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { - return [ "group", classid, assignmentNumber, groupNumber ]; + return ["group", classid, assignmentNumber, groupNumber]; } function groupSubmissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number, full: boolean) { - return [ "group-submissions", classid, assignmentNumber, groupNumber, full ]; + return ["group-submissions", classid, assignmentNumber, groupNumber, full]; } function groupQuestionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number, full: boolean) { - return [ "group-questions", classid, assignmentNumber, groupNumber, full ]; + return ["group-questions", classid, assignmentNumber, groupNumber, full]; } export async function invalidateAllGroupKeys( - queryClient: QueryClient, - classid?: string, + queryClient: QueryClient, + classid?: string, assignmentNumber?: number, groupNumber?: number, ) { - const keys = [ - "group", - "group-submissions", - "group-questions", - ]; + const keys = ["group", "group-submissions", "group-questions"]; for (const key of keys) { - const queryKey = [key, classid, assignmentNumber, groupNumber].filter(arg => arg !== undefined); + const queryKey = [key, classid, assignmentNumber, groupNumber].filter((arg) => arg !== undefined); await queryClient.invalidateQueries({ queryKey: queryKey }); } - await queryClient.invalidateQueries({ queryKey: [ "groups", classid, assignmentNumber ].filter(arg => arg !== undefined) }); + await queryClient.invalidateQueries({ + queryKey: ["groups", classid, assignmentNumber].filter((arg) => arg !== undefined), + }); } function checkEnabled( - classid: string | undefined, - assignmentNumber: number | undefined, + classid: string | undefined, + assignmentNumber: number | undefined, groupNumber: number | undefined, ): boolean { - return Boolean(classid) && !isNaN(Number(groupNumber)) && !isNaN(Number(assignmentNumber)); + return Boolean(classid) && !isNaN(Number(groupNumber)) && !isNaN(Number(assignmentNumber)); } function toValues( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter, ) { @@ -58,22 +63,22 @@ function toValues( } export function useGroupsQuery( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { const { cid, an, f } = toValues(classid, assignmentNumber, 1, full); return useQuery({ - queryKey: computed(() => (groupsQueryKey(cid!, an!, f))), + queryKey: computed(() => groupsQueryKey(cid!, an!, f)), queryFn: async () => new GroupController(cid!, an!).getAll(f), enabled: () => checkEnabled(cid, an, 1), }); } export function useGroupQuery( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, ): UseQueryReturnType { const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber, true); @@ -85,14 +90,22 @@ export function useGroupQuery( }); } -export function useCreateGroupMutation(): UseMutationReturnType { +export function useCreateGroupMutation(): UseMutationReturnType< + GroupResponse, + Error, + { cid: string; an: number; data: GroupDTO }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ cid, an, data }) => new GroupController(cid, an).createGroup(data), onSuccess: async (response) => { - const cid = typeof(response.group.class) === 'string' ? response.group.class : response.group.class.id; - const an = typeof(response.group.assignment) === 'number' ? response.group.assignment : response.group.assignment.id; + const cid = typeof response.group.class === "string" ? response.group.class : response.group.class.id; + const an = + typeof response.group.assignment === "number" + ? response.group.assignment + : response.group.assignment.id; await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid, an, true) }); await queryClient.invalidateQueries({ queryKey: groupsQueryKey(cid, an, false) }); @@ -100,14 +113,22 @@ export function useCreateGroupMutation(): UseMutationReturnType { +export function useDeleteGroupMutation(): UseMutationReturnType< + GroupResponse, + Error, + { cid: string; an: number; gn: number }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ({cid, an, gn}) => new GroupController(cid, an).deleteGroup(gn), + mutationFn: async ({ cid, an, gn }) => new GroupController(cid, an).deleteGroup(gn), onSuccess: async (response) => { - const cid = typeof(response.group.class) === 'string' ? response.group.class : response.group.class.id; - const an = typeof(response.group.assignment) === 'number' ? response.group.assignment : response.group.assignment.id; + const cid = typeof response.group.class === "string" ? response.group.class : response.group.class.id; + const an = + typeof response.group.assignment === "number" + ? response.group.assignment + : response.group.assignment.id; const gn = response.group.groupNumber; await invalidateAllGroupKeys(queryClient, cid, an, gn); @@ -116,14 +137,22 @@ export function useDeleteGroupMutation(): UseMutationReturnType}, unknown> { +export function useUpdateGroupMutation(): UseMutationReturnType< + GroupResponse, + Error, + { cid: string; an: number; gn: number; data: Partial }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ({cid, an, gn, data}) => new GroupController(cid, an).updateGroup(gn, data), + mutationFn: async ({ cid, an, gn, data }) => new GroupController(cid, an).updateGroup(gn, data), onSuccess: async (response) => { - const cid = typeof(response.group.class) === 'string' ? response.group.class : response.group.class.id; - const an = typeof(response.group.assignment) === 'number' ? response.group.assignment : response.group.assignment.id; + const cid = typeof response.group.class === "string" ? response.group.class : response.group.class.id; + const an = + typeof response.group.assignment === "number" + ? response.group.assignment + : response.group.assignment.id; const gn = response.group.groupNumber; await invalidateAllGroupKeys(queryClient, cid, an, gn); @@ -132,8 +161,8 @@ export function useUpdateGroupMutation(): UseMutationReturnType, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { @@ -147,8 +176,8 @@ export function useGroupSubmissionsQuery( } export function useGroupQuestionsQuery( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { diff --git a/frontend/src/queries/submissions.ts b/frontend/src/queries/submissions.ts index 5360afaf..97effd15 100644 --- a/frontend/src/queries/submissions.ts +++ b/frontend/src/queries/submissions.ts @@ -1,81 +1,96 @@ import { SubmissionController, type SubmissionResponse, type SubmissionsResponse } from "@/controllers/submissions"; import type { SubmissionDTO } from "@dwengo-1/common/interfaces/submission"; -import { QueryClient, useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; +import { + QueryClient, + useMutation, + useQuery, + useQueryClient, + type UseMutationReturnType, + type UseQueryReturnType, +} from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; function submissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number, full: boolean) { - return [ "submissions", classid, assignmentNumber, groupNumber, full ]; + return ["submissions", classid, assignmentNumber, groupNumber, full]; } function submissionQueryKey(classid: string, assignmentNumber: number, groupNumber: number, submissionNumber: number) { - return [ "submission", classid, assignmentNumber, groupNumber, submissionNumber ]; + return ["submission", classid, assignmentNumber, groupNumber, submissionNumber]; } export async function invalidateAllSubmissionKeys( - queryClient: QueryClient, - classid?: string, + queryClient: QueryClient, + classid?: string, assignmentNumber?: number, groupNumber?: number, submissionNumber?: number, ) { - const keys = [ - "submission", - ]; + const keys = ["submission"]; for (const key of keys) { - const queryKey = [key, classid, assignmentNumber, groupNumber, submissionNumber].filter(arg => arg !== undefined); + const queryKey = [key, classid, assignmentNumber, groupNumber, submissionNumber].filter( + (arg) => arg !== undefined, + ); await queryClient.invalidateQueries({ queryKey: queryKey }); } - await queryClient.invalidateQueries({ queryKey: [ "submissions", classid, assignmentNumber, groupNumber ].filter(arg => arg !== undefined) }); - await queryClient.invalidateQueries({ queryKey: [ "group-submissions", classid, assignmentNumber, groupNumber ].filter(arg => arg !== undefined) }); - await queryClient.invalidateQueries({ queryKey: [ "assignment-submissions", classid, assignmentNumber ].filter(arg => arg !== undefined) }); + await queryClient.invalidateQueries({ + queryKey: ["submissions", classid, assignmentNumber, groupNumber].filter((arg) => arg !== undefined), + }); + await queryClient.invalidateQueries({ + queryKey: ["group-submissions", classid, assignmentNumber, groupNumber].filter((arg) => arg !== undefined), + }); + await queryClient.invalidateQueries({ + queryKey: ["assignment-submissions", classid, assignmentNumber].filter((arg) => arg !== undefined), + }); } function checkEnabled( - classid: string | undefined, - assignmentNumber: number | undefined, + classid: string | undefined, + assignmentNumber: number | undefined, groupNumber: number | undefined, - submissionNumber: number | undefined + submissionNumber: number | undefined, ): boolean { - return Boolean(classid) - && !isNaN(Number(groupNumber)) - && !isNaN(Number(assignmentNumber)) - && !isNaN(Number(submissionNumber)); + return ( + Boolean(classid) && + !isNaN(Number(groupNumber)) && + !isNaN(Number(assignmentNumber)) && + !isNaN(Number(submissionNumber)) + ); } function toValues( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, submissionNumber: MaybeRefOrGetter, full: MaybeRefOrGetter, ) { - return { - cid: toValue(classid), - an: toValue(assignmentNumber), - gn: toValue(groupNumber), - sn: toValue(submissionNumber), - f: toValue(full) + return { + cid: toValue(classid), + an: toValue(assignmentNumber), + gn: toValue(groupNumber), + sn: toValue(submissionNumber), + f: toValue(full), }; } export function useSubmissionsQuery( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, - groupNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { const { cid, an, gn, sn, f } = toValues(classid, assignmentNumber, groupNumber, 1, full); return useQuery({ - queryKey: computed(() => (submissionsQueryKey(cid!, an!, gn!, f))), + queryKey: computed(() => submissionsQueryKey(cid!, an!, gn!, f)), queryFn: async () => new SubmissionController(cid!, an!, gn!).getAll(f), enabled: () => checkEnabled(cid, an, gn, sn), }); } export function useSubmissionQuery( - classid: MaybeRefOrGetter, - assignmentNumber: MaybeRefOrGetter, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, ): UseQueryReturnType { const { cid, an, gn, sn, f } = toValues(classid, assignmentNumber, groupNumber, 1, true); @@ -87,20 +102,25 @@ export function useSubmissionQuery( }); } -export function useCreateSubmissionMutation(): UseMutationReturnType { +export function useCreateSubmissionMutation(): UseMutationReturnType< + SubmissionResponse, + Error, + { cid: string; an: number; gn: number; data: SubmissionDTO }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ({cid, an, gn, data}) => new SubmissionController(cid, an, gn).createSubmission(data), + mutationFn: async ({ cid, an, gn, data }) => new SubmissionController(cid, an, gn).createSubmission(data), onSuccess: async (response) => { if (!response.submission.group) { await invalidateAllSubmissionKeys(queryClient); } else { const cls = response.submission.group.class; const assignment = response.submission.group.assignment; - - const cid = typeof(cls) === 'string' ? cls : cls.id; - const an = typeof(assignment) === 'number' ? assignment : assignment.id; + + const cid = typeof cls === "string" ? cls : cls.id; + const an = typeof assignment === "number" ? assignment : assignment.id; const gn = response.submission.group.groupNumber; await invalidateAllSubmissionKeys(queryClient, cid, an, gn); @@ -109,24 +129,29 @@ export function useCreateSubmissionMutation(): UseMutationReturnType { +export function useDeleteSubmissionMutation(): UseMutationReturnType< + SubmissionResponse, + Error, + { cid: string; an: number; gn: number; sn: number }, + unknown +> { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ({cid, an, gn, sn}) => new SubmissionController(cid, an, gn).deleteSubmission(sn), + mutationFn: async ({ cid, an, gn, sn }) => new SubmissionController(cid, an, gn).deleteSubmission(sn), onSuccess: async (response) => { if (!response.submission.group) { await invalidateAllSubmissionKeys(queryClient); } else { const cls = response.submission.group.class; const assignment = response.submission.group.assignment; - - const cid = typeof(cls) === 'string' ? cls : cls.id; - const an = typeof(assignment) === 'number' ? assignment : assignment.id; + + const cid = typeof cls === "string" ? cls : cls.id; + const an = typeof assignment === "number" ? assignment : assignment.id; const gn = response.submission.group.groupNumber; await invalidateAllSubmissionKeys(queryClient, cid, an, gn); } }, }); -} \ No newline at end of file +} From e995419227a9bad743ac90e80656ad3871a34abc Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Sun, 13 Apr 2025 15:01:22 +0200 Subject: [PATCH 26/26] fix: fixed linting error --- frontend/src/controllers/submissions.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frontend/src/controllers/submissions.ts b/frontend/src/controllers/submissions.ts index 1f281e62..0d9c73f0 100644 --- a/frontend/src/controllers/submissions.ts +++ b/frontend/src/controllers/submissions.ts @@ -14,10 +14,6 @@ export class SubmissionController extends BaseController { super(`class/${classid}/assignments/${assignmentNumber}/groups/${groupNumber}/submissions`); } - protected getBasePath(classid: string, assignmentNumber: number, groupNumber: number) { - return `class/${classid}/assignments/${assignmentNumber}/groups/${groupNumber}/submissions`; - } - async getAll(full = true): Promise { return this.get(`/`, { full }); }