diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 95d07cdf..255d0402 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -52,6 +52,16 @@ export default { subjects: "My subjects", announcements: "Announcements", }, + admin: { + users: "Users", + userTable: { + name: "Name", + uid: "UGent ID", + email: "Email", + isTeacher: "Is Teacher", + isAdmin: "Is Admin", + }, + }, subject: { register: "Register to subject:", academy_year: "Academic year", diff --git a/frontend/src/i18n/locales/nl.ts b/frontend/src/i18n/locales/nl.ts index 34eb547d..3ea22ba4 100644 --- a/frontend/src/i18n/locales/nl.ts +++ b/frontend/src/i18n/locales/nl.ts @@ -52,7 +52,16 @@ export default { subjects: "Mijn vakken", announcements: "Meldingen", }, - + admin: { + users: "Gebruikers", + userTable: { + name: "Naam", + uid: "UGent ID", + email: "Email", + isTeacher: "Is Lesgever", + isAdmin: "Is Beheerder", + }, + }, subject: { register: "Registreer bij vak:", academy_year: "Academiejaar", diff --git a/frontend/src/queries/User.ts b/frontend/src/queries/User.ts index 8aec9787..79d5eebd 100644 --- a/frontend/src/queries/User.ts +++ b/frontend/src/queries/User.ts @@ -6,7 +6,7 @@ import { type UseMutationReturnType, } from "@tanstack/vue-query"; import type User from "@/models/User"; -import { getMySubjects, getUser, toggleAdmin } from "@/services/user"; +import { getMySubjects, getUser, getUsers, toggleAdmin, toggleTeacher } from "@/services/user"; import { type Ref, computed } from "vue"; import type { UserSubjectList } from "@/models/Subject"; @@ -14,6 +14,10 @@ function USER_QUERY_KEY(uid: string | null): string[] { return uid ? ["user", uid] : ["user"]; } +function USERS_QUERY_KEY(): string[] { + return ["users"]; +} + export function useUserQuery(uid: Ref | null): UseQueryReturnType { return useQuery({ queryKey: computed(() => USER_QUERY_KEY(uid?.value!)), @@ -22,27 +26,70 @@ export function useUserQuery(uid: Ref | null): UseQueryRetur }); } -// TODO: Now only toggles current user -export function useToggleAdminMutation(): UseMutationReturnType { +export function useUsersQuery(): UseQueryReturnType { + return useQuery({ + queryKey: USERS_QUERY_KEY(), + queryFn: () => getUsers(), + }); +} + +// TODO: Use USER_QUERY_KEY(uid) instead of USERS_QUERY_KEY() for invalidation +function useToggleMutation( + toggleFn: (uid: string) => Promise, + getField: (_: User) => boolean, + setField: (_: User, value: boolean) => void +): UseMutationReturnType { const queryClient = useQueryClient(); return useMutation({ - mutationFn: toggleAdmin, - onMutate: async (user: User) => { - await queryClient.cancelQueries({ queryKey: USER_QUERY_KEY(null) }); - queryClient.setQueryData(USER_QUERY_KEY(null), () => { - return { ...user, is_admin: !user.is_admin }; + mutationFn: async (uid) => await toggleFn(uid), + onMutate: async (uid: string) => { + const users = queryClient.getQueryData(USERS_QUERY_KEY()); + await queryClient.cancelQueries({ queryKey: USERS_QUERY_KEY() }); + queryClient.setQueryData(USERS_QUERY_KEY(), () => { + return users?.map((user: User) => { + const mappedUser = { ...user }; + setField(mappedUser, user.uid === uid ? !getField(user) : getField(user)); + return mappedUser; + }); }); + return { previousUsers: users! }; }, - onSettled: () => { - queryClient.invalidateQueries({ queryKey: USER_QUERY_KEY(null) }); + onSettled: (_, __, uid, ctx) => { + queryClient.invalidateQueries({ queryKey: USERS_QUERY_KEY() }); }, - onError: (_, user) => { + onError: (_, uid, ctx) => { + queryClient.setQueryData(USERS_QUERY_KEY(), () => ctx!.previousUsers!); alert("Could not update user"); - queryClient.setQueryData(USER_QUERY_KEY(null), () => user!); }, }); } +export function useToggleAdminMutation(): UseMutationReturnType< + User, + Error, + string, + { previousUsers: User[] } +> { + return useToggleMutation( + toggleAdmin, + (user) => user.is_admin, + (user, value) => (user.is_admin = value) + ); +} + +export function useToggleTeacherMutation(): UseMutationReturnType< + User, + Error, + string, + { previousUsers: User[] } +> { + return useToggleMutation( + toggleTeacher, + (user) => user.is_teacher, + (user, value) => (user.is_teacher = value) + ); +} + // Hook for fetching subjects for a user export function useMySubjectsQuery(): UseQueryReturnType { return useQuery({ diff --git a/frontend/src/services/user.ts b/frontend/src/services/user.ts index 205012b4..7cc262c2 100644 --- a/frontend/src/services/user.ts +++ b/frontend/src/services/user.ts @@ -6,8 +6,16 @@ export async function getUser(uid?: string): Promise { return authorized_fetch(`/api/users/${uid || "me"}`, { method: "GET" }); } -export async function toggleAdmin() { - return authorized_fetch("/api/users/me", { method: "POST" }); +export async function getUsers(): Promise { + return authorized_fetch("/api/users", { method: "GET" }); +} + +export async function toggleAdmin(uid: string): Promise { + return authorized_fetch(`/api/users/${uid}/admin`, { method: "POST" }); +} + +export async function toggleTeacher(uid: string): Promise { + return authorized_fetch(`/api/users/${uid}/teacher`, { method: "POST" }); } // Fetches all subjects for logged in user diff --git a/frontend/src/views/AdminView.vue b/frontend/src/views/AdminView.vue index 141a911f..1ce92d1d 100644 --- a/frontend/src/views/AdminView.vue +++ b/frontend/src/views/AdminView.vue @@ -1,3 +1,127 @@ + +