From 8f9f21eb4cc2af9df8adc8993b4bbda396524892 Mon Sep 17 00:00:00 2001 From: ShenyiCui Date: Sun, 9 Jun 2024 18:09:05 +0800 Subject: [PATCH 1/7] feat: add excuses model + migration --- .../20240609100553_add_excuses/migration.sql | 17 +++++++++++++++++ prisma/schema.prisma | 15 +++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 prisma/migrations/20240609100553_add_excuses/migration.sql diff --git a/prisma/migrations/20240609100553_add_excuses/migration.sql b/prisma/migrations/20240609100553_add_excuses/migration.sql new file mode 100644 index 0000000..ddb8cd8 --- /dev/null +++ b/prisma/migrations/20240609100553_add_excuses/migration.sql @@ -0,0 +1,17 @@ +-- CreateTable +CREATE TABLE "excuses" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + "student_id" INTEGER NOT NULL, + "window_id" INTEGER NOT NULL, + "reason" TEXT NOT NULL, + + CONSTRAINT "excuses_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "excuses" ADD CONSTRAINT "excuses_student_id_fkey" FOREIGN KEY ("student_id") REFERENCES "students"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "excuses" ADD CONSTRAINT "excuses_window_id_fkey" FOREIGN KEY ("window_id") REFERENCES "windows"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b643103..5b1f0d2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -143,6 +143,7 @@ model Student { results StudentResult[] exclusion Exclusion? pairingStudents PairingStudent[] + excuses Excuse[] @@unique([userId, cohortId]) @@map("students") @@ -161,10 +162,24 @@ model Window { studentResults StudentResult[] exclusions Exclusion[] pairings Pairing[] + Excuse Excuse[] @@map("windows") } +model Excuse { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + studentId Int @map("student_id") + windowId Int @map("window_id") + reason String + student Student @relation(fields: [studentId], references: [id], onDelete: Cascade) + window Window @relation(fields: [windowId], references: [id], onDelete: Cascade) + + @@map("excuses") +} + // Tracks the student's work done for each window model StudentResult { id Int @id @default(autoincrement()) From 7a1af2ede9bd1e8d4074c25cc0fd344003a15294 Mon Sep 17 00:00:00 2001 From: ShenyiCui Date: Sun, 9 Jun 2024 22:37:02 +0800 Subject: [PATCH 2/7] feat: add columns to excuse table --- .../migration.sql | 14 ++++++++ .../migration.sql | 8 +++++ prisma/schema.prisma | 34 ++++++++++++++----- 3 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 prisma/migrations/20240609125209_add_excusefrom_to_excuses/migration.sql create mode 100644 prisma/migrations/20240609125605_add_status_to_excuses/migration.sql diff --git a/prisma/migrations/20240609125209_add_excusefrom_to_excuses/migration.sql b/prisma/migrations/20240609125209_add_excusefrom_to_excuses/migration.sql new file mode 100644 index 0000000..6b0bf6e --- /dev/null +++ b/prisma/migrations/20240609125209_add_excusefrom_to_excuses/migration.sql @@ -0,0 +1,14 @@ +/* + Warnings: + + - Added the required column `excuse_from` to the `excuses` table without a default value. This is not possible if the table is not empty. + +*/ +-- CreateEnum +CREATE TYPE "excuse_from" AS ENUM ('INTERVIEW', 'QUESTION', 'INTERVIEW_AND_QUESTION'); + +-- CreateEnum +CREATE TYPE "excuse_status" AS ENUM ('PENDING', 'ACCEPTED', 'REJECTED'); + +-- AlterTable +ALTER TABLE "excuses" ADD COLUMN "excuse_from" "excuse_from" NOT NULL; diff --git a/prisma/migrations/20240609125605_add_status_to_excuses/migration.sql b/prisma/migrations/20240609125605_add_status_to_excuses/migration.sql new file mode 100644 index 0000000..3f41c6d --- /dev/null +++ b/prisma/migrations/20240609125605_add_status_to_excuses/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `excuse_status` to the `excuses` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "excuses" ADD COLUMN "excuse_status" "excuse_status" NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5b1f0d2..9562b7a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -168,14 +168,16 @@ model Window { } model Excuse { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - studentId Int @map("student_id") - windowId Int @map("window_id") - reason String - student Student @relation(fields: [studentId], references: [id], onDelete: Cascade) - window Window @relation(fields: [windowId], references: [id], onDelete: Cascade) + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + studentId Int @map("student_id") + windowId Int @map("window_id") + excuseFrom ExcuseFrom @map("excuse_from") + excuseStatus ExcuseStatus @map("excuse_status") + reason String + student Student @relation(fields: [studentId], references: [id], onDelete: Cascade) + window Window @relation(fields: [windowId], references: [id], onDelete: Cascade) @@map("excuses") } @@ -410,3 +412,19 @@ enum KeyBinding { @@map("key_binding") } + +enum ExcuseFrom { + INTERVIEW + QUESTION + INTERVIEW_AND_QUESTION + + @@map("excuse_from") +} + +enum ExcuseStatus { + PENDING + ACCEPTED + REJECTED + + @@map("excuse_status") +} From d3242d2830525ef41e4ae5f74bc34fc6a0ead0d9 Mon Sep 17 00:00:00 2001 From: ShenyiCui Date: Mon, 10 Jun 2024 02:07:47 +0800 Subject: [PATCH 3/7] feat: add CRUD without data validation --- .../general/excuses/dtos/create-excuse.dto.ts | 21 +++++ .../general/excuses/dtos/update-excuse.dto.ts | 14 +++ .../general/excuses/excuses.controller.ts | 87 +++++++++++++++++++ src/product/general/excuses/excuses.module.ts | 10 +++ .../general/excuses/excuses.service.ts | 80 +++++++++++++++++ src/product/general/general.module.ts | 2 + src/product/interfaces/excuses.ts | 76 ++++++++++++++++ 7 files changed, 290 insertions(+) create mode 100644 src/product/general/excuses/dtos/create-excuse.dto.ts create mode 100644 src/product/general/excuses/dtos/update-excuse.dto.ts create mode 100644 src/product/general/excuses/excuses.controller.ts create mode 100644 src/product/general/excuses/excuses.module.ts create mode 100644 src/product/general/excuses/excuses.service.ts create mode 100644 src/product/interfaces/excuses.ts diff --git a/src/product/general/excuses/dtos/create-excuse.dto.ts b/src/product/general/excuses/dtos/create-excuse.dto.ts new file mode 100644 index 0000000..2c3f812 --- /dev/null +++ b/src/product/general/excuses/dtos/create-excuse.dto.ts @@ -0,0 +1,21 @@ +import { IsEnum, IsNotEmpty, IsNumber, IsString } from 'class-validator'; + +import { ExcuseFrom } from '../../../../infra/prisma/generated'; + +export class CreateExcuseDto { + @IsString() + @IsNotEmpty() + reason: string; + + @IsEnum(ExcuseFrom) + @IsNotEmpty() + excuseFrom: ExcuseFrom; + + @IsNumber() + @IsNotEmpty() + windowId: number; + + @IsNumber() + @IsNotEmpty() + studentId: number; +} diff --git a/src/product/general/excuses/dtos/update-excuse.dto.ts b/src/product/general/excuses/dtos/update-excuse.dto.ts new file mode 100644 index 0000000..d7f5b61 --- /dev/null +++ b/src/product/general/excuses/dtos/update-excuse.dto.ts @@ -0,0 +1,14 @@ +import { IsEnum, IsString } from 'class-validator'; + +import { ExcuseFrom, ExcuseStatus } from '../../../../infra/prisma/generated'; + +export class UpdateExcuseDto { + @IsString() + reason: string; + + @IsEnum(ExcuseFrom) + excuseFrom: ExcuseFrom; + + @IsEnum(ExcuseStatus) + status: ExcuseStatus; +} diff --git a/src/product/general/excuses/excuses.controller.ts b/src/product/general/excuses/excuses.controller.ts new file mode 100644 index 0000000..2490753 --- /dev/null +++ b/src/product/general/excuses/excuses.controller.ts @@ -0,0 +1,87 @@ +import { + Body, + Controller, + Delete, + Get, + Logger, + Param, + Post, + Put, + Query, + UseFilters, + UseGuards, +} from '@nestjs/common'; + +import { User } from '../../../infra/prisma/generated'; +import { ExcuseBase } from '../../../product/interfaces/excuses'; +import { GetUserRest } from '../../../productinfra/decorators'; +import { + JwtRestAdminGuard, + JwtRestStudentOrAdminGuard, +} from '../../../productinfra/guards'; +import { BadRequestExceptionFilter } from '../../../utils'; + +import { CreateExcuseDto } from './dtos/create-excuse.dto'; +import { UpdateExcuseDto } from './dtos/update-excuse.dto'; +import { ExcusesService } from './excuses.service'; + +@UseGuards(JwtRestStudentOrAdminGuard) +@Controller('excuses') +export class ExcusesController { + constructor( + private readonly logger: Logger, + private readonly excusesService: ExcusesService, + ) {} + + @UseGuards(JwtRestAdminGuard) + @Get() + @UseFilters(BadRequestExceptionFilter) + findAllExcuses(@Query('cohortId') id: string): Promise { + this.logger.log('GET /excuses', ExcusesController.name); + return this.excusesService.findAllExcuses(+id); + } + + @Get('/self') + @UseFilters(BadRequestExceptionFilter) + findSelfExcuse( + @GetUserRest() user: User, + @Query('windowId') windowId?: string, + ): Promise { + this.logger.log('GET /excuses/self', ExcusesController.name); + const windowIdNumber = windowId ? +windowId : undefined; + return this.excusesService.findSelf(user, windowIdNumber); + } + + @Get(':id') + @UseFilters(BadRequestExceptionFilter) + findWindow(@Param('id') id: string): Promise { + this.logger.log('GET /excuses/:id', ExcusesController.name); + return this.excusesService.findExcuse(+id); + } + + @Delete(':id') + @UseFilters(BadRequestExceptionFilter) + deleteExcuse(@Param('id') id: string): Promise { + this.logger.log('DELETE /excuses/:id', ExcusesController.name); + return this.excusesService.deleteExcuse(+id); + } + + @Post('/create') + @UseFilters(BadRequestExceptionFilter) + createExcuse(@Body() dto: CreateExcuseDto): Promise { + this.logger.log('POST /excuses/create', ExcusesController.name); + + return this.excusesService.createExcuse(dto); + } + + @Put(':id') + @UseFilters(BadRequestExceptionFilter) + updateExcuse( + @Param('id') id: string, + @Body() dto: Partial, + ): Promise { + this.logger.log('PUT /excuses/:id', ExcusesController.name); + + return this.excusesService.updateExcuse(+id, dto); + } +} diff --git a/src/product/general/excuses/excuses.module.ts b/src/product/general/excuses/excuses.module.ts new file mode 100644 index 0000000..718eb8d --- /dev/null +++ b/src/product/general/excuses/excuses.module.ts @@ -0,0 +1,10 @@ +import { Logger, Module } from '@nestjs/common'; + +import { ExcusesController } from './excuses.controller'; +import { ExcusesService } from './excuses.service'; + +@Module({ + controllers: [ExcusesController], + providers: [ExcusesService, Logger], +}) +export class ExcusesModule {} diff --git a/src/product/general/excuses/excuses.service.ts b/src/product/general/excuses/excuses.service.ts new file mode 100644 index 0000000..490c09a --- /dev/null +++ b/src/product/general/excuses/excuses.service.ts @@ -0,0 +1,80 @@ +import { Injectable } from '@nestjs/common'; + +import { ExcuseStatus, User } from '../../../infra/prisma/generated'; +import { PrismaService } from '../../../infra/prisma/prisma.service'; +import { + ExcuseBase, + makeExcuseBase, +} from '../../../product/interfaces/excuses'; + +import { CreateExcuseDto } from './dtos/create-excuse.dto'; +import { UpdateExcuseDto } from './dtos/update-excuse.dto'; + +@Injectable() +export class ExcusesService { + constructor(private readonly prismaService: PrismaService) {} + + async findAllExcuses(cohortId: number): Promise { + const excuses = await this.prismaService.excuse.findMany({ + where: { window: { cohortId } }, + include: { student: { include: { user: true } }, window: true }, + }); + + return excuses.map((excuse) => makeExcuseBase(excuse)); + } + + async findExcuse(id: number): Promise { + const excuse = await this.prismaService.excuse.findUniqueOrThrow({ + where: { id }, + include: { student: { include: { user: true } }, window: true }, + }); + + return makeExcuseBase(excuse); + } + + async findSelf(user: User, windowId?: number): Promise { + const student = await this.prismaService.student.findFirst({ + where: { userId: user.id }, + }); + + if (!student) { + return []; + } + + const excuses = await this.prismaService.excuse.findMany({ + where: { studentId: student.id, windowId }, + include: { student: { include: { user: true } }, window: true }, + }); + + return excuses.map((excuse) => makeExcuseBase(excuse)); + } + + async createExcuse(excuse: CreateExcuseDto): Promise { + const createExcuseData = { + ...excuse, + excuseStatus: ExcuseStatus.PENDING, + }; + + const createdExcuse = await this.prismaService.excuse.create({ + data: createExcuseData, + }); + + return createdExcuse.id; + } + + async deleteExcuse(id: number): Promise { + await this.prismaService.excuse.delete({ where: { id } }); + } + + async updateExcuse( + id: number, + excuse: Partial, + ): Promise { + const res = await this.prismaService.excuse.update({ + where: { id }, + data: excuse, + }); + + return res.id; + } +} diff --git a/src/product/general/general.module.ts b/src/product/general/general.module.ts index 777509e..66da43e 100644 --- a/src/product/general/general.module.ts +++ b/src/product/general/general.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { AuthModule } from './auth/auth.module'; import { CodeModule } from './code/code.module'; +import { ExcusesModule } from './excuses/excuses.module'; import { InterviewsModule } from './interviews/interviews.module'; import { NotesModule } from './notes/notes.module'; import { QuestionsModule } from './questions/questions.module'; @@ -17,6 +18,7 @@ import { UsersModule } from './users/users.module'; UsersModule, QuestionsModule, InterviewsModule, + ExcusesModule, ], }) export class GeneralModule {} diff --git a/src/product/interfaces/excuses.ts b/src/product/interfaces/excuses.ts new file mode 100644 index 0000000..9beb351 --- /dev/null +++ b/src/product/interfaces/excuses.ts @@ -0,0 +1,76 @@ +import { ExcuseFrom,ExcuseStatus } from 'src/infra/prisma/generated'; + +import { UserBase } from './users'; +import { WindowBase } from './windows'; + +export interface ExcuseBase { + id: number; + user: UserBase; + window: WindowBase; + excuseFrom: ExcuseFrom; + excuseReason: string; + status: ExcuseStatus; +} + +// Excuse with Student with User and Window +interface ExcuseWithRelations { + id: number; + createdAt: Date; + updatedAt: Date; + studentId: number; + windowId: number; + excuseFrom: ExcuseFrom; + excuseStatus: ExcuseStatus; + reason: string; + student: { + id: number; + createdAt: Date; + updatedAt: Date; + userId: string; + cohortId: number; + coursemologyName: string; + coursemologyProfileUrl: string; + user: { + id: string; + createdAt: Date; + updatedAt: Date; + githubUsername: string; + photoUrl: string; + profileUrl: string; + name: string; + role: string; + }; + }; + window: { + id: number; + createdAt: Date; + updatedAt: Date; + startAt: Date; + endAt: Date; + cohortId: number; + requireInterview: boolean; + numQuestions: number; + }; +} + +export const makeExcuseBase = (excuse: ExcuseWithRelations): ExcuseBase => { + return { + id: excuse.id, + user: { + name: excuse.student.user.name, + githubUsername: excuse.student.user.githubUsername, + profileUrl: excuse.student.user.profileUrl, + photoUrl: excuse.student.user.photoUrl, + }, + window: { + id: excuse.window.id, + startAt: excuse.window.startAt, + endAt: excuse.window.endAt, + numQuestions: excuse.window.numQuestions, + requireInterview: excuse.window.requireInterview, + }, + excuseFrom: excuse.excuseFrom, + excuseReason: excuse.reason, + status: excuse.excuseStatus, + }; +}; From e6230ebc5617dff69546b9f5691c8d8eaacd7a12 Mon Sep 17 00:00:00 2001 From: ShenyiCui Date: Mon, 10 Jun 2024 02:11:26 +0800 Subject: [PATCH 4/7] refactor: linter prettier errors --- src/product/interfaces/excuses.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/product/interfaces/excuses.ts b/src/product/interfaces/excuses.ts index 9beb351..f1cc023 100644 --- a/src/product/interfaces/excuses.ts +++ b/src/product/interfaces/excuses.ts @@ -1,4 +1,4 @@ -import { ExcuseFrom,ExcuseStatus } from 'src/infra/prisma/generated'; +import { ExcuseFrom, ExcuseStatus } from 'src/infra/prisma/generated'; import { UserBase } from './users'; import { WindowBase } from './windows'; From e47f9db667f7f83dc61a82a3dedb28b374d3aee0 Mon Sep 17 00:00:00 2001 From: ShenyiCui Date: Mon, 10 Jun 2024 02:35:09 +0800 Subject: [PATCH 5/7] feat: remove need for studentId from excuse creation --- src/product/general/excuses/dtos/create-excuse.dto.ts | 4 ---- src/product/general/excuses/excuses.controller.ts | 7 +++++-- src/product/general/excuses/excuses.service.ts | 11 ++++++++++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/product/general/excuses/dtos/create-excuse.dto.ts b/src/product/general/excuses/dtos/create-excuse.dto.ts index 2c3f812..35d28af 100644 --- a/src/product/general/excuses/dtos/create-excuse.dto.ts +++ b/src/product/general/excuses/dtos/create-excuse.dto.ts @@ -14,8 +14,4 @@ export class CreateExcuseDto { @IsNumber() @IsNotEmpty() windowId: number; - - @IsNumber() - @IsNotEmpty() - studentId: number; } diff --git a/src/product/general/excuses/excuses.controller.ts b/src/product/general/excuses/excuses.controller.ts index 2490753..5f65160 100644 --- a/src/product/general/excuses/excuses.controller.ts +++ b/src/product/general/excuses/excuses.controller.ts @@ -68,10 +68,13 @@ export class ExcusesController { @Post('/create') @UseFilters(BadRequestExceptionFilter) - createExcuse(@Body() dto: CreateExcuseDto): Promise { + createExcuse( + @Body() dto: CreateExcuseDto, + @GetUserRest() user: User, + ): Promise { this.logger.log('POST /excuses/create', ExcusesController.name); - return this.excusesService.createExcuse(dto); + return this.excusesService.createExcuse(dto, user); } @Put(':id') diff --git a/src/product/general/excuses/excuses.service.ts b/src/product/general/excuses/excuses.service.ts index 490c09a..0abc54c 100644 --- a/src/product/general/excuses/excuses.service.ts +++ b/src/product/general/excuses/excuses.service.ts @@ -49,9 +49,18 @@ export class ExcusesService { return excuses.map((excuse) => makeExcuseBase(excuse)); } - async createExcuse(excuse: CreateExcuseDto): Promise { + async createExcuse(excuse: CreateExcuseDto, user: User): Promise { + const student = await this.prismaService.student.findFirst({ + where: { userId: user.id }, + }); + + if (!student) { + throw new Error('Student not found'); + } + const createExcuseData = { ...excuse, + studentId: student.id, excuseStatus: ExcuseStatus.PENDING, }; From 2a609e907d64975c94fa7789cd893c83f4b54ae2 Mon Sep 17 00:00:00 2001 From: ShenyiCui Date: Tue, 11 Jun 2024 02:20:23 +0800 Subject: [PATCH 6/7] feat: rename column excuseStatus to status --- .../migration.sql | 1 + prisma/schema.prisma | 20 +++++++++---------- src/product/interfaces/excuses.ts | 8 ++++---- 3 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 prisma/migrations/20240610181544_rename_excuse_status_to_status/migration.sql diff --git a/prisma/migrations/20240610181544_rename_excuse_status_to_status/migration.sql b/prisma/migrations/20240610181544_rename_excuse_status_to_status/migration.sql new file mode 100644 index 0000000..af5102c --- /dev/null +++ b/prisma/migrations/20240610181544_rename_excuse_status_to_status/migration.sql @@ -0,0 +1 @@ +-- This is an empty migration. \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9562b7a..e6aa867 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -168,16 +168,16 @@ model Window { } model Excuse { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - studentId Int @map("student_id") - windowId Int @map("window_id") - excuseFrom ExcuseFrom @map("excuse_from") - excuseStatus ExcuseStatus @map("excuse_status") - reason String - student Student @relation(fields: [studentId], references: [id], onDelete: Cascade) - window Window @relation(fields: [windowId], references: [id], onDelete: Cascade) + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + studentId Int @map("student_id") + windowId Int @map("window_id") + excuseFrom ExcuseFrom @map("excuse_from") + status ExcuseStatus @map("excuse_status") + reason String + student Student @relation(fields: [studentId], references: [id], onDelete: Cascade) + window Window @relation(fields: [windowId], references: [id], onDelete: Cascade) @@map("excuses") } diff --git a/src/product/interfaces/excuses.ts b/src/product/interfaces/excuses.ts index f1cc023..2ee2a18 100644 --- a/src/product/interfaces/excuses.ts +++ b/src/product/interfaces/excuses.ts @@ -8,7 +8,7 @@ export interface ExcuseBase { user: UserBase; window: WindowBase; excuseFrom: ExcuseFrom; - excuseReason: string; + reason: string; status: ExcuseStatus; } @@ -20,7 +20,7 @@ interface ExcuseWithRelations { studentId: number; windowId: number; excuseFrom: ExcuseFrom; - excuseStatus: ExcuseStatus; + status: ExcuseStatus; reason: string; student: { id: number; @@ -70,7 +70,7 @@ export const makeExcuseBase = (excuse: ExcuseWithRelations): ExcuseBase => { requireInterview: excuse.window.requireInterview, }, excuseFrom: excuse.excuseFrom, - excuseReason: excuse.reason, - status: excuse.excuseStatus, + reason: excuse.reason, + status: excuse.status, }; }; From eb2aa9b2cb77e69c851dcb5c86422c58a613c443 Mon Sep 17 00:00:00 2001 From: ShenyiCui Date: Tue, 11 Jun 2024 02:20:50 +0800 Subject: [PATCH 7/7] feat: add backend validation to create and update excuses --- .../general/excuses/excuses.service.ts | 39 +++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/product/general/excuses/excuses.service.ts b/src/product/general/excuses/excuses.service.ts index 0abc54c..28cc76e 100644 --- a/src/product/general/excuses/excuses.service.ts +++ b/src/product/general/excuses/excuses.service.ts @@ -50,18 +50,27 @@ export class ExcusesService { } async createExcuse(excuse: CreateExcuseDto, user: User): Promise { - const student = await this.prismaService.student.findFirst({ - where: { userId: user.id }, - }); + const [student, window] = await Promise.all([ + this.prismaService.student.findFirst({ + where: { userId: user.id }, + }), + this.prismaService.window.findUnique({ + where: { id: excuse.windowId }, + }), + ]); + + if (!student || !window) { + throw new Error('Student or Window not found'); + } - if (!student) { - throw new Error('Student not found'); + if (window.endAt < new Date()) { + throw new Error('End date must be greater than current date'); } const createExcuseData = { ...excuse, studentId: student.id, - excuseStatus: ExcuseStatus.PENDING, + status: ExcuseStatus.PENDING, }; const createdExcuse = await this.prismaService.excuse.create({ @@ -79,9 +88,25 @@ export class ExcusesService { id: number, excuse: Partial, ): Promise { + const existingExcuse = await this.prismaService.excuse.findUnique({ + where: { id }, + }); + + if (!existingExcuse) { + throw new Error('Excuse not found'); + } + + const window = await this.prismaService.window.findUnique({ + where: { id: existingExcuse.windowId }, + }); + + if (window && window.endAt < new Date()) { + throw new Error('End date must be greater than current date'); + } + const res = await this.prismaService.excuse.update({ where: { id }, - data: excuse, + data: { ...excuse }, }); return res.id;