feat: (frontend) queries teacher + test controller teacher
This commit is contained in:
parent
5e0f284131
commit
44c242fc57
11 changed files with 184 additions and 59 deletions
|
@ -28,27 +28,20 @@ export class BaseController {
|
|||
return res.json();
|
||||
}
|
||||
|
||||
protected async post<T>(path: string, body?: unknown): Promise<T> {
|
||||
const options: RequestInit = {
|
||||
protected async post(path: string, body: unknown): Promise<void> {
|
||||
const res = await fetch(`${this.baseUrl}${path}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
};
|
||||
|
||||
if (body !== undefined) {
|
||||
options.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
const res = await fetch(`${this.baseUrl}${path}`, options);
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const errorData = await res.json().catch(() => ({}));
|
||||
throw new Error(errorData?.error || `Error ${res.status}: ${res.statusText}`);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
protected async delete<T>(path: string): Promise<T> {
|
||||
protected async delete(path: string): Promise<void> {
|
||||
const res = await fetch(`${this.baseUrl}${path}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
@ -57,11 +50,9 @@ export class BaseController {
|
|||
const errorData = await res.json().catch(() => ({}));
|
||||
throw new Error(errorData?.error || `Error ${res.status}: ${res.statusText}`);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
protected async put<T>(path: string, body: unknown): Promise<T> {
|
||||
protected async put(path: string, body: unknown): Promise<void> {
|
||||
const res = await fetch(`${this.baseUrl}${path}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
@ -72,7 +63,5 @@ export class BaseController {
|
|||
const errorData = await res.json().catch(() => ({}));
|
||||
throw new Error(errorData?.error || `Error ${res.status}: ${res.statusText}`);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import { StudentController } from "@/controllers/students.ts";
|
||||
import { TeacherController } from "@/controllers/teachers.ts";
|
||||
import {ThemeController} from "@/controllers/themes.ts";
|
||||
|
||||
export function controllerGetter<T>(Factory: new () => T): () => T {
|
||||
let instance: T | undefined;
|
||||
|
||||
return (): T => {
|
||||
if (!instance) {
|
||||
instance = new Factory();
|
||||
}
|
||||
return instance;
|
||||
};
|
||||
}
|
||||
|
||||
export const getStudentController = controllerGetter(StudentController);
|
||||
export const getTeacherController = controllerGetter(TeacherController);
|
||||
export const getThemeController = controllerGetter(ThemeController);
|
|
@ -14,11 +14,11 @@ export class StudentController extends BaseController {
|
|||
}
|
||||
|
||||
createStudent(data: any) {
|
||||
return this.post<{ student: any }>("/", data);
|
||||
return this.post("/", data);
|
||||
}
|
||||
|
||||
deleteStudent(username: string) {
|
||||
return this.delete<{ student: any }>(`/${username}`);
|
||||
return this.delete(`/${username}`);
|
||||
}
|
||||
|
||||
getClasses(username: string, full = true) {
|
||||
|
@ -46,10 +46,10 @@ export class StudentController extends BaseController {
|
|||
}
|
||||
|
||||
createJoinRequest(username: string, classId: string) {
|
||||
return this.post<any>(`/${username}/joinRequests/${classId}`);
|
||||
return this.post(`/${username}/joinRequests}`, classId);
|
||||
}
|
||||
|
||||
deleteJoinRequest(username: string, classId: string) {
|
||||
return this.delete<any>(`/${username}/joinRequests/${classId}`);
|
||||
return this.delete(`/${username}/joinRequests/${classId}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,11 @@ export class TeacherController extends BaseController {
|
|||
}
|
||||
|
||||
createTeacher(data: any) {
|
||||
return this.post<any>("/", data);
|
||||
return this.post("/", data);
|
||||
}
|
||||
|
||||
deleteTeacher(username: string) {
|
||||
return this.delete<any>(`/${username}`);
|
||||
return this.delete(`/${username}`);
|
||||
}
|
||||
|
||||
getClasses(username: string, full = false) {
|
||||
|
@ -33,5 +33,13 @@ export class TeacherController extends BaseController {
|
|||
return this.get<{ questions: any[] }>(`/${username}/questions`, { full });
|
||||
}
|
||||
|
||||
getStudentJoinRequests(username: string, classId: string){
|
||||
return this.get<{ joinRequests: any[] }>(`/${username}/joinRequests/${classId}`);
|
||||
}
|
||||
|
||||
updateStudentJoinRequest(teacherUsername: string, classId: string, studentUsername: string, accepted: boolean){
|
||||
return this.put(`/${teacherUsername}/joinRequests/${classId}/${studentUsername}`, accepted)
|
||||
}
|
||||
|
||||
// GetInvitations(id: string) {return this.get<{ invitations: string[] }>(`/${id}/invitations`);}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { computed, toValue } from "vue";
|
||||
import type { MaybeRefOrGetter } from "vue";
|
||||
import {useMutation, useQuery, useQueryClient} from "@tanstack/vue-query";
|
||||
import { getStudentController } from "@/controllers/controllers.ts";
|
||||
import {StudentController} from "@/controllers/students.ts";
|
||||
|
||||
const studentController = getStudentController();
|
||||
const studentController = new StudentController();
|
||||
|
||||
/** 🔑 Query keys */
|
||||
const STUDENTS_QUERY_KEY = (full: boolean) => ['students', full];
|
||||
|
|
108
frontend/src/queries/teachers.ts
Normal file
108
frontend/src/queries/teachers.ts
Normal file
|
@ -0,0 +1,108 @@
|
|||
import { computed, toValue } from "vue";
|
||||
import type { MaybeRefOrGetter } from "vue";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/vue-query";
|
||||
import {TeacherController} from "@/controllers/teachers.ts";
|
||||
|
||||
const teacherController = new TeacherController();
|
||||
|
||||
/** 🔑 Query keys */
|
||||
const TEACHERS_QUERY_KEY = (full: boolean) => ["teachers", full];
|
||||
const TEACHER_QUERY_KEY = (username: string) => ["teacher", username];
|
||||
const TEACHER_CLASSES_QUERY_KEY = (username: string, full: boolean) => ["teacher-classes", username, full];
|
||||
const TEACHER_STUDENTS_QUERY_KEY = (username: string, full: boolean) => ["teacher-students", username, full];
|
||||
const TEACHER_QUESTIONS_QUERY_KEY = (username: string, full: boolean) => ["teacher-questions", username, full];
|
||||
const JOIN_REQUESTS_QUERY_KEY = (username: string, classId: string) => ["join-requests", username, classId];
|
||||
|
||||
export function useTeachersQuery(full: MaybeRefOrGetter<boolean> = false) {
|
||||
return useQuery({
|
||||
queryKey: computed(() => TEACHERS_QUERY_KEY(toValue(full))),
|
||||
queryFn: () => teacherController.getAll(toValue(full)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTeacherQuery(username: MaybeRefOrGetter<string | undefined>) {
|
||||
return useQuery({
|
||||
queryKey: computed(() => TEACHER_QUERY_KEY(toValue(username)!)),
|
||||
queryFn: () => teacherController.getByUsername(toValue(username)!),
|
||||
enabled: () => !!toValue(username),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTeacherClassesQuery(username: MaybeRefOrGetter<string | undefined>, full: MaybeRefOrGetter<boolean> = false) {
|
||||
return useQuery({
|
||||
queryKey: computed(() => TEACHER_CLASSES_QUERY_KEY(toValue(username)!, toValue(full))),
|
||||
queryFn: () => teacherController.getClasses(toValue(username)!, toValue(full)),
|
||||
enabled: () => !!toValue(username),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTeacherStudentsQuery(username: MaybeRefOrGetter<string | undefined>, full: MaybeRefOrGetter<boolean> = false) {
|
||||
return useQuery({
|
||||
queryKey: computed(() => TEACHER_STUDENTS_QUERY_KEY(toValue(username)!, toValue(full))),
|
||||
queryFn: () => teacherController.getStudents(toValue(username)!, toValue(full)),
|
||||
enabled: () => !!toValue(username),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTeacherQuestionsQuery(username: MaybeRefOrGetter<string | undefined>, full: MaybeRefOrGetter<boolean> = false) {
|
||||
return useQuery({
|
||||
queryKey: computed(() => TEACHER_QUESTIONS_QUERY_KEY(toValue(username)!, toValue(full))),
|
||||
queryFn: () => teacherController.getQuestions(toValue(username)!, toValue(full)),
|
||||
enabled: () => !!toValue(username),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTeacherJoinRequestsQuery(username: MaybeRefOrGetter<string | undefined>, classId: MaybeRefOrGetter<string | undefined>) {
|
||||
return useQuery({
|
||||
queryKey: computed(() => JOIN_REQUESTS_QUERY_KEY(toValue(username)!, toValue(classId)!)),
|
||||
queryFn: () => teacherController.getStudentJoinRequests(toValue(username)!, toValue(classId)!),
|
||||
enabled: () => !!toValue(username) && !!toValue(classId),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateTeacherMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: any) => teacherController.createTeacher(data),
|
||||
onSuccess: () => {
|
||||
await queryClient.invalidateQueries({ queryKey: ['teachers'] });
|
||||
},
|
||||
onError: (err) => {
|
||||
alert("Create teacher failed:", err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteTeacherMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (username: string) => teacherController.deleteTeacher(username),
|
||||
onSuccess: () => {
|
||||
await queryClient.invalidateQueries({ queryKey: ['teachers'] });
|
||||
},
|
||||
onError: (err) => {
|
||||
alert("Delete teacher failed:", err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateJoinRequestMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ teacherUsername, classId, studentUsername, accepted }: {
|
||||
teacherUsername: string;
|
||||
classId: string;
|
||||
studentUsername: string;
|
||||
accepted: boolean;
|
||||
}) => teacherController.updateStudentJoinRequest(teacherUsername, classId, studentUsername, accepted),
|
||||
onSuccess: (_, { teacherUsername, classId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: JOIN_REQUESTS_QUERY_KEY(teacherUsername, classId) });
|
||||
},
|
||||
onError: (err) => {
|
||||
alert("Failed to update join request:", err);
|
||||
},
|
||||
});
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import { useQuery } from "@tanstack/vue-query";
|
||||
import { getThemeController } from "@/controllers/controllers";
|
||||
import { type MaybeRefOrGetter, toValue } from "vue";
|
||||
import {ThemeController} from "@/controllers/themes.ts";
|
||||
|
||||
const themeController = getThemeController();
|
||||
const themeController = new ThemeController();
|
||||
|
||||
export const useThemeQuery = (language: MaybeRefOrGetter<string>) =>
|
||||
useQuery({
|
||||
|
|
|
@ -1,26 +1,22 @@
|
|||
import { describe, it, expect, beforeAll } from 'vitest';
|
||||
import {getStudentController} from "../../src/controllers/controllers";
|
||||
import {StudentController} from "../../src/controllers/students";
|
||||
|
||||
const controller = getStudentController();
|
||||
const controller = new StudentController();
|
||||
|
||||
describe('StudentController', () => {
|
||||
const newStudent = {
|
||||
username: 'TestStudent',
|
||||
username: 'teststudent1',
|
||||
firstName: 'Testy',
|
||||
lastName: 'McTestface',
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
// Zet eventueel mock server op hier als je dat gebruikt
|
||||
// Start backend
|
||||
});
|
||||
|
||||
it('creates a student and fetches it by username', async () => {
|
||||
// Create student
|
||||
const created = await controller.createStudent(newStudent);
|
||||
|
||||
expect(created).toBeDefined();
|
||||
expect(created.username).toBe(newStudent.username);
|
||||
|
||||
await controller.createStudent(newStudent);
|
||||
|
||||
// Fetch same student
|
||||
const fetched = await controller.getByUsername(newStudent.username);
|
||||
|
@ -33,9 +29,7 @@ describe('StudentController', () => {
|
|||
expect(student.firstName).toBe(newStudent.firstName);
|
||||
expect(student.lastName).toBe(newStudent.lastName);
|
||||
|
||||
|
||||
await controller.deleteStudent(newStudent.username);
|
||||
|
||||
await expect(controller.getByUsername(newStudent.username)).rejects.toThrow();
|
||||
|
||||
});
|
||||
});
|
||||
|
|
35
frontend/tests/controllers/teacher.test.ts
Normal file
35
frontend/tests/controllers/teacher.test.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { describe, it, expect, beforeAll } from 'vitest';
|
||||
import {TeacherController} from "../../src/controllers/teachers";
|
||||
|
||||
const controller = new TeacherController();
|
||||
|
||||
describe('TeacherController', () => {
|
||||
const newTeacher = {
|
||||
username: 'testteacher',
|
||||
firstName: 'Testy',
|
||||
lastName: 'McTestface',
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
// Start backend
|
||||
});
|
||||
|
||||
it('creates a student and fetches it by username', async () => {
|
||||
// Create student
|
||||
await controller.createTeacher(newTeacher);
|
||||
|
||||
// Fetch same student
|
||||
const fetched = await controller.getByUsername(newTeacher.username);
|
||||
|
||||
expect(fetched).toBeDefined();
|
||||
expect(fetched.teacher).toBeDefined();
|
||||
|
||||
const teacher = fetched.teacher;
|
||||
expect(teacher.username).toBe(newTeacher.username);
|
||||
expect(teacher.firstName).toBe(newTeacher.firstName);
|
||||
expect(teacher.lastName).toBe(newTeacher.lastName);
|
||||
|
||||
|
||||
await controller.deleteTeacher(newTeacher.username);
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue