Merge pull request #142 from SELab-2/feat/user-routes
Some checks failed
Lint / Run linters (push) Has been cancelled
Some checks failed
Lint / Run linters (push) Has been cancelled
feat: endpoints finaliseren users
This commit is contained in:
commit
2b2c97a82d
32 changed files with 1443 additions and 374 deletions
18
backend/src/controllers/error-helper.ts
Normal file
18
backend/src/controllers/error-helper.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { BadRequestException } from '../exceptions/bad-request-exception.js';
|
||||
|
||||
/**
|
||||
* Checks for the presence of required fields and throws a BadRequestException
|
||||
* if any are missing.
|
||||
*
|
||||
* @param fields - An object with key-value pairs to validate.
|
||||
*/
|
||||
export function requireFields(fields: Record<string, unknown>): void {
|
||||
const missing = Object.entries(fields)
|
||||
.filter(([_, value]) => value === undefined || value === null || value === '')
|
||||
.map(([key]) => key);
|
||||
|
||||
if (missing.length > 0) {
|
||||
const message = `Missing required field${missing.length > 1 ? 's' : ''}: ${missing.join(', ')}`;
|
||||
throw new BadRequestException(message);
|
||||
}
|
||||
}
|
|
@ -1,100 +1,67 @@
|
|||
import { Request, Response } from 'express';
|
||||
import {
|
||||
createClassJoinRequest,
|
||||
createStudent,
|
||||
deleteClassJoinRequest,
|
||||
deleteStudent,
|
||||
getAllStudents,
|
||||
getJoinRequestByStudentClass,
|
||||
getJoinRequestsByStudent,
|
||||
getStudent,
|
||||
getStudentAssignments,
|
||||
getStudentClasses,
|
||||
getStudentGroups,
|
||||
getStudentQuestions,
|
||||
getStudentSubmissions,
|
||||
} from '../services/students.js';
|
||||
|
||||
import { requireFields } from './error-helper.js';
|
||||
import { StudentDTO } from '@dwengo-1/common/interfaces/student';
|
||||
|
||||
// TODO: accept arguments (full, ...)
|
||||
// TODO: endpoints
|
||||
export async function getAllStudentsHandler(req: Request, res: Response): Promise<void> {
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
const students = await getAllStudents(full);
|
||||
const students: StudentDTO[] | string[] = await getAllStudents(full);
|
||||
|
||||
if (!students) {
|
||||
res.status(404).json({ error: `Student not found.` });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ students: students });
|
||||
res.json({ students });
|
||||
}
|
||||
|
||||
export async function getStudentHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.username;
|
||||
requireFields({ username });
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
const student = await getStudent(username);
|
||||
|
||||
const user = await getStudent(username);
|
||||
|
||||
if (!user) {
|
||||
res.status(404).json({
|
||||
error: `User with username '${username}' not found.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(user);
|
||||
res.json({ student });
|
||||
}
|
||||
|
||||
export async function createStudentHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.body.username;
|
||||
const firstName = req.body.firstName;
|
||||
const lastName = req.body.lastName;
|
||||
requireFields({ username, firstName, lastName });
|
||||
|
||||
const userData = req.body as StudentDTO;
|
||||
|
||||
if (!userData.username || !userData.firstName || !userData.lastName) {
|
||||
res.status(400).json({
|
||||
error: 'Missing required fields: username, firstName, lastName',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const newUser = await createStudent(userData);
|
||||
|
||||
if (!newUser) {
|
||||
res.status(500).json({
|
||||
error: 'Something went wrong while creating student',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(201).json(newUser);
|
||||
const student = await createStudent(userData);
|
||||
res.json({ student });
|
||||
}
|
||||
|
||||
export async function deleteStudentHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.username;
|
||||
requireFields({ username });
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
|
||||
const deletedUser = await deleteStudent(username);
|
||||
if (!deletedUser) {
|
||||
res.status(404).json({
|
||||
error: `User with username '${username}' not found.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).json(deletedUser);
|
||||
const student = await deleteStudent(username);
|
||||
res.json({ student });
|
||||
}
|
||||
|
||||
export async function getStudentClassesHandler(req: Request, res: Response): Promise<void> {
|
||||
const full = req.query.full === 'true';
|
||||
const username = req.params.id;
|
||||
const username = req.params.username;
|
||||
requireFields({ username });
|
||||
|
||||
const classes = await getStudentClasses(username, full);
|
||||
|
||||
res.json({ classes: classes });
|
||||
res.json({ classes });
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
@ -103,33 +70,75 @@ export async function getStudentClassesHandler(req: Request, res: Response): Pro
|
|||
// Have this assignment.
|
||||
export async function getStudentAssignmentsHandler(req: Request, res: Response): Promise<void> {
|
||||
const full = req.query.full === 'true';
|
||||
const username = req.params.id;
|
||||
const username = req.params.username;
|
||||
requireFields({ username });
|
||||
|
||||
const assignments = getStudentAssignments(username, full);
|
||||
|
||||
res.json({
|
||||
assignments: assignments,
|
||||
});
|
||||
res.json({ assignments });
|
||||
}
|
||||
|
||||
export async function getStudentGroupsHandler(req: Request, res: Response): Promise<void> {
|
||||
const full = req.query.full === 'true';
|
||||
const username = req.params.id;
|
||||
const username = req.params.username;
|
||||
requireFields({ username });
|
||||
|
||||
const groups = await getStudentGroups(username, full);
|
||||
|
||||
res.json({
|
||||
groups: groups,
|
||||
});
|
||||
res.json({ groups });
|
||||
}
|
||||
|
||||
export async function getStudentSubmissionsHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.id;
|
||||
const username = req.params.username;
|
||||
const full = req.query.full === 'true';
|
||||
requireFields({ username });
|
||||
|
||||
const submissions = await getStudentSubmissions(username, full);
|
||||
|
||||
res.json({
|
||||
submissions: submissions,
|
||||
});
|
||||
res.json({ submissions });
|
||||
}
|
||||
|
||||
export async function getStudentQuestionsHandler(req: Request, res: Response): Promise<void> {
|
||||
const full = req.query.full === 'true';
|
||||
const username = req.params.username;
|
||||
requireFields({ username });
|
||||
|
||||
const questions = await getStudentQuestions(username, full);
|
||||
|
||||
res.json({ questions });
|
||||
}
|
||||
|
||||
export async function createStudentRequestHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.username;
|
||||
const classId = req.body.classId;
|
||||
requireFields({ username, classId });
|
||||
|
||||
const request = await createClassJoinRequest(username, classId);
|
||||
res.json({ request });
|
||||
}
|
||||
|
||||
export async function getStudentRequestsHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.username;
|
||||
requireFields({ username });
|
||||
|
||||
const requests = await getJoinRequestsByStudent(username);
|
||||
res.json({ requests });
|
||||
}
|
||||
|
||||
export async function getStudentRequestHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.username;
|
||||
const classId = req.params.classId;
|
||||
requireFields({ username, classId });
|
||||
|
||||
const request = await getJoinRequestByStudentClass(username, classId);
|
||||
res.json({ request });
|
||||
}
|
||||
|
||||
export async function deleteClassJoinRequestHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.username;
|
||||
const classId = req.params.classId;
|
||||
requireFields({ username, classId });
|
||||
|
||||
const request = await deleteClassJoinRequest(username, classId);
|
||||
res.json({ request });
|
||||
}
|
||||
|
|
|
@ -4,137 +4,97 @@ import {
|
|||
deleteTeacher,
|
||||
getAllTeachers,
|
||||
getClassesByTeacher,
|
||||
getQuestionsByTeacher,
|
||||
getJoinRequestsByClass,
|
||||
getStudentsByTeacher,
|
||||
getTeacher,
|
||||
getTeacherQuestions,
|
||||
updateClassJoinRequestStatus,
|
||||
} from '../services/teachers.js';
|
||||
import { requireFields } from './error-helper.js';
|
||||
import { TeacherDTO } from '@dwengo-1/common/interfaces/teacher';
|
||||
|
||||
export async function getAllTeachersHandler(req: Request, res: Response): Promise<void> {
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
const teachers = await getAllTeachers(full);
|
||||
const teachers: TeacherDTO[] | string[] = await getAllTeachers(full);
|
||||
|
||||
if (!teachers) {
|
||||
res.status(404).json({ error: `Teacher not found.` });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ teachers: teachers });
|
||||
res.json({ teachers });
|
||||
}
|
||||
|
||||
export async function getTeacherHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.username;
|
||||
requireFields({ username });
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
const teacher = await getTeacher(username);
|
||||
|
||||
const user = await getTeacher(username);
|
||||
|
||||
if (!user) {
|
||||
res.status(404).json({
|
||||
error: `Teacher '${username}' not found.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(user);
|
||||
res.json({ teacher });
|
||||
}
|
||||
|
||||
export async function createTeacherHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.body.username;
|
||||
const firstName = req.body.firstName;
|
||||
const lastName = req.body.lastName;
|
||||
requireFields({ username, firstName, lastName });
|
||||
|
||||
const userData = req.body as TeacherDTO;
|
||||
|
||||
if (!userData.username || !userData.firstName || !userData.lastName) {
|
||||
res.status(400).json({
|
||||
error: 'Missing required fields: username, firstName, lastName',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const newUser = await createTeacher(userData);
|
||||
|
||||
if (!newUser) {
|
||||
res.status(400).json({ error: 'Failed to create teacher' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(201).json(newUser);
|
||||
const teacher = await createTeacher(userData);
|
||||
res.json({ teacher });
|
||||
}
|
||||
|
||||
export async function deleteTeacherHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.username;
|
||||
requireFields({ username });
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
|
||||
const deletedUser = await deleteTeacher(username);
|
||||
if (!deletedUser) {
|
||||
res.status(404).json({
|
||||
error: `User '${username}' not found.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).json(deletedUser);
|
||||
const teacher = await deleteTeacher(username);
|
||||
res.json({ teacher });
|
||||
}
|
||||
|
||||
export async function getTeacherClassHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.username;
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
requireFields({ username });
|
||||
|
||||
const classes = await getClassesByTeacher(username, full);
|
||||
|
||||
if (!classes) {
|
||||
res.status(404).json({ error: 'Teacher not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ classes: classes });
|
||||
res.json({ classes });
|
||||
}
|
||||
|
||||
export async function getTeacherStudentHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.username;
|
||||
const full = req.query.full === 'true';
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
requireFields({ username });
|
||||
|
||||
const students = await getStudentsByTeacher(username, full);
|
||||
|
||||
if (!students) {
|
||||
res.status(404).json({ error: 'Teacher not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ students: students });
|
||||
res.json({ students });
|
||||
}
|
||||
|
||||
export async function getTeacherQuestionHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.params.username;
|
||||
const full = req.query.full === 'true';
|
||||
requireFields({ username });
|
||||
|
||||
if (!username) {
|
||||
res.status(400).json({ error: 'Missing required field: username' });
|
||||
return;
|
||||
}
|
||||
const questions = await getTeacherQuestions(username, full);
|
||||
|
||||
const questions = await getQuestionsByTeacher(username, full);
|
||||
|
||||
if (!questions) {
|
||||
res.status(404).json({ error: 'Teacher not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({ questions: questions });
|
||||
res.json({ questions });
|
||||
}
|
||||
|
||||
export async function getStudentJoinRequestHandler(req: Request, res: Response): Promise<void> {
|
||||
const username = req.query.username as string;
|
||||
const classId = req.params.classId;
|
||||
requireFields({ username, classId });
|
||||
|
||||
const joinRequests = await getJoinRequestsByClass(classId);
|
||||
res.json({ joinRequests });
|
||||
}
|
||||
|
||||
export async function updateStudentJoinRequestHandler(req: Request, res: Response): Promise<void> {
|
||||
const studentUsername = req.query.studentUsername as string;
|
||||
const classId = req.params.classId;
|
||||
const accepted = req.body.accepted !== 'false'; // Default = true
|
||||
requireFields({ studentUsername, classId });
|
||||
|
||||
const request = await updateClassJoinRequestStatus(studentUsername, classId, accepted);
|
||||
res.json({ request });
|
||||
}
|
||||
|
|
|
@ -2,13 +2,17 @@ import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
|
|||
import { Class } from '../../entities/classes/class.entity.js';
|
||||
import { ClassJoinRequest } from '../../entities/classes/class-join-request.entity.js';
|
||||
import { Student } from '../../entities/users/student.entity.js';
|
||||
import { ClassJoinRequestStatus } from '@dwengo-1/common/util/class-join-request';
|
||||
|
||||
export class ClassJoinRequestRepository extends DwengoEntityRepository<ClassJoinRequest> {
|
||||
public async findAllRequestsBy(requester: Student): Promise<ClassJoinRequest[]> {
|
||||
return this.findAll({ where: { requester: requester } });
|
||||
}
|
||||
public async findAllOpenRequestsTo(clazz: Class): Promise<ClassJoinRequest[]> {
|
||||
return this.findAll({ where: { class: clazz } });
|
||||
return this.findAll({ where: { class: clazz, status: ClassJoinRequestStatus.Open } }); // TODO check if works like this
|
||||
}
|
||||
public async findByStudentAndClass(requester: Student, clazz: Class): Promise<ClassJoinRequest | null> {
|
||||
return this.findOne({ requester, class: clazz });
|
||||
}
|
||||
public async deleteBy(requester: Student, clazz: Class): Promise<void> {
|
||||
return this.deleteWhere({ requester: requester, class: clazz });
|
||||
|
|
|
@ -54,4 +54,11 @@ export class QuestionRepository extends DwengoEntityRepository<Question> {
|
|||
orderBy: { timestamp: 'ASC' },
|
||||
});
|
||||
}
|
||||
|
||||
public async findAllByAuthor(author: Student): Promise<Question[]> {
|
||||
return this.findAll({
|
||||
where: { author },
|
||||
orderBy: { timestamp: 'DESC' }, // New to old
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
export interface Theme {
|
||||
title: string;
|
||||
hruids: string[];
|
||||
}
|
||||
import { Theme } from '@dwengo-1/common/interfaces/theme';
|
||||
|
||||
export const themes: Theme[] = [
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { mapToUserDTO } from './user.js';
|
||||
import { mapToQuestionDTO, mapToQuestionId } from './question.js';
|
||||
import { mapToQuestionDTO, mapToQuestionDTOId } from './question.js';
|
||||
import { Answer } from '../entities/questions/answer.entity.js';
|
||||
import { AnswerDTO, AnswerId } from '@dwengo-1/common/interfaces/answer';
|
||||
|
||||
|
@ -16,10 +16,10 @@ export function mapToAnswerDTO(answer: Answer): AnswerDTO {
|
|||
};
|
||||
}
|
||||
|
||||
export function mapToAnswerId(answer: AnswerDTO): AnswerId {
|
||||
export function mapToAnswerDTOId(answer: Answer): AnswerId {
|
||||
return {
|
||||
author: answer.author.username,
|
||||
toQuestion: mapToQuestionId(answer.toQuestion),
|
||||
sequenceNumber: answer.sequenceNumber,
|
||||
toQuestion: mapToQuestionDTOId(answer.toQuestion),
|
||||
sequenceNumber: answer.sequenceNumber!,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
import { Question } from '../entities/questions/question.entity.js';
|
||||
import { mapToStudentDTO } from './student.js';
|
||||
import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question';
|
||||
import { LearningObjectIdentifier } from '@dwengo-1/common/interfaces/learning-content';
|
||||
|
||||
function getLearningObjectIdentifier(question: Question): LearningObjectIdentifier {
|
||||
return {
|
||||
hruid: question.learningObjectHruid,
|
||||
language: question.learningObjectLanguage,
|
||||
version: question.learningObjectVersion,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Question entity to a DTO format.
|
||||
*/
|
||||
export function mapToQuestionDTO(question: Question): QuestionDTO {
|
||||
const learningObjectIdentifier = {
|
||||
hruid: question.learningObjectHruid,
|
||||
language: question.learningObjectLanguage,
|
||||
version: question.learningObjectVersion,
|
||||
};
|
||||
const learningObjectIdentifier = getLearningObjectIdentifier(question);
|
||||
|
||||
return {
|
||||
learningObjectIdentifier,
|
||||
|
@ -21,9 +26,11 @@ export function mapToQuestionDTO(question: Question): QuestionDTO {
|
|||
};
|
||||
}
|
||||
|
||||
export function mapToQuestionId(question: QuestionDTO): QuestionId {
|
||||
export function mapToQuestionDTOId(question: Question): QuestionId {
|
||||
const learningObjectIdentifier = getLearningObjectIdentifier(question);
|
||||
|
||||
return {
|
||||
learningObjectIdentifier: question.learningObjectIdentifier,
|
||||
learningObjectIdentifier,
|
||||
sequenceNumber: question.sequenceNumber!,
|
||||
};
|
||||
}
|
||||
|
|
23
backend/src/interfaces/student-request.ts
Normal file
23
backend/src/interfaces/student-request.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { mapToStudentDTO } from './student.js';
|
||||
import { ClassJoinRequest } from '../entities/classes/class-join-request.entity.js';
|
||||
import { getClassJoinRequestRepository } from '../data/repositories.js';
|
||||
import { Student } from '../entities/users/student.entity.js';
|
||||
import { Class } from '../entities/classes/class.entity.js';
|
||||
import { ClassJoinRequestDTO } from '@dwengo-1/common/interfaces/class-join-request';
|
||||
import { ClassJoinRequestStatus } from '@dwengo-1/common/util/class-join-request';
|
||||
|
||||
export function mapToStudentRequestDTO(request: ClassJoinRequest): ClassJoinRequestDTO {
|
||||
return {
|
||||
requester: mapToStudentDTO(request.requester),
|
||||
class: request.class.classId!,
|
||||
status: request.status,
|
||||
};
|
||||
}
|
||||
|
||||
export function mapToStudentRequest(student: Student, cls: Class): ClassJoinRequest {
|
||||
return getClassJoinRequestRepository().create({
|
||||
requester: student,
|
||||
class: cls,
|
||||
status: ClassJoinRequestStatus.Open,
|
||||
});
|
||||
}
|
19
backend/src/routes/student-join-requests.ts
Normal file
19
backend/src/routes/student-join-requests.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import express from 'express';
|
||||
import {
|
||||
createStudentRequestHandler,
|
||||
deleteClassJoinRequestHandler,
|
||||
getStudentRequestHandler,
|
||||
getStudentRequestsHandler,
|
||||
} from '../controllers/students.js';
|
||||
|
||||
const router = express.Router({ mergeParams: true });
|
||||
|
||||
router.get('/', getStudentRequestsHandler);
|
||||
|
||||
router.post('/', createStudentRequestHandler);
|
||||
|
||||
router.get('/:classId', getStudentRequestHandler);
|
||||
|
||||
router.delete('/:classId', deleteClassJoinRequestHandler);
|
||||
|
||||
export default router;
|
|
@ -7,8 +7,10 @@ import {
|
|||
getStudentClassesHandler,
|
||||
getStudentGroupsHandler,
|
||||
getStudentHandler,
|
||||
getStudentQuestionsHandler,
|
||||
getStudentSubmissionsHandler,
|
||||
} from '../controllers/students.js';
|
||||
import joinRequestRouter from './student-join-requests.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
|
@ -17,30 +19,26 @@ router.get('/', getAllStudentsHandler);
|
|||
|
||||
router.post('/', createStudentHandler);
|
||||
|
||||
router.delete('/', deleteStudentHandler);
|
||||
|
||||
router.delete('/:username', deleteStudentHandler);
|
||||
|
||||
// Information about a student's profile
|
||||
router.get('/:username', getStudentHandler);
|
||||
|
||||
// The list of classes a student is in
|
||||
router.get('/:id/classes', getStudentClassesHandler);
|
||||
router.get('/:username/classes', getStudentClassesHandler);
|
||||
|
||||
// The list of submissions a student has made
|
||||
router.get('/:id/submissions', getStudentSubmissionsHandler);
|
||||
router.get('/:username/submissions', getStudentSubmissionsHandler);
|
||||
|
||||
// The list of assignments a student has
|
||||
router.get('/:id/assignments', getStudentAssignmentsHandler);
|
||||
router.get('/:username/assignments', getStudentAssignmentsHandler);
|
||||
|
||||
// The list of groups a student is in
|
||||
router.get('/:id/groups', getStudentGroupsHandler);
|
||||
router.get('/:username/groups', getStudentGroupsHandler);
|
||||
|
||||
// A list of questions a user has created
|
||||
router.get('/:id/questions', (_req, res) => {
|
||||
res.json({
|
||||
questions: ['0'],
|
||||
});
|
||||
});
|
||||
router.get('/:username/questions', getStudentQuestionsHandler);
|
||||
|
||||
router.use('/:username/joinRequests', joinRequestRouter);
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -3,10 +3,12 @@ import {
|
|||
createTeacherHandler,
|
||||
deleteTeacherHandler,
|
||||
getAllTeachersHandler,
|
||||
getStudentJoinRequestHandler,
|
||||
getTeacherClassHandler,
|
||||
getTeacherHandler,
|
||||
getTeacherQuestionHandler,
|
||||
getTeacherStudentHandler,
|
||||
updateStudentJoinRequestHandler,
|
||||
} from '../controllers/teachers.js';
|
||||
const router = express.Router();
|
||||
|
||||
|
@ -15,8 +17,6 @@ router.get('/', getAllTeachersHandler);
|
|||
|
||||
router.post('/', createTeacherHandler);
|
||||
|
||||
router.delete('/', deleteTeacherHandler);
|
||||
|
||||
router.get('/:username', getTeacherHandler);
|
||||
|
||||
router.delete('/:username', deleteTeacherHandler);
|
||||
|
@ -27,6 +27,10 @@ router.get('/:username/students', getTeacherStudentHandler);
|
|||
|
||||
router.get('/:username/questions', getTeacherQuestionHandler);
|
||||
|
||||
router.get('/:username/joinRequests/:classId', getStudentJoinRequestHandler);
|
||||
|
||||
router.put('/:username/joinRequests/:classId/:studentUsername', updateStudentJoinRequestHandler);
|
||||
|
||||
// Invitations to other classes a teacher received
|
||||
router.get('/:id/invitations', (_req, res) => {
|
||||
res.json({
|
||||
|
|
|
@ -3,12 +3,25 @@ import { mapToClassDTO } from '../interfaces/class.js';
|
|||
import { mapToStudentDTO } from '../interfaces/student.js';
|
||||
import { mapToTeacherInvitationDTO, mapToTeacherInvitationDTOIds } from '../interfaces/teacher-invitation.js';
|
||||
import { getLogger } from '../logging/initalize.js';
|
||||
import { NotFoundException } from '../exceptions/not-found-exception.js';
|
||||
import { Class } from '../entities/classes/class.entity.js';
|
||||
import { ClassDTO } from '@dwengo-1/common/interfaces/class';
|
||||
import { TeacherInvitationDTO } from '@dwengo-1/common/interfaces/teacher-invitation';
|
||||
import { StudentDTO } from '@dwengo-1/common/interfaces/student';
|
||||
|
||||
const logger = getLogger();
|
||||
|
||||
export async function fetchClass(classId: string): Promise<Class> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classId);
|
||||
|
||||
if (!cls) {
|
||||
throw new NotFoundException('Class with id not found');
|
||||
}
|
||||
|
||||
return cls;
|
||||
}
|
||||
|
||||
export async function getAllClasses(full: boolean): Promise<ClassDTO[] | string[]> {
|
||||
const classRepository = getClassRepository();
|
||||
const classes = await classRepository.find({}, { populate: ['students', 'teachers'] });
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { getAnswerRepository, getQuestionRepository } from '../data/repositories.js';
|
||||
import { mapToQuestionDTO, mapToQuestionId } from '../interfaces/question.js';
|
||||
import { mapToQuestionDTO, mapToQuestionDTOId } from '../interfaces/question.js';
|
||||
import { Question } from '../entities/questions/question.entity.js';
|
||||
import { Answer } from '../entities/questions/answer.entity.js';
|
||||
import { mapToAnswerDTO, mapToAnswerId } from '../interfaces/answer.js';
|
||||
import { mapToAnswerDTO, mapToAnswerDTOId } from '../interfaces/answer.js';
|
||||
import { QuestionRepository } from '../data/questions/question-repository.js';
|
||||
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
|
||||
import { mapToStudent } from '../interfaces/student.js';
|
||||
|
@ -17,13 +17,11 @@ export async function getAllQuestions(id: LearningObjectIdentifier, full: boolea
|
|||
return [];
|
||||
}
|
||||
|
||||
const questionsDTO: QuestionDTO[] = questions.map(mapToQuestionDTO);
|
||||
|
||||
if (full) {
|
||||
return questionsDTO;
|
||||
return questions.map(mapToQuestionDTO);
|
||||
}
|
||||
|
||||
return questionsDTO.map(mapToQuestionId);
|
||||
return questions.map(mapToQuestionDTOId);
|
||||
}
|
||||
|
||||
async function fetchQuestion(questionId: QuestionId): Promise<Question | null> {
|
||||
|
@ -61,13 +59,11 @@ export async function getAnswersByQuestion(questionId: QuestionId, full: boolean
|
|||
return [];
|
||||
}
|
||||
|
||||
const answersDTO = answers.map(mapToAnswerDTO);
|
||||
|
||||
if (full) {
|
||||
return answersDTO;
|
||||
return answers.map(mapToAnswerDTO);
|
||||
}
|
||||
|
||||
return answersDTO.map(mapToAnswerId);
|
||||
return answers.map(mapToAnswerDTOId);
|
||||
}
|
||||
|
||||
export async function createQuestion(questionDTO: QuestionDTO): Promise<QuestionDTO | null> {
|
||||
|
|
|
@ -1,67 +1,75 @@
|
|||
import { getClassRepository, getGroupRepository, getStudentRepository, getSubmissionRepository } from '../data/repositories.js';
|
||||
import {
|
||||
getClassJoinRequestRepository,
|
||||
getClassRepository,
|
||||
getGroupRepository,
|
||||
getQuestionRepository,
|
||||
getStudentRepository,
|
||||
getSubmissionRepository,
|
||||
} from '../data/repositories.js';
|
||||
import { mapToClassDTO } from '../interfaces/class.js';
|
||||
import { mapToGroupDTO, mapToGroupDTOId } from '../interfaces/group.js';
|
||||
import { mapToStudent, mapToStudentDTO } from '../interfaces/student.js';
|
||||
import { mapToSubmissionDTO, mapToSubmissionDTOId } from '../interfaces/submission.js';
|
||||
import { getAllAssignments } from './assignments.js';
|
||||
import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment';
|
||||
import { mapToQuestionDTO, mapToQuestionDTOId } from '../interfaces/question.js';
|
||||
import { mapToStudentRequest, mapToStudentRequestDTO } from '../interfaces/student-request.js';
|
||||
import { Student } from '../entities/users/student.entity.js';
|
||||
import { NotFoundException } from '../exceptions/not-found-exception.js';
|
||||
import { fetchClass } from './classes.js';
|
||||
import { StudentDTO } from '@dwengo-1/common/interfaces/student';
|
||||
import { ClassDTO } from '@dwengo-1/common/interfaces/class';
|
||||
import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment';
|
||||
import { GroupDTO } from '@dwengo-1/common/interfaces/group';
|
||||
import { SubmissionDTO, SubmissionDTOId } from '@dwengo-1/common/interfaces/submission';
|
||||
import { StudentDTO } from '@dwengo-1/common/interfaces/student';
|
||||
import { getLogger } from '../logging/initalize.js';
|
||||
import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question';
|
||||
import { ClassJoinRequestDTO } from '@dwengo-1/common/interfaces/class-join-request';
|
||||
|
||||
export async function getAllStudents(full: boolean): Promise<StudentDTO[] | string[]> {
|
||||
const studentRepository = getStudentRepository();
|
||||
const students = await studentRepository.findAll();
|
||||
const users = await studentRepository.findAll();
|
||||
|
||||
if (full) {
|
||||
return students.map(mapToStudentDTO);
|
||||
return users.map(mapToStudentDTO);
|
||||
}
|
||||
|
||||
return students.map((student) => student.username);
|
||||
return users.map((user) => user.username);
|
||||
}
|
||||
|
||||
export async function getStudent(username: string): Promise<StudentDTO | null> {
|
||||
export async function fetchStudent(username: string): Promise<Student> {
|
||||
const studentRepository = getStudentRepository();
|
||||
const user = await studentRepository.findByUsername(username);
|
||||
return user ? mapToStudentDTO(user) : null;
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException('Student with username not found');
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
export async function createStudent(userData: StudentDTO): Promise<StudentDTO | null> {
|
||||
export async function getStudent(username: string): Promise<StudentDTO> {
|
||||
const user = await fetchStudent(username);
|
||||
return mapToStudentDTO(user);
|
||||
}
|
||||
|
||||
export async function createStudent(userData: StudentDTO): Promise<StudentDTO> {
|
||||
const studentRepository = getStudentRepository();
|
||||
|
||||
const newStudent = mapToStudent(userData);
|
||||
await studentRepository.save(newStudent, { preventOverwrite: true });
|
||||
return mapToStudentDTO(newStudent);
|
||||
return userData;
|
||||
}
|
||||
|
||||
export async function deleteStudent(username: string): Promise<StudentDTO | null> {
|
||||
export async function deleteStudent(username: string): Promise<StudentDTO> {
|
||||
const studentRepository = getStudentRepository();
|
||||
|
||||
const user = await studentRepository.findByUsername(username);
|
||||
const student = await fetchStudent(username); // Throws error if it does not exist
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
await studentRepository.deleteByUsername(username);
|
||||
|
||||
return mapToStudentDTO(user);
|
||||
} catch (e) {
|
||||
getLogger().error(e);
|
||||
return null;
|
||||
}
|
||||
await studentRepository.deleteByUsername(username);
|
||||
return mapToStudentDTO(student);
|
||||
}
|
||||
|
||||
export async function getStudentClasses(username: string, full: boolean): Promise<ClassDTO[] | string[]> {
|
||||
const studentRepository = getStudentRepository();
|
||||
const student = await studentRepository.findByUsername(username);
|
||||
|
||||
if (!student) {
|
||||
return [];
|
||||
}
|
||||
const student = await fetchStudent(username);
|
||||
|
||||
const classRepository = getClassRepository();
|
||||
const classes = await classRepository.findByStudent(student);
|
||||
|
@ -74,12 +82,7 @@ export async function getStudentClasses(username: string, full: boolean): Promis
|
|||
}
|
||||
|
||||
export async function getStudentAssignments(username: string, full: boolean): Promise<AssignmentDTO[]> {
|
||||
const studentRepository = getStudentRepository();
|
||||
const student = await studentRepository.findByUsername(username);
|
||||
|
||||
if (!student) {
|
||||
return [];
|
||||
}
|
||||
const student = await fetchStudent(username);
|
||||
|
||||
const classRepository = getClassRepository();
|
||||
const classes = await classRepository.findByStudent(student);
|
||||
|
@ -88,12 +91,7 @@ export async function getStudentAssignments(username: string, full: boolean): Pr
|
|||
}
|
||||
|
||||
export async function getStudentGroups(username: string, full: boolean): Promise<GroupDTO[]> {
|
||||
const studentRepository = getStudentRepository();
|
||||
const student = await studentRepository.findByUsername(username);
|
||||
|
||||
if (!student) {
|
||||
return [];
|
||||
}
|
||||
const student = await fetchStudent(username);
|
||||
|
||||
const groupRepository = getGroupRepository();
|
||||
const groups = await groupRepository.findAllGroupsWithStudent(student);
|
||||
|
@ -106,12 +104,7 @@ export async function getStudentGroups(username: string, full: boolean): Promise
|
|||
}
|
||||
|
||||
export async function getStudentSubmissions(username: string, full: boolean): Promise<SubmissionDTO[] | SubmissionDTOId[]> {
|
||||
const studentRepository = getStudentRepository();
|
||||
const student = await studentRepository.findByUsername(username);
|
||||
|
||||
if (!student) {
|
||||
return [];
|
||||
}
|
||||
const student = await fetchStudent(username);
|
||||
|
||||
const submissionRepository = getSubmissionRepository();
|
||||
const submissions = await submissionRepository.findAllSubmissionsForStudent(student);
|
||||
|
@ -122,3 +115,66 @@ export async function getStudentSubmissions(username: string, full: boolean): Pr
|
|||
|
||||
return submissions.map(mapToSubmissionDTOId);
|
||||
}
|
||||
|
||||
export async function getStudentQuestions(username: string, full: boolean): Promise<QuestionDTO[] | QuestionId[]> {
|
||||
const student = await fetchStudent(username);
|
||||
|
||||
const questionRepository = getQuestionRepository();
|
||||
const questions = await questionRepository.findAllByAuthor(student);
|
||||
|
||||
if (full) {
|
||||
return questions.map(mapToQuestionDTO);
|
||||
}
|
||||
|
||||
return questions.map(mapToQuestionDTOId);
|
||||
}
|
||||
|
||||
export async function createClassJoinRequest(username: string, classId: string): Promise<ClassJoinRequestDTO> {
|
||||
const requestRepo = getClassJoinRequestRepository();
|
||||
|
||||
const student = await fetchStudent(username); // Throws error if student not found
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
const request = mapToStudentRequest(student, cls);
|
||||
await requestRepo.save(request, { preventOverwrite: true });
|
||||
return mapToStudentRequestDTO(request);
|
||||
}
|
||||
|
||||
export async function getJoinRequestsByStudent(username: string): Promise<ClassJoinRequestDTO[]> {
|
||||
const requestRepo = getClassJoinRequestRepository();
|
||||
|
||||
const student = await fetchStudent(username);
|
||||
|
||||
const requests = await requestRepo.findAllRequestsBy(student);
|
||||
return requests.map(mapToStudentRequestDTO);
|
||||
}
|
||||
|
||||
export async function getJoinRequestByStudentClass(username: string, classId: string): Promise<ClassJoinRequestDTO> {
|
||||
const requestRepo = getClassJoinRequestRepository();
|
||||
|
||||
const student = await fetchStudent(username);
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
const request = await requestRepo.findByStudentAndClass(student, cls);
|
||||
if (!request) {
|
||||
throw new NotFoundException('Join request not found');
|
||||
}
|
||||
|
||||
return mapToStudentRequestDTO(request);
|
||||
}
|
||||
|
||||
export async function deleteClassJoinRequest(username: string, classId: string): Promise<ClassJoinRequestDTO> {
|
||||
const requestRepo = getClassJoinRequestRepository();
|
||||
|
||||
const student = await fetchStudent(username);
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
const request = await requestRepo.findByStudentAndClass(student, cls);
|
||||
|
||||
if (!request) {
|
||||
throw new NotFoundException('Join request not found');
|
||||
}
|
||||
|
||||
await requestRepo.deleteBy(student, cls);
|
||||
return mapToStudentRequestDTO(request);
|
||||
}
|
||||
|
|
|
@ -1,137 +1,165 @@
|
|||
import { getClassRepository, getLearningObjectRepository, getQuestionRepository, getTeacherRepository } from '../data/repositories.js';
|
||||
import {
|
||||
getClassJoinRequestRepository,
|
||||
getClassRepository,
|
||||
getLearningObjectRepository,
|
||||
getQuestionRepository,
|
||||
getTeacherRepository,
|
||||
} from '../data/repositories.js';
|
||||
import { mapToClassDTO } from '../interfaces/class.js';
|
||||
import { getClassStudents } from './classes.js';
|
||||
import { mapToQuestionDTO, mapToQuestionId } from '../interfaces/question.js';
|
||||
import { mapToQuestionDTO, mapToQuestionDTOId } from '../interfaces/question.js';
|
||||
import { mapToTeacher, mapToTeacherDTO } from '../interfaces/teacher.js';
|
||||
import { ClassDTO } from '@dwengo-1/common/interfaces/class';
|
||||
import { Teacher } from '../entities/users/teacher.entity.js';
|
||||
import { fetchStudent } from './students.js';
|
||||
import { ClassJoinRequest } from '../entities/classes/class-join-request.entity.js';
|
||||
import { mapToStudentRequestDTO } from '../interfaces/student-request.js';
|
||||
import { TeacherRepository } from '../data/users/teacher-repository.js';
|
||||
import { ClassRepository } from '../data/classes/class-repository.js';
|
||||
import { Class } from '../entities/classes/class.entity.js';
|
||||
import { LearningObjectRepository } from '../data/content/learning-object-repository.js';
|
||||
import { LearningObject } from '../entities/content/learning-object.entity.js';
|
||||
import { QuestionRepository } from '../data/questions/question-repository.js';
|
||||
import { Question } from '../entities/questions/question.entity.js';
|
||||
import { ClassJoinRequestRepository } from '../data/classes/class-join-request-repository.js';
|
||||
import { Student } from '../entities/users/student.entity.js';
|
||||
import { NotFoundException } from '../exceptions/not-found-exception.js';
|
||||
import { getClassStudents } from './classes.js';
|
||||
import { TeacherDTO } from '@dwengo-1/common/interfaces/teacher';
|
||||
import { ClassDTO } from '@dwengo-1/common/interfaces/class';
|
||||
import { StudentDTO } from '@dwengo-1/common/interfaces/student';
|
||||
import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question';
|
||||
import { getLogger } from '../logging/initalize.js';
|
||||
import { ClassJoinRequestDTO } from '@dwengo-1/common/interfaces/class-join-request';
|
||||
import { ClassJoinRequestStatus } from '@dwengo-1/common/util/class-join-request';
|
||||
|
||||
export async function getAllTeachers(full: boolean): Promise<TeacherDTO[] | string[]> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const teachers = await teacherRepository.findAll();
|
||||
const teacherRepository: TeacherRepository = getTeacherRepository();
|
||||
const users: Teacher[] = await teacherRepository.findAll();
|
||||
|
||||
if (full) {
|
||||
return teachers.map(mapToTeacherDTO);
|
||||
return users.map(mapToTeacherDTO);
|
||||
}
|
||||
return users.map((user) => user.username);
|
||||
}
|
||||
|
||||
export async function fetchTeacher(username: string): Promise<Teacher> {
|
||||
const teacherRepository: TeacherRepository = getTeacherRepository();
|
||||
const user: Teacher | null = await teacherRepository.findByUsername(username);
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException('Teacher with username not found');
|
||||
}
|
||||
|
||||
return teachers.map((teacher) => teacher.username);
|
||||
return user;
|
||||
}
|
||||
|
||||
export async function getTeacher(username: string): Promise<TeacherDTO | null> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const user = await teacherRepository.findByUsername(username);
|
||||
return user ? mapToTeacherDTO(user) : null;
|
||||
export async function getTeacher(username: string): Promise<TeacherDTO> {
|
||||
const user: Teacher = await fetchTeacher(username);
|
||||
return mapToTeacherDTO(user);
|
||||
}
|
||||
|
||||
export async function createTeacher(userData: TeacherDTO): Promise<TeacherDTO | null> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
export async function createTeacher(userData: TeacherDTO): Promise<TeacherDTO> {
|
||||
const teacherRepository: TeacherRepository = getTeacherRepository();
|
||||
|
||||
const newTeacher = mapToTeacher(userData);
|
||||
await teacherRepository.save(newTeacher, { preventOverwrite: true });
|
||||
|
||||
return mapToTeacherDTO(newTeacher);
|
||||
}
|
||||
|
||||
export async function deleteTeacher(username: string): Promise<TeacherDTO | null> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
export async function deleteTeacher(username: string): Promise<TeacherDTO> {
|
||||
const teacherRepository: TeacherRepository = getTeacherRepository();
|
||||
|
||||
const user = await teacherRepository.findByUsername(username);
|
||||
const teacher = await fetchTeacher(username); // Throws error if it does not exist
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
await teacherRepository.deleteByUsername(username);
|
||||
|
||||
return mapToTeacherDTO(user);
|
||||
} catch (e) {
|
||||
getLogger().error(e);
|
||||
return null;
|
||||
}
|
||||
await teacherRepository.deleteByUsername(username);
|
||||
return mapToTeacherDTO(teacher);
|
||||
}
|
||||
|
||||
export async function fetchClassesByTeacher(username: string): Promise<ClassDTO[] | null> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const teacher = await teacherRepository.findByUsername(username);
|
||||
if (!teacher) {
|
||||
return null;
|
||||
}
|
||||
async function fetchClassesByTeacher(username: string): Promise<ClassDTO[]> {
|
||||
const teacher: Teacher = await fetchTeacher(username);
|
||||
|
||||
const classRepository = getClassRepository();
|
||||
const classes = await classRepository.findByTeacher(teacher);
|
||||
const classRepository: ClassRepository = getClassRepository();
|
||||
const classes: Class[] = await classRepository.findByTeacher(teacher);
|
||||
return classes.map(mapToClassDTO);
|
||||
}
|
||||
|
||||
export async function getClassesByTeacher(username: string, full: boolean): Promise<ClassDTO[] | string[] | null> {
|
||||
const classes = await fetchClassesByTeacher(username);
|
||||
|
||||
if (!classes) {
|
||||
return null;
|
||||
}
|
||||
export async function getClassesByTeacher(username: string, full: boolean): Promise<ClassDTO[] | string[]> {
|
||||
const classes: ClassDTO[] = await fetchClassesByTeacher(username);
|
||||
|
||||
if (full) {
|
||||
return classes;
|
||||
}
|
||||
|
||||
return classes.map((cls) => cls.id);
|
||||
}
|
||||
|
||||
export async function fetchStudentsByTeacher(username: string): Promise<StudentDTO[] | null> {
|
||||
const classes = (await getClassesByTeacher(username, false)) as string[];
|
||||
export async function getStudentsByTeacher(username: string, full: boolean): Promise<StudentDTO[] | string[]> {
|
||||
const classes: ClassDTO[] = await fetchClassesByTeacher(username);
|
||||
|
||||
if (!classes) {
|
||||
return null;
|
||||
if (!classes || classes.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return (await Promise.all(classes.map(async (id) => getClassStudents(id)))).flat();
|
||||
}
|
||||
|
||||
export async function getStudentsByTeacher(username: string, full: boolean): Promise<StudentDTO[] | string[] | null> {
|
||||
const students = await fetchStudentsByTeacher(username);
|
||||
|
||||
if (!students) {
|
||||
return null;
|
||||
}
|
||||
const classIds: string[] = classes.map((cls) => cls.id);
|
||||
|
||||
const students: StudentDTO[] = (await Promise.all(classIds.map(async (id) => getClassStudents(id)))).flat();
|
||||
if (full) {
|
||||
return students;
|
||||
}
|
||||
|
||||
return students.map((student) => student.username);
|
||||
}
|
||||
|
||||
export async function fetchTeacherQuestions(username: string): Promise<QuestionDTO[] | null> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const teacher = await teacherRepository.findByUsername(username);
|
||||
if (!teacher) {
|
||||
return null;
|
||||
}
|
||||
export async function getTeacherQuestions(username: string, full: boolean): Promise<QuestionDTO[] | QuestionId[]> {
|
||||
const teacher: Teacher = await fetchTeacher(username);
|
||||
|
||||
// Find all learning objects that this teacher manages
|
||||
const learningObjectRepository = getLearningObjectRepository();
|
||||
const learningObjects = await learningObjectRepository.findAllByTeacher(teacher);
|
||||
const learningObjectRepository: LearningObjectRepository = getLearningObjectRepository();
|
||||
const learningObjects: LearningObject[] = await learningObjectRepository.findAllByTeacher(teacher);
|
||||
|
||||
if (!learningObjects || learningObjects.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Fetch all questions related to these learning objects
|
||||
const questionRepository = getQuestionRepository();
|
||||
const questions = await questionRepository.findAllByLearningObjects(learningObjects);
|
||||
|
||||
return questions.map(mapToQuestionDTO);
|
||||
}
|
||||
|
||||
export async function getQuestionsByTeacher(username: string, full: boolean): Promise<QuestionDTO[] | QuestionId[] | null> {
|
||||
const questions = await fetchTeacherQuestions(username);
|
||||
|
||||
if (!questions) {
|
||||
return null;
|
||||
}
|
||||
const questionRepository: QuestionRepository = getQuestionRepository();
|
||||
const questions: Question[] = await questionRepository.findAllByLearningObjects(learningObjects);
|
||||
|
||||
if (full) {
|
||||
return questions;
|
||||
return questions.map(mapToQuestionDTO);
|
||||
}
|
||||
|
||||
return questions.map(mapToQuestionId);
|
||||
return questions.map(mapToQuestionDTOId);
|
||||
}
|
||||
|
||||
export async function getJoinRequestsByClass(classId: string): Promise<ClassJoinRequestDTO[]> {
|
||||
const classRepository: ClassRepository = getClassRepository();
|
||||
const cls: Class | null = await classRepository.findById(classId);
|
||||
|
||||
if (!cls) {
|
||||
throw new NotFoundException('Class with id not found');
|
||||
}
|
||||
|
||||
const requestRepo: ClassJoinRequestRepository = getClassJoinRequestRepository();
|
||||
const requests: ClassJoinRequest[] = await requestRepo.findAllOpenRequestsTo(cls);
|
||||
return requests.map(mapToStudentRequestDTO);
|
||||
}
|
||||
|
||||
export async function updateClassJoinRequestStatus(studentUsername: string, classId: string, accepted = true): Promise<ClassJoinRequestDTO> {
|
||||
const requestRepo: ClassJoinRequestRepository = getClassJoinRequestRepository();
|
||||
const classRepo: ClassRepository = getClassRepository();
|
||||
|
||||
const student: Student = await fetchStudent(studentUsername);
|
||||
const cls: Class | null = await classRepo.findById(classId);
|
||||
|
||||
if (!cls) {
|
||||
throw new NotFoundException('Class not found');
|
||||
}
|
||||
|
||||
const request: ClassJoinRequest | null = await requestRepo.findByStudentAndClass(student, cls);
|
||||
|
||||
if (!request) {
|
||||
throw new NotFoundException('Join request not found');
|
||||
}
|
||||
|
||||
request.status = accepted ? ClassJoinRequestStatus.Accepted : ClassJoinRequestStatus.Declined;
|
||||
|
||||
await requestRepo.save(request);
|
||||
return mapToStudentRequestDTO(request);
|
||||
}
|
||||
|
|
232
backend/tests/controllers/students.test.ts
Normal file
232
backend/tests/controllers/students.test.ts
Normal file
|
@ -0,0 +1,232 @@
|
|||
import { setupTestApp } from '../setup-tests.js';
|
||||
import { describe, it, expect, beforeAll, beforeEach, vi, Mock } from 'vitest';
|
||||
import { Request, Response } from 'express';
|
||||
import {
|
||||
getAllStudentsHandler,
|
||||
getStudentHandler,
|
||||
createStudentHandler,
|
||||
deleteStudentHandler,
|
||||
getStudentClassesHandler,
|
||||
getStudentGroupsHandler,
|
||||
getStudentSubmissionsHandler,
|
||||
getStudentQuestionsHandler,
|
||||
createStudentRequestHandler,
|
||||
getStudentRequestsHandler,
|
||||
deleteClassJoinRequestHandler,
|
||||
getStudentRequestHandler,
|
||||
} from '../../src/controllers/students.js';
|
||||
import { TEST_STUDENTS } from '../test_assets/users/students.testdata.js';
|
||||
import { NotFoundException } from '../../src/exceptions/not-found-exception.js';
|
||||
import { BadRequestException } from '../../src/exceptions/bad-request-exception.js';
|
||||
import { ConflictException } from '../../src/exceptions/conflict-exception.js';
|
||||
import { EntityAlreadyExistsException } from '../../src/exceptions/entity-already-exists-exception.js';
|
||||
import { StudentDTO } from '@dwengo-1/common/interfaces/student';
|
||||
|
||||
describe('Student controllers', () => {
|
||||
let req: Partial<Request>;
|
||||
let res: Partial<Response>;
|
||||
|
||||
let jsonMock: Mock;
|
||||
|
||||
beforeAll(async () => {
|
||||
await setupTestApp();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jsonMock = vi.fn();
|
||||
res = {
|
||||
json: jsonMock,
|
||||
};
|
||||
});
|
||||
|
||||
it('Get student', async () => {
|
||||
req = { params: { username: 'DireStraits' } };
|
||||
|
||||
await getStudentHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ student: expect.anything() }));
|
||||
});
|
||||
|
||||
it('Student not found', async () => {
|
||||
req = { params: { username: 'doesnotexist' } };
|
||||
|
||||
await expect(async () => getStudentHandler(req as Request, res as Response)).rejects.toThrow(NotFoundException);
|
||||
});
|
||||
|
||||
it('No username', async () => {
|
||||
req = { params: {} };
|
||||
|
||||
await expect(async () => getStudentHandler(req as Request, res as Response)).rejects.toThrowError(BadRequestException);
|
||||
});
|
||||
|
||||
it('Create and delete student', async () => {
|
||||
const student = {
|
||||
id: 'coolstudent',
|
||||
username: 'coolstudent',
|
||||
firstName: 'New',
|
||||
lastName: 'Student',
|
||||
} as StudentDTO;
|
||||
req = {
|
||||
body: student,
|
||||
};
|
||||
|
||||
await createStudentHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ student: expect.objectContaining(student) }));
|
||||
|
||||
req = { params: { username: 'coolstudent' } };
|
||||
|
||||
await deleteStudentHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ student: expect.objectContaining(student) }));
|
||||
});
|
||||
|
||||
it('Create duplicate student', async () => {
|
||||
req = {
|
||||
body: {
|
||||
username: 'DireStraits',
|
||||
firstName: 'dupe',
|
||||
lastName: 'dupe',
|
||||
},
|
||||
};
|
||||
|
||||
await expect(async () => createStudentHandler(req as Request, res as Response)).rejects.toThrowError(EntityAlreadyExistsException);
|
||||
});
|
||||
|
||||
it('Create student no body', async () => {
|
||||
req = { body: {} };
|
||||
|
||||
await expect(async () => createStudentHandler(req as Request, res as Response)).rejects.toThrowError(BadRequestException);
|
||||
});
|
||||
|
||||
it('Student list', async () => {
|
||||
req = { query: { full: 'true' } };
|
||||
|
||||
await getAllStudentsHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ students: expect.anything() }));
|
||||
|
||||
const result = jsonMock.mock.lastCall?.[0];
|
||||
|
||||
// Check is DireStraits is part of the student list
|
||||
const studentUsernames = result.students.map((s: StudentDTO) => s.username);
|
||||
expect(studentUsernames).toContain('DireStraits');
|
||||
|
||||
// Check length, +1 because of create
|
||||
expect(result.students).toHaveLength(TEST_STUDENTS.length);
|
||||
});
|
||||
|
||||
it('Student classes', async () => {
|
||||
req = { params: { username: 'DireStraits' }, query: {} };
|
||||
|
||||
await getStudentClassesHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ classes: expect.anything() }));
|
||||
|
||||
const result = jsonMock.mock.lastCall?.[0];
|
||||
expect(result.classes).to.have.length.greaterThan(0);
|
||||
});
|
||||
|
||||
it('Student groups', async () => {
|
||||
req = { params: { username: 'DireStraits' }, query: {} };
|
||||
|
||||
await getStudentGroupsHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ groups: expect.anything() }));
|
||||
|
||||
const result = jsonMock.mock.lastCall?.[0];
|
||||
expect(result.groups).to.have.length.greaterThan(0);
|
||||
});
|
||||
|
||||
it('Student submissions', async () => {
|
||||
req = { params: { username: 'DireStraits' }, query: { full: 'true' } };
|
||||
|
||||
await getStudentSubmissionsHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ submissions: expect.anything() }));
|
||||
|
||||
const result = jsonMock.mock.lastCall?.[0];
|
||||
expect(result.submissions).to.have.length.greaterThan(0);
|
||||
});
|
||||
|
||||
it('Student questions', async () => {
|
||||
req = { params: { username: 'DireStraits' }, query: { full: 'true' } };
|
||||
|
||||
await getStudentQuestionsHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ questions: expect.anything() }));
|
||||
|
||||
const result = jsonMock.mock.lastCall?.[0];
|
||||
expect(result.questions).to.have.length.greaterThan(0);
|
||||
});
|
||||
|
||||
it('Deleting non-existent student', async () => {
|
||||
req = { params: { username: 'doesnotexist' } };
|
||||
|
||||
await expect(async () => deleteStudentHandler(req as Request, res as Response)).rejects.toThrow(NotFoundException);
|
||||
});
|
||||
|
||||
it('Get join requests by student', async () => {
|
||||
req = {
|
||||
params: { username: 'PinkFloyd' },
|
||||
};
|
||||
|
||||
await getStudentRequestsHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
requests: expect.anything(),
|
||||
})
|
||||
);
|
||||
|
||||
const result = jsonMock.mock.lastCall?.[0];
|
||||
// Console.log('[JOIN REQUESTS]', result.requests);
|
||||
expect(result.requests.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('Get join request by student and class', async () => {
|
||||
req = {
|
||||
params: { username: 'PinkFloyd', classId: 'id02' },
|
||||
};
|
||||
|
||||
await getStudentRequestHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
request: expect.anything(),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('Create join request', async () => {
|
||||
req = {
|
||||
params: { username: 'Noordkaap' },
|
||||
body: { classId: 'id02' },
|
||||
};
|
||||
|
||||
await createStudentRequestHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ request: expect.anything() }));
|
||||
});
|
||||
|
||||
it('Create join request duplicate', async () => {
|
||||
req = {
|
||||
params: { username: 'Tool' },
|
||||
body: { classId: 'id02' },
|
||||
};
|
||||
|
||||
await expect(async () => createStudentRequestHandler(req as Request, res as Response)).rejects.toThrow(ConflictException);
|
||||
});
|
||||
|
||||
it('Delete join request', async () => {
|
||||
req = {
|
||||
params: { username: 'Noordkaap', classId: 'id02' },
|
||||
};
|
||||
|
||||
await deleteClassJoinRequestHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ request: expect.anything() }));
|
||||
|
||||
await expect(async () => deleteClassJoinRequestHandler(req as Request, res as Response)).rejects.toThrow(NotFoundException);
|
||||
});
|
||||
});
|
204
backend/tests/controllers/teachers.test.ts
Normal file
204
backend/tests/controllers/teachers.test.ts
Normal file
|
@ -0,0 +1,204 @@
|
|||
import { beforeAll, beforeEach, describe, expect, it, Mock, vi } from 'vitest';
|
||||
import { Request, Response } from 'express';
|
||||
import { setupTestApp } from '../setup-tests.js';
|
||||
import { NotFoundException } from '../../src/exceptions/not-found-exception.js';
|
||||
import {
|
||||
createTeacherHandler,
|
||||
deleteTeacherHandler,
|
||||
getAllTeachersHandler,
|
||||
getStudentJoinRequestHandler,
|
||||
getTeacherClassHandler,
|
||||
getTeacherHandler,
|
||||
getTeacherStudentHandler,
|
||||
updateStudentJoinRequestHandler,
|
||||
} from '../../src/controllers/teachers.js';
|
||||
import { BadRequestException } from '../../src/exceptions/bad-request-exception.js';
|
||||
import { EntityAlreadyExistsException } from '../../src/exceptions/entity-already-exists-exception.js';
|
||||
import { getStudentRequestsHandler } from '../../src/controllers/students.js';
|
||||
import { TeacherDTO } from '@dwengo-1/common/interfaces/teacher';
|
||||
|
||||
describe('Teacher controllers', () => {
|
||||
let req: Partial<Request>;
|
||||
let res: Partial<Response>;
|
||||
|
||||
let jsonMock: Mock;
|
||||
|
||||
beforeAll(async () => {
|
||||
await setupTestApp();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jsonMock = vi.fn();
|
||||
res = {
|
||||
json: jsonMock,
|
||||
};
|
||||
});
|
||||
|
||||
it('Get teacher', async () => {
|
||||
req = { params: { username: 'FooFighters' } };
|
||||
|
||||
await getTeacherHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ teacher: expect.anything() }));
|
||||
});
|
||||
|
||||
it('Teacher not found', async () => {
|
||||
req = { params: { username: 'doesnotexist' } };
|
||||
|
||||
await expect(async () => getTeacherHandler(req as Request, res as Response)).rejects.toThrow(NotFoundException);
|
||||
});
|
||||
|
||||
it('No username', async () => {
|
||||
req = { params: {} };
|
||||
|
||||
await expect(async () => getTeacherHandler(req as Request, res as Response)).rejects.toThrowError(BadRequestException);
|
||||
});
|
||||
|
||||
it('Create and delete teacher', async () => {
|
||||
const teacher = {
|
||||
id: 'coolteacher',
|
||||
username: 'coolteacher',
|
||||
firstName: 'New',
|
||||
lastName: 'Teacher',
|
||||
};
|
||||
req = {
|
||||
body: teacher,
|
||||
};
|
||||
|
||||
await createTeacherHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ teacher: expect.objectContaining(teacher) }));
|
||||
|
||||
req = { params: { username: 'coolteacher' } };
|
||||
|
||||
await deleteTeacherHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ teacher: expect.objectContaining(teacher) }));
|
||||
});
|
||||
|
||||
it('Create duplicate student', async () => {
|
||||
req = {
|
||||
body: {
|
||||
username: 'FooFighters',
|
||||
firstName: 'Dave',
|
||||
lastName: 'Grohl',
|
||||
},
|
||||
};
|
||||
|
||||
await expect(async () => createTeacherHandler(req as Request, res as Response)).rejects.toThrowError(EntityAlreadyExistsException);
|
||||
});
|
||||
|
||||
it('Create teacher no body', async () => {
|
||||
req = { body: {} };
|
||||
|
||||
await expect(async () => createTeacherHandler(req as Request, res as Response)).rejects.toThrowError(BadRequestException);
|
||||
});
|
||||
|
||||
it('Teacher list', async () => {
|
||||
req = { query: { full: 'true' } };
|
||||
|
||||
await getAllTeachersHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ teachers: expect.anything() }));
|
||||
|
||||
const result = jsonMock.mock.lastCall?.[0];
|
||||
|
||||
const teacherUsernames = result.teachers.map((s: TeacherDTO) => s.username);
|
||||
expect(teacherUsernames).toContain('FooFighters');
|
||||
|
||||
expect(result.teachers).toHaveLength(4);
|
||||
});
|
||||
|
||||
it('Deleting non-existent student', async () => {
|
||||
req = { params: { username: 'doesnotexist' } };
|
||||
|
||||
await expect(async () => deleteTeacherHandler(req as Request, res as Response)).rejects.toThrow(NotFoundException);
|
||||
});
|
||||
|
||||
it('Get teacher classes', async () => {
|
||||
req = {
|
||||
params: { username: 'FooFighters' },
|
||||
query: { full: 'true' },
|
||||
};
|
||||
|
||||
await getTeacherClassHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ classes: expect.anything() }));
|
||||
|
||||
const result = jsonMock.mock.lastCall?.[0];
|
||||
// Console.log('[TEACHER CLASSES]', result);
|
||||
expect(result.classes.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('Get teacher students', async () => {
|
||||
req = {
|
||||
params: { username: 'FooFighters' },
|
||||
query: { full: 'true' },
|
||||
};
|
||||
|
||||
await getTeacherStudentHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ students: expect.anything() }));
|
||||
|
||||
const result = jsonMock.mock.lastCall?.[0];
|
||||
// Console.log('[TEACHER STUDENTS]', result.students);
|
||||
expect(result.students.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
It('Get teacher questions', async () => {
|
||||
req = {
|
||||
params: { username: 'FooFighters' },
|
||||
query: { full: 'true' },
|
||||
};
|
||||
|
||||
await getTeacherQuestionHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ questions: expect.anything() }));
|
||||
|
||||
const result = jsonMock.mock.lastCall?.[0];
|
||||
// console.log('[TEACHER QUESTIONS]', result.questions);
|
||||
expect(result.questions.length).toBeGreaterThan(0);
|
||||
|
||||
// TODO fix
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
it('Get join requests by class', async () => {
|
||||
req = {
|
||||
query: { username: 'LimpBizkit' },
|
||||
params: { classId: 'id02' },
|
||||
};
|
||||
|
||||
await getStudentJoinRequestHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ joinRequests: expect.anything() }));
|
||||
|
||||
const result = jsonMock.mock.lastCall?.[0];
|
||||
// Console.log('[JOIN REQUESTS FOR CLASS]', result.joinRequests);
|
||||
expect(result.joinRequests.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('Update join request status', async () => {
|
||||
req = {
|
||||
query: { username: 'LimpBizkit', studentUsername: 'PinkFloyd' },
|
||||
params: { classId: 'id02' },
|
||||
body: { accepted: 'true' },
|
||||
};
|
||||
|
||||
await updateStudentJoinRequestHandler(req as Request, res as Response);
|
||||
|
||||
expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ request: expect.anything() }));
|
||||
|
||||
req = {
|
||||
params: { username: 'PinkFloyd' },
|
||||
};
|
||||
|
||||
await getStudentRequestsHandler(req as Request, res as Response);
|
||||
|
||||
const status: boolean = jsonMock.mock.lastCall?.[0].requests[0].status;
|
||||
expect(status).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -1,49 +1,19 @@
|
|||
import { EntityManager } from '@mikro-orm/core';
|
||||
import { Student } from '../../../src/entities/users/student.entity';
|
||||
|
||||
// 🔓 Ruwe testdata array — herbruikbaar in assertions
|
||||
export const TEST_STUDENTS = [
|
||||
{ username: 'Noordkaap', firstName: 'Stijn', lastName: 'Meuris' },
|
||||
{ username: 'DireStraits', firstName: 'Mark', lastName: 'Knopfler' },
|
||||
{ username: 'Tool', firstName: 'Maynard', lastName: 'Keenan' },
|
||||
{ username: 'SmashingPumpkins', firstName: 'Billy', lastName: 'Corgan' },
|
||||
{ username: 'PinkFloyd', firstName: 'David', lastName: 'Gilmoure' },
|
||||
{ username: 'TheDoors', firstName: 'Jim', lastName: 'Morisson' },
|
||||
// ⚠️ Deze mag niet gebruikt worden in elke test!
|
||||
{ username: 'Nirvana', firstName: 'Kurt', lastName: 'Cobain' },
|
||||
];
|
||||
|
||||
// 🏗️ Functie die ORM entities maakt uit de data array
|
||||
export function makeTestStudents(em: EntityManager): Student[] {
|
||||
const student01 = em.create(Student, {
|
||||
username: 'Noordkaap',
|
||||
firstName: 'Stijn',
|
||||
lastName: 'Meuris',
|
||||
});
|
||||
|
||||
const student02 = em.create(Student, {
|
||||
username: 'DireStraits',
|
||||
firstName: 'Mark',
|
||||
lastName: 'Knopfler',
|
||||
});
|
||||
|
||||
const student03 = em.create(Student, {
|
||||
username: 'Tool',
|
||||
firstName: 'Maynard',
|
||||
lastName: 'Keenan',
|
||||
});
|
||||
|
||||
const student04 = em.create(Student, {
|
||||
username: 'SmashingPumpkins',
|
||||
firstName: 'Billy',
|
||||
lastName: 'Corgan',
|
||||
});
|
||||
|
||||
const student05 = em.create(Student, {
|
||||
username: 'PinkFloyd',
|
||||
firstName: 'David',
|
||||
lastName: 'Gilmoure',
|
||||
});
|
||||
|
||||
const student06 = em.create(Student, {
|
||||
username: 'TheDoors',
|
||||
firstName: 'Jim',
|
||||
lastName: 'Morisson',
|
||||
});
|
||||
|
||||
// Do not use for any tests, gets deleted in a unit test
|
||||
const student07 = em.create(Student, {
|
||||
username: 'Nirvana',
|
||||
firstName: 'Kurt',
|
||||
lastName: 'Cobain',
|
||||
});
|
||||
|
||||
return [student01, student02, student03, student04, student05, student06, student07];
|
||||
return TEST_STUDENTS.map((data) => em.create(Student, data));
|
||||
}
|
||||
|
|
8
common/src/interfaces/class-join-request.ts
Normal file
8
common/src/interfaces/class-join-request.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { StudentDTO } from './student';
|
||||
import { ClassJoinRequestStatus } from '../util/class-join-request';
|
||||
|
||||
export interface ClassJoinRequestDTO {
|
||||
requester: StudentDTO;
|
||||
class: string;
|
||||
status: ClassJoinRequestStatus;
|
||||
}
|
4
common/src/interfaces/theme.ts
Normal file
4
common/src/interfaces/theme.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export interface Theme {
|
||||
title: string;
|
||||
hruids: string[];
|
||||
}
|
5
frontend/src/controllers/assignments.ts
Normal file
5
frontend/src/controllers/assignments.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import type { AssignmentDTO } from "@dwengo-1/interfaces/assignment";
|
||||
|
||||
export interface AssignmentsResponse {
|
||||
assignments: AssignmentDTO[];
|
||||
} // TODO ID
|
5
frontend/src/controllers/classes.ts
Normal file
5
frontend/src/controllers/classes.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import type { ClassDTO } from "@dwengo-1/interfaces/class";
|
||||
|
||||
export interface ClassesResponse {
|
||||
classes: ClassDTO[] | string[];
|
||||
}
|
5
frontend/src/controllers/groups.ts
Normal file
5
frontend/src/controllers/groups.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import type { GroupDTO } from "@dwengo-1/interfaces/group";
|
||||
|
||||
export interface GroupsResponse {
|
||||
groups: GroupDTO[];
|
||||
} // | TODO id
|
5
frontend/src/controllers/questions.ts
Normal file
5
frontend/src/controllers/questions.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import type { QuestionDTO, QuestionId } from "@dwengo-1/interfaces/question";
|
||||
|
||||
export interface QuestionsResponse {
|
||||
questions: QuestionDTO[] | QuestionId[];
|
||||
}
|
79
frontend/src/controllers/students.ts
Normal file
79
frontend/src/controllers/students.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { BaseController } from "@/controllers/base-controller.ts";
|
||||
import type { ClassesResponse } from "@/controllers/classes.ts";
|
||||
import type { AssignmentsResponse } from "@/controllers/assignments.ts";
|
||||
import type { GroupsResponse } from "@/controllers/groups.ts";
|
||||
import type { SubmissionsResponse } from "@/controllers/submissions.ts";
|
||||
import type { QuestionsResponse } from "@/controllers/questions.ts";
|
||||
import type { StudentDTO } from "@dwengo-1/interfaces/student";
|
||||
import type { ClassJoinRequestDTO } from "@dwengo-1/interfaces/class-join-request";
|
||||
|
||||
export interface StudentsResponse {
|
||||
students: StudentDTO[] | string[];
|
||||
}
|
||||
export interface StudentResponse {
|
||||
student: StudentDTO;
|
||||
}
|
||||
export interface JoinRequestsResponse {
|
||||
requests: ClassJoinRequestDTO[];
|
||||
}
|
||||
export interface JoinRequestResponse {
|
||||
request: ClassJoinRequestDTO;
|
||||
}
|
||||
|
||||
export class StudentController extends BaseController {
|
||||
constructor() {
|
||||
super("student");
|
||||
}
|
||||
|
||||
async getAll(full = true): Promise<StudentsResponse> {
|
||||
return this.get<StudentsResponse>("/", { full });
|
||||
}
|
||||
|
||||
async getByUsername(username: string): Promise<StudentResponse> {
|
||||
return this.get<StudentResponse>(`/${username}`);
|
||||
}
|
||||
|
||||
async createStudent(data: StudentDTO): Promise<StudentResponse> {
|
||||
return this.post<StudentResponse>("/", data);
|
||||
}
|
||||
|
||||
async deleteStudent(username: string): Promise<StudentResponse> {
|
||||
return this.delete<StudentResponse>(`/${username}`);
|
||||
}
|
||||
|
||||
async getClasses(username: string, full = true): Promise<ClassesResponse> {
|
||||
return this.get<ClassesResponse>(`/${username}/classes`, { full });
|
||||
}
|
||||
|
||||
async getAssignments(username: string, full = true): Promise<AssignmentsResponse> {
|
||||
return this.get<AssignmentsResponse>(`/${username}/assignments`, { full });
|
||||
}
|
||||
|
||||
async getGroups(username: string, full = true): Promise<GroupsResponse> {
|
||||
return this.get<GroupsResponse>(`/${username}/groups`, { full });
|
||||
}
|
||||
|
||||
async getSubmissions(username: string): Promise<SubmissionsResponse> {
|
||||
return this.get<SubmissionsResponse>(`/${username}/submissions`);
|
||||
}
|
||||
|
||||
async getQuestions(username: string, full = true): Promise<QuestionsResponse> {
|
||||
return this.get<QuestionsResponse>(`/${username}/questions`, { full });
|
||||
}
|
||||
|
||||
async getJoinRequests(username: string): Promise<JoinRequestsResponse> {
|
||||
return this.get<JoinRequestsResponse>(`/${username}/joinRequests`);
|
||||
}
|
||||
|
||||
async getJoinRequest(username: string, classId: string): Promise<JoinRequestResponse> {
|
||||
return this.get<JoinRequestResponse>(`/${username}/joinRequests/${classId}`);
|
||||
}
|
||||
|
||||
async createJoinRequest(username: string, classId: string): Promise<JoinRequestResponse> {
|
||||
return this.post<JoinRequestResponse>(`/${username}/joinRequests}`, classId);
|
||||
}
|
||||
|
||||
async deleteJoinRequest(username: string, classId: string): Promise<JoinRequestResponse> {
|
||||
return this.delete<JoinRequestResponse>(`/${username}/joinRequests/${classId}`);
|
||||
}
|
||||
}
|
5
frontend/src/controllers/submissions.ts
Normal file
5
frontend/src/controllers/submissions.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { type SubmissionDTO, SubmissionDTOId } from "@dwengo-1/interfaces/submission";
|
||||
|
||||
export interface SubmissionsResponse {
|
||||
submissions: SubmissionDTO[] | SubmissionDTOId[];
|
||||
}
|
64
frontend/src/controllers/teachers.ts
Normal file
64
frontend/src/controllers/teachers.ts
Normal file
|
@ -0,0 +1,64 @@
|
|||
import { BaseController } from "@/controllers/base-controller.ts";
|
||||
import type { JoinRequestResponse, JoinRequestsResponse, StudentsResponse } from "@/controllers/students.ts";
|
||||
import type { QuestionsResponse } from "@/controllers/questions.ts";
|
||||
import type { ClassesResponse } from "@/controllers/classes.ts";
|
||||
import type { TeacherDTO } from "@dwengo-1/interfaces/teacher";
|
||||
|
||||
export interface TeachersResponse {
|
||||
teachers: TeacherDTO[] | string[];
|
||||
}
|
||||
export interface TeacherResponse {
|
||||
teacher: TeacherDTO;
|
||||
}
|
||||
|
||||
export class TeacherController extends BaseController {
|
||||
constructor() {
|
||||
super("teacher");
|
||||
}
|
||||
|
||||
async getAll(full = false): Promise<TeachersResponse> {
|
||||
return this.get<TeachersResponse>("/", { full });
|
||||
}
|
||||
|
||||
async getByUsername(username: string): Promise<TeacherResponse> {
|
||||
return this.get<TeacherResponse>(`/${username}`);
|
||||
}
|
||||
|
||||
async createTeacher(data: TeacherDTO): Promise<TeacherResponse> {
|
||||
return this.post<TeacherResponse>("/", data);
|
||||
}
|
||||
|
||||
async deleteTeacher(username: string): Promise<TeacherResponse> {
|
||||
return this.delete<TeacherResponse>(`/${username}`);
|
||||
}
|
||||
|
||||
async getClasses(username: string, full = false): Promise<ClassesResponse> {
|
||||
return this.get<ClassesResponse>(`/${username}/classes`, { full });
|
||||
}
|
||||
|
||||
async getStudents(username: string, full = false): Promise<StudentsResponse> {
|
||||
return this.get<StudentsResponse>(`/${username}/students`, { full });
|
||||
}
|
||||
|
||||
async getQuestions(username: string, full = false): Promise<QuestionsResponse> {
|
||||
return this.get<QuestionsResponse>(`/${username}/questions`, { full });
|
||||
}
|
||||
|
||||
async getStudentJoinRequests(username: string, classId: string): Promise<JoinRequestsResponse> {
|
||||
return this.get<JoinRequestsResponse>(`/${username}/joinRequests/${classId}`);
|
||||
}
|
||||
|
||||
async updateStudentJoinRequest(
|
||||
teacherUsername: string,
|
||||
classId: string,
|
||||
studentUsername: string,
|
||||
accepted: boolean,
|
||||
): Promise<JoinRequestResponse> {
|
||||
return this.put<JoinRequestResponse>(
|
||||
`/${teacherUsername}/joinRequests/${classId}/${studentUsername}`,
|
||||
accepted,
|
||||
);
|
||||
}
|
||||
|
||||
// GetInvitations(id: string) {return this.get<{ invitations: string[] }>(`/${id}/invitations`);}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
import { BaseController } from "@/controllers/base-controller.ts";
|
||||
import type { Theme } from "@dwengo-1/interfaces/theme";
|
||||
|
||||
export class ThemeController extends BaseController {
|
||||
constructor() {
|
||||
super("theme");
|
||||
}
|
||||
|
||||
async getAll(language: string | null = null): Promise<unknown> {
|
||||
async getAll(language: string | null = null): Promise<Theme[]> {
|
||||
const query = language ? { language } : undefined;
|
||||
return this.get("/", query);
|
||||
}
|
||||
|
|
205
frontend/src/queries/students.ts
Normal file
205
frontend/src/queries/students.ts
Normal file
|
@ -0,0 +1,205 @@
|
|||
import { computed, toValue } from "vue";
|
||||
import type { MaybeRefOrGetter } from "vue";
|
||||
import {
|
||||
useMutation,
|
||||
type UseMutationReturnType,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
type UseQueryReturnType,
|
||||
} from "@tanstack/vue-query";
|
||||
import {
|
||||
type JoinRequestResponse,
|
||||
type JoinRequestsResponse,
|
||||
StudentController,
|
||||
type StudentResponse,
|
||||
type StudentsResponse,
|
||||
} from "@/controllers/students.ts";
|
||||
import type { ClassesResponse } from "@/controllers/classes.ts";
|
||||
import type { AssignmentsResponse } from "@/controllers/assignments.ts";
|
||||
import type { GroupsResponse } from "@/controllers/groups.ts";
|
||||
import type { SubmissionsResponse } from "@/controllers/submissions.ts";
|
||||
import type { QuestionsResponse } from "@/controllers/questions.ts";
|
||||
import type { StudentDTO } from "@dwengo-1/interfaces/student";
|
||||
|
||||
const studentController = new StudentController();
|
||||
|
||||
/** 🔑 Query keys */
|
||||
function studentsQueryKey(full: boolean): [string, boolean] {
|
||||
return ["students", full];
|
||||
}
|
||||
function studentQueryKey(username: string): [string, string] {
|
||||
return ["student", username];
|
||||
}
|
||||
function studentClassesQueryKey(username: string, full: boolean): [string, string, boolean] {
|
||||
return ["student-classes", username, full];
|
||||
}
|
||||
function studentAssignmentsQueryKey(username: string, full: boolean): [string, string, boolean] {
|
||||
return ["student-assignments", username, full];
|
||||
}
|
||||
function studentGroupsQueryKeys(username: string, full: boolean): [string, string, boolean] {
|
||||
return ["student-groups", username, full];
|
||||
}
|
||||
function studentSubmissionsQueryKey(username: string): [string, string] {
|
||||
return ["student-submissions", username];
|
||||
}
|
||||
function studentQuestionsQueryKey(username: string, full: boolean): [string, string, boolean] {
|
||||
return ["student-questions", username, full];
|
||||
}
|
||||
export function studentJoinRequestsQueryKey(username: string): [string, string] {
|
||||
return ["student-join-requests", username];
|
||||
}
|
||||
export function studentJoinRequestQueryKey(username: string, classId: string): [string, string, string] {
|
||||
return ["student-join-request", username, classId];
|
||||
}
|
||||
|
||||
export function useStudentsQuery(full: MaybeRefOrGetter<boolean> = true): UseQueryReturnType<StudentsResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => studentsQueryKey(toValue(full))),
|
||||
queryFn: async () => studentController.getAll(toValue(full)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
): UseQueryReturnType<StudentResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => studentQueryKey(toValue(username)!)),
|
||||
queryFn: async () => studentController.getByUsername(toValue(username)!),
|
||||
enabled: () => Boolean(toValue(username)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentClassesQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
full: MaybeRefOrGetter<boolean> = true,
|
||||
): UseQueryReturnType<ClassesResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => studentClassesQueryKey(toValue(username)!, toValue(full))),
|
||||
queryFn: async () => studentController.getClasses(toValue(username)!, toValue(full)),
|
||||
enabled: () => Boolean(toValue(username)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentAssignmentsQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
full: MaybeRefOrGetter<boolean> = true,
|
||||
): UseQueryReturnType<AssignmentsResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => studentAssignmentsQueryKey(toValue(username)!, toValue(full))),
|
||||
queryFn: async () => studentController.getAssignments(toValue(username)!, toValue(full)),
|
||||
enabled: () => Boolean(toValue(username)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentGroupsQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
full: MaybeRefOrGetter<boolean> = true,
|
||||
): UseQueryReturnType<GroupsResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => studentGroupsQueryKeys(toValue(username)!, toValue(full))),
|
||||
queryFn: async () => studentController.getGroups(toValue(username)!, toValue(full)),
|
||||
enabled: () => Boolean(toValue(username)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentSubmissionsQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
): UseQueryReturnType<SubmissionsResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => studentSubmissionsQueryKey(toValue(username)!)),
|
||||
queryFn: async () => studentController.getSubmissions(toValue(username)!),
|
||||
enabled: () => Boolean(toValue(username)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentQuestionsQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
full: MaybeRefOrGetter<boolean> = true,
|
||||
): UseQueryReturnType<QuestionsResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => studentQuestionsQueryKey(toValue(username)!, toValue(full))),
|
||||
queryFn: async () => studentController.getQuestions(toValue(username)!, toValue(full)),
|
||||
enabled: () => Boolean(toValue(username)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentJoinRequestsQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
): UseQueryReturnType<JoinRequestsResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => studentJoinRequestsQueryKey(toValue(username)!)),
|
||||
queryFn: async () => studentController.getJoinRequests(toValue(username)!),
|
||||
enabled: () => Boolean(toValue(username)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useStudentJoinRequestQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
classId: MaybeRefOrGetter<string | undefined>,
|
||||
): UseQueryReturnType<JoinRequestResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => studentJoinRequestQueryKey(toValue(username)!, toValue(classId)!)),
|
||||
queryFn: async () => studentController.getJoinRequest(toValue(username)!, toValue(classId)!),
|
||||
enabled: () => Boolean(toValue(username)) && Boolean(toValue(classId)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateStudentMutation(): UseMutationReturnType<StudentResponse, Error, StudentDTO, unknown> {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (data) => studentController.createStudent(data),
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries({ queryKey: ["students"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteStudentMutation(): UseMutationReturnType<StudentResponse, Error, string, unknown> {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (username) => studentController.deleteStudent(username),
|
||||
onSuccess: async (deletedUser) => {
|
||||
await queryClient.invalidateQueries({ queryKey: ["students"] });
|
||||
await queryClient.invalidateQueries({ queryKey: studentQueryKey(deletedUser.student.username) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateJoinRequestMutation(): UseMutationReturnType<
|
||||
JoinRequestResponse,
|
||||
Error,
|
||||
{ username: string; classId: string },
|
||||
unknown
|
||||
> {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ username, classId }) => studentController.createJoinRequest(username, classId),
|
||||
onSuccess: async (newJoinRequest) => {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: studentJoinRequestsQueryKey(newJoinRequest.request.requester),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteJoinRequestMutation(): UseMutationReturnType<
|
||||
JoinRequestResponse,
|
||||
Error,
|
||||
{ username: string; classId: string },
|
||||
unknown
|
||||
> {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ username, classId }) => studentController.deleteJoinRequest(username, classId),
|
||||
onSuccess: async (deletedJoinRequest) => {
|
||||
const username = deletedJoinRequest.request.requester;
|
||||
const classId = deletedJoinRequest.request.class;
|
||||
await queryClient.invalidateQueries({ queryKey: studentJoinRequestsQueryKey(username) });
|
||||
await queryClient.invalidateQueries({ queryKey: studentJoinRequestQueryKey(username, classId) });
|
||||
},
|
||||
});
|
||||
}
|
136
frontend/src/queries/teachers.ts
Normal file
136
frontend/src/queries/teachers.ts
Normal file
|
@ -0,0 +1,136 @@
|
|||
import { computed, toValue } from "vue";
|
||||
import type { MaybeRefOrGetter } from "vue";
|
||||
import { useMutation, useQuery, useQueryClient, UseMutationReturnType, UseQueryReturnType } from "@tanstack/vue-query";
|
||||
import { TeacherController, type TeacherResponse, type TeachersResponse } from "@/controllers/teachers.ts";
|
||||
import type { ClassesResponse } from "@/controllers/classes.ts";
|
||||
import type { JoinRequestResponse, JoinRequestsResponse, StudentsResponse } from "@/controllers/students.ts";
|
||||
import type { QuestionsResponse } from "@/controllers/questions.ts";
|
||||
import type { TeacherDTO } from "@dwengo-1/interfaces/teacher";
|
||||
import { studentJoinRequestQueryKey, studentJoinRequestsQueryKey } from "@/queries/students.ts";
|
||||
|
||||
const teacherController = new TeacherController();
|
||||
|
||||
/** 🔑 Query keys */
|
||||
function teachersQueryKey(full: boolean): [string, boolean] {
|
||||
return ["teachers", full];
|
||||
}
|
||||
|
||||
function teacherQueryKey(username: string): [string, string] {
|
||||
return ["teacher", username];
|
||||
}
|
||||
|
||||
function teacherClassesQueryKey(username: string, full: boolean): [string, string, boolean] {
|
||||
return ["teacher-classes", username, full];
|
||||
}
|
||||
|
||||
function teacherStudentsQueryKey(username: string, full: boolean): [string, string, boolean] {
|
||||
return ["teacher-students", username, full];
|
||||
}
|
||||
|
||||
function teacherQuestionsQueryKey(username: string, full: boolean): [string, string, boolean] {
|
||||
return ["teacher-questions", username, full];
|
||||
}
|
||||
|
||||
export function useTeachersQuery(full: MaybeRefOrGetter<boolean> = false): UseQueryReturnType<TeachersResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => teachersQueryKey(toValue(full))),
|
||||
queryFn: async () => teacherController.getAll(toValue(full)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTeacherQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
): UseQueryReturnType<TeacherResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => teacherQueryKey(toValue(username)!)),
|
||||
queryFn: async () => teacherController.getByUsername(toValue(username)!),
|
||||
enabled: () => Boolean(toValue(username)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTeacherClassesQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
full: MaybeRefOrGetter<boolean> = false,
|
||||
): UseQueryReturnType<ClassesResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => teacherClassesQueryKey(toValue(username)!, toValue(full))),
|
||||
queryFn: async () => teacherController.getClasses(toValue(username)!, toValue(full)),
|
||||
enabled: () => Boolean(toValue(username)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTeacherStudentsQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
full: MaybeRefOrGetter<boolean> = false,
|
||||
): UseQueryReturnType<StudentsResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => teacherStudentsQueryKey(toValue(username)!, toValue(full))),
|
||||
queryFn: async () => teacherController.getStudents(toValue(username)!, toValue(full)),
|
||||
enabled: () => Boolean(toValue(username)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTeacherQuestionsQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
full: MaybeRefOrGetter<boolean> = false,
|
||||
): UseQueryReturnType<QuestionsResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => teacherQuestionsQueryKey(toValue(username)!, toValue(full))),
|
||||
queryFn: async () => teacherController.getQuestions(toValue(username)!, toValue(full)),
|
||||
enabled: () => Boolean(toValue(username)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTeacherJoinRequestsQuery(
|
||||
username: MaybeRefOrGetter<string | undefined>,
|
||||
classId: MaybeRefOrGetter<string | undefined>,
|
||||
): UseQueryReturnType<JoinRequestsResponse, Error> {
|
||||
return useQuery({
|
||||
queryKey: computed(() => JOIN_REQUESTS_QUERY_KEY(toValue(username)!, toValue(classId)!)),
|
||||
queryFn: async () => teacherController.getStudentJoinRequests(toValue(username)!, toValue(classId)!),
|
||||
enabled: () => Boolean(toValue(username)) && Boolean(toValue(classId)),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateTeacherMutation(): UseMutationReturnType<TeacherResponse, Error, TeacherDTO, unknown> {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (data: TeacherDTO) => teacherController.createTeacher(data),
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries({ queryKey: ["teachers"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteTeacherMutation(): UseMutationReturnType<TeacherResponse, Error, string, unknown> {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (username: string) => teacherController.deleteTeacher(username),
|
||||
onSuccess: async (deletedTeacher) => {
|
||||
await queryClient.invalidateQueries({ queryKey: ["teachers"] });
|
||||
await queryClient.invalidateQueries({ queryKey: teacherQueryKey(deletedTeacher.teacher.username) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateJoinRequestMutation(): UseMutationReturnType<
|
||||
JoinRequestResponse,
|
||||
Error,
|
||||
{ teacherUsername: string; classId: string; studentUsername: string; accepted: boolean },
|
||||
unknown
|
||||
> {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ teacherUsername, classId, studentUsername, accepted }) =>
|
||||
teacherController.updateStudentJoinRequest(teacherUsername, classId, studentUsername, accepted),
|
||||
onSuccess: async (deletedJoinRequest) => {
|
||||
const username = deletedJoinRequest.request.requester;
|
||||
const classId = deletedJoinRequest.request.class;
|
||||
await queryClient.invalidateQueries({ queryKey: studentJoinRequestsQueryKey(username) });
|
||||
await queryClient.invalidateQueries({ queryKey: studentJoinRequestQueryKey(username, classId) });
|
||||
},
|
||||
});
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query";
|
||||
import { getThemeController } from "@/controllers/controllers";
|
||||
import { type MaybeRefOrGetter, toValue } from "vue";
|
||||
import type { Theme } from "@/data-objects/theme.ts";
|
||||
import type { Theme } from "@dwengo-1/interfaces/theme";
|
||||
import { getThemeController } from "@/controllers/controllers.ts";
|
||||
|
||||
const themeController = getThemeController();
|
||||
|
||||
export function useThemeQuery(language: MaybeRefOrGetter<string>): UseQueryReturnType<Theme[], Error> {
|
||||
export function useThemeQuery(language: MaybeRefOrGetter<string | undefined>): UseQueryReturnType<Theme[], Error> {
|
||||
return useQuery({
|
||||
queryKey: ["themes", language],
|
||||
queryFn: async () => {
|
||||
|
@ -16,10 +16,12 @@ export function useThemeQuery(language: MaybeRefOrGetter<string>): UseQueryRetur
|
|||
});
|
||||
}
|
||||
|
||||
export function useThemeHruidsQuery(themeKey: string | null): UseQueryReturnType<unknown, Error> {
|
||||
export function useThemeHruidsQuery(
|
||||
themeKey: MaybeRefOrGetter<string | undefined>,
|
||||
): UseQueryReturnType<string[], Error> {
|
||||
return useQuery({
|
||||
queryKey: ["theme-hruids", themeKey],
|
||||
queryFn: async () => themeController.getHruidsByKey(themeKey!),
|
||||
queryFn: async () => themeController.getHruidsByKey(toValue(themeKey)!),
|
||||
enabled: Boolean(themeKey),
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue