diff --git a/src/common/entities/EArbitraryPlannerItem.ts b/src/common/entities/EArbitraryPlannerItem.ts new file mode 100644 index 00000000..b5dfbb69 --- /dev/null +++ b/src/common/entities/EArbitraryPlannerItem.ts @@ -0,0 +1,19 @@ +import { Prisma } from '@prisma/client'; + +export namespace EArbitraryPlannerItem { + export const Basic = + Prisma.validator()({}); + export type Basic = Prisma.planner_arbitraryplanneritemGetPayload< + typeof Basic + >; + + export const Details = + Prisma.validator()({ + include: { + subject_department: true, + }, + }); + export type Details = Prisma.planner_arbitraryplanneritemGetPayload< + typeof Details + >; +} diff --git a/src/common/interfaces/IPlanner.ts b/src/common/interfaces/IPlanner.ts new file mode 100644 index 00000000..ce982d33 --- /dev/null +++ b/src/common/interfaces/IPlanner.ts @@ -0,0 +1,48 @@ +import { Type } from 'class-transformer'; +import { IsIn, IsInt, IsString } from 'class-validator'; +import { DepartmentResponseDto } from './dto/department/department.response.dto'; + +export namespace IPlanner { + export interface ArbitraryPlannerItemResponseDto { + id: number; + item_type: 'ARBITRARY'; + is_excluded: boolean; + year: number; + semester: number; + department: DepartmentResponseDto | null; + type: string; + type_en: string; + credit: number; + credit_au: number; + } + + export class AddArbitraryItemDto { + // year, semester, department, type, type_en, credit, credit_au + + @IsInt() + @Type(() => Number) + year!: number; + + @IsIn([1, 2, 3, 4]) + @Type(() => Number) + semester!: 1 | 2 | 3 | 4; + + @IsInt() + @Type(() => Number) + department!: number; + + @IsString() + type!: string; + + @IsString() + type_en!: string; + + @IsInt() + @Type(() => Number) + credit!: number; + + @IsInt() + @Type(() => Number) + credit_au!: number; + } +} diff --git a/src/modules/planners/planners.controller.ts b/src/modules/planners/planners.controller.ts index 2d5a9951..b2b939fc 100644 --- a/src/modules/planners/planners.controller.ts +++ b/src/modules/planners/planners.controller.ts @@ -9,6 +9,7 @@ import { } from '@nestjs/common'; import { session_userprofile } from '@prisma/client'; import { GetUser } from 'src/common/decorators/get-user.decorator'; +import { IPlanner } from 'src/common/interfaces/IPlanner'; import { PlannerBodyDto, PlannerQueryDto, @@ -44,4 +45,22 @@ export class PlannersController { const newPlanner = await this.plannersService.postPlanner(planner, user); return newPlanner; } + + @Post(':plannerId/add-arbitrary-item') + async addArbitraryItem( + @Param('id') id: number, + + @Param('plannerId') plannerId: number, + @Body() item: IPlanner.AddArbitraryItemDto, + @GetUser() user: session_userprofile, + ) { + if (id !== user.id) throw new UnauthorizedException(); + + const newPlanner = await this.plannersService.addArbitraryItem( + plannerId, + item, + user, + ); + return newPlanner; + } } diff --git a/src/modules/planners/planners.service.ts b/src/modules/planners/planners.service.ts index bfb610b6..866d2fdf 100644 --- a/src/modules/planners/planners.service.ts +++ b/src/modules/planners/planners.service.ts @@ -1,11 +1,18 @@ -import { Injectable } from '@nestjs/common'; +import { + Injectable, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; import { session_userprofile } from '@prisma/client'; +import { IPlanner } from 'src/common/interfaces/IPlanner'; import { PlannerBodyDto, PlannerQueryDto, } from 'src/common/interfaces/dto/planner/planner.request.dto'; import { PlannerResponseDto } from 'src/common/interfaces/dto/planner/planner.response.dto'; +import { toJsonArbitraryItem } from 'src/common/interfaces/serializer/planner.item.serializer'; import { toJsonPlanner } from 'src/common/interfaces/serializer/planner.serializer'; +import { DepartmentRepository } from 'src/prisma/repositories/department.repository'; import { LectureRepository } from 'src/prisma/repositories/lecture.repository'; import { PlannerRepository } from 'src/prisma/repositories/planner.repository'; @@ -14,6 +21,7 @@ export class PlannersService { constructor( private readonly PlannerRepository: PlannerRepository, private readonly LectureRepository: LectureRepository, + private readonly DepartmentRepository: DepartmentRepository, ) {} public async getPlannerByUser( @@ -93,4 +101,31 @@ export class PlannersService { return toJsonPlanner(planner); } + + async addArbitraryItem( + plannerId: number, + body: IPlanner.AddArbitraryItemDto, + user: session_userprofile, + ) { + const planner = await this.PlannerRepository.getBasicPlannerById(plannerId); + if (!planner) throw new NotFoundException(); + if (planner.user_id !== user.id) throw new UnauthorizedException(); + + const department = await this.DepartmentRepository.getBasicDepartmentById( + body.department, + ); + + const arbitraryItem = + await this.PlannerRepository.createArbitraryPlannerItem(planner, { + year: body.year, + semester: body.semester, + department_id: department?.id || null, + type: body.type, + type_en: body.type_en, + credit: body.credit, + credit_au: body.credit_au, + is_excluded: false, + }); + return toJsonArbitraryItem(arbitraryItem); + } } diff --git a/src/prisma/repositories/department.repository.ts b/src/prisma/repositories/department.repository.ts index 30114ad0..76e4701e 100644 --- a/src/prisma/repositories/department.repository.ts +++ b/src/prisma/repositories/department.repository.ts @@ -6,6 +6,12 @@ import { PrismaService } from '../prisma.service'; export class DepartmentRepository { constructor(private readonly prisma: PrismaService) {} + async getBasicDepartmentById(id: number): Promise { + return this.prisma.subject_department.findUnique({ + where: { id }, + }); + } + async getDepartmentOfUser( user: session_userprofile, ): Promise { diff --git a/src/prisma/repositories/planner.repository.ts b/src/prisma/repositories/planner.repository.ts index 041c23c0..9a6d8577 100644 --- a/src/prisma/repositories/planner.repository.ts +++ b/src/prisma/repositories/planner.repository.ts @@ -1,5 +1,6 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { session_userprofile } from '@prisma/client'; +import { EArbitraryPlannerItem } from 'src/common/entities/EArbitraryPlannerItem'; import { PlannerBodyDto, PlannerQueryDto, @@ -38,6 +39,14 @@ export class PlannerRepository { }); } + public async getBasicPlannerById(id: number): Promise { + return await this.prisma.planner_planner.findUnique({ + where: { + id: id, + }, + }); + } + public async createPlanner( body: PlannerBodyDto, arrange_order: number, @@ -221,8 +230,11 @@ export class PlannerRepository { public async createArbitraryPlannerItem( planner: PlannerBasic, - target_item: ArbitraryPlannerItem, - ) { + target_item: Omit< + ArbitraryPlannerItem, + 'id' | 'planner_id' | 'subject_department' + >, + ): Promise { return await this.prisma.planner_arbitraryplanneritem.create({ data: { planner_planner: { @@ -245,6 +257,7 @@ export class PlannerRepository { credit: target_item.credit, credit_au: target_item.credit_au, }, + include: EArbitraryPlannerItem.Details.include, }); } }