Skip to content

Refactor source code #221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jul 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6a0a9f2
Merge branch 'dev' into feature/refactor
hardingadonis Jul 20, 2025
b5aed09
Merge branch 'dev' into feature/refactor
hardingadonis Jul 20, 2025
bc3fa92
feat: add DTOs for thesis management and update imports
hardingadonis Jul 20, 2025
afbc514
feat: implement lecturer, moderator, and publish controllers and serv…
hardingadonis Jul 20, 2025
c98eeee
feat: enhance thesis controllers and documentation with API operation…
hardingadonis Jul 20, 2025
011291f
feat: add thesis constants for API integration
hardingadonis Jul 20, 2025
abade4a
feat: refactor thesis controllers and services for improved structure…
hardingadonis Jul 20, 2025
fc76e20
feat: implement thesis response mapping and enhance thesis lecturer s…
hardingadonis Jul 20, 2025
8510b0a
feat: add thesis detail mapper and response classes; update module an…
hardingadonis Jul 20, 2025
851b4ee
feat: enhance thesis publish service and controller with detailed res…
hardingadonis Jul 20, 2025
1fb9b5b
feat: update thesis moderator controller and service for consistent t…
hardingadonis Jul 20, 2025
a202f86
feat: update thesis moderator service and controller for improved res…
hardingadonis Jul 20, 2025
403a9ff
feat: refactor thesis assignment logic for improved clarity and consi…
hardingadonis Jul 20, 2025
5d0314e
feat: refactor thesis lecturer controller and service for improved st…
hardingadonis Jul 20, 2025
8d23d0a
feat: enhance thesis lecturer controller and service with improved re…
hardingadonis Jul 20, 2025
0a29348
refactor: replace ThesisController with ThesisLecturerController and …
hardingadonis Jul 20, 2025
fcf3465
feat: integrate caching in thesis services for improved performance a…
hardingadonis Jul 20, 2025
afb92f2
feat: enhance thesis creation logic to include lecturer and skill det…
hardingadonis Jul 20, 2025
e12fa16
refactor: extract cache handling logic into a separate method for the…
hardingadonis Jul 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/domains/theses/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '@/theses/constants/thesis.constant';
9 changes: 9 additions & 0 deletions src/domains/theses/constants/thesis.constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const THESIS_API_TAGS = 'Thesis';

export const BASE_THESIS_PATH = 'theses';

export const CACHE_KEY = 'cache:' + BASE_THESIS_PATH;

export const THESIS_CONSTANTS = {
BASE: BASE_THESIS_PATH,
};
3 changes: 3 additions & 0 deletions src/domains/theses/controllers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from '@/theses/controllers/thesis-lecturer.controller';
export * from '@/theses/controllers/thesis-moderator.controller';
export * from '@/theses/controllers/thesis-publish.controller';
105 changes: 105 additions & 0 deletions src/domains/theses/controllers/thesis-lecturer.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Param,
Post,
Put,
Req,
UseGuards,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { Request } from 'express';

import {
JwtAccessAuthGuard,
Role,
RoleGuard,
Roles,
UserPayload,
} from '@/auth';
import { ApiArrayResponse, ApiBaseResponse } from '@/common';
import { THESIS_API_TAGS, THESIS_CONSTANTS } from '@/theses/constants';
import { ThesisLecturerDocs } from '@/theses/docs';
import { CreateThesisDto, UpdateThesisDto } from '@/theses/dtos';
import { ThesisDetailResponse, ThesisResponse } from '@/theses/responses';
import { ThesisLecturerService } from '@/theses/services';

@UseGuards(JwtAccessAuthGuard, RoleGuard)
@ApiBearerAuth()
@ApiTags(THESIS_API_TAGS)
@Controller(THESIS_CONSTANTS.BASE)
export class ThesisLecturerController {
constructor(private readonly service: ThesisLecturerService) {}

@Roles(Role.MODERATOR, Role.LECTURER)
@HttpCode(HttpStatus.CREATED)
@Post()
@ApiOperation(ThesisLecturerDocs.create)
@ApiBaseResponse(ThesisDetailResponse, HttpStatus.CREATED)
async create(
@Req() req: Request,
@Body() dto: CreateThesisDto,
): Promise<ThesisDetailResponse> {
const user = req.user as UserPayload;

return await this.service.create(user.id, dto);
}

@Roles(Role.LECTURER, Role.MODERATOR)
@HttpCode(HttpStatus.OK)
@Get('lecturer/:lecturerId')
@ApiOperation(ThesisLecturerDocs.findAllByLecturerId)
@ApiArrayResponse(ThesisDetailResponse, HttpStatus.OK)
async findAllByLecturerId(
@Param('lecturerId') lecturerId: string,
): Promise<ThesisDetailResponse[]> {
return await this.service.findAllByLecturerId(lecturerId);
}

@Roles(Role.MODERATOR, Role.LECTURER)
@HttpCode(HttpStatus.OK)
@Put(':id')
@ApiOperation(ThesisLecturerDocs.update)
@ApiBaseResponse(ThesisDetailResponse, HttpStatus.OK)
async update(
@Req() req: Request,
@Param('id') id: string,
@Body() dto: UpdateThesisDto,
): Promise<ThesisDetailResponse> {
const user = req.user as UserPayload;

return await this.service.update(user.id, id, dto);
}

@Roles(Role.MODERATOR, Role.LECTURER)
@HttpCode(HttpStatus.OK)
@Post(':id/submit')
@ApiOperation(ThesisLecturerDocs.submitForReview)
@ApiBaseResponse(ThesisDetailResponse, HttpStatus.OK)
async submitForReview(
@Req() req: Request,
@Param('id') id: string,
): Promise<ThesisDetailResponse> {
const user = req.user as UserPayload;

return await this.service.submitForReview(user.id, id);
}

@Roles(Role.MODERATOR, Role.LECTURER)
@HttpCode(HttpStatus.OK)
@Delete(':id')
@ApiOperation(ThesisLecturerDocs.remove)
@ApiBaseResponse(ThesisResponse, HttpStatus.OK)
async remove(
@Req() req: Request,
@Param('id') id: string,
): Promise<ThesisResponse> {
const user = req.user as UserPayload;

return await this.service.remove(user.id, id);
}
}
64 changes: 64 additions & 0 deletions src/domains/theses/controllers/thesis-moderator.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
Body,
Controller,
HttpCode,
HttpStatus,
Param,
Post,
Put,
UseGuards,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';

import { JwtAccessAuthGuard, Role, RoleGuard, Roles } from '@/auth';
import { ApiBaseResponse, ApiEmptyResponse } from '@/common';
import { THESIS_API_TAGS, THESIS_CONSTANTS } from '@/theses/constants';
import { ThesisModeratorDocs } from '@/theses/docs';
import {
AssignThesisDto,
PublishThesisDto,
ReviewThesisDto,
} from '@/theses/dtos';
import { ThesisDetailResponse } from '@/theses/responses';
import { ThesisModeratorService } from '@/theses/services';

@UseGuards(JwtAccessAuthGuard, RoleGuard)
@ApiBearerAuth()
@ApiTags(THESIS_API_TAGS)
@Controller(THESIS_CONSTANTS.BASE)
export class ThesisModeratorController {
constructor(private readonly service: ThesisModeratorService) {}

@Roles(Role.MODERATOR)
@HttpCode(HttpStatus.OK)
@Put('publish')
@ApiOperation(ThesisModeratorDocs.publishTheses)
@ApiEmptyResponse(HttpStatus.OK)
async publishTheses(@Body() dto: PublishThesisDto): Promise<void> {
return await this.service.publishTheses(dto);
}

@Roles(Role.MODERATOR)
@HttpCode(HttpStatus.OK)
@Post(':id/review')
@ApiOperation(ThesisModeratorDocs.reviewThesis)
@ApiBaseResponse(ThesisDetailResponse, HttpStatus.OK)
async reviewThesis(
@Param('id') id: string,
@Body() dto: ReviewThesisDto,
): Promise<ThesisDetailResponse> {
return await this.service.reviewThesis(id, dto);
}

@Roles(Role.MODERATOR)
@HttpCode(HttpStatus.OK)
@Post(':id/assign')
@ApiOperation(ThesisModeratorDocs.assignThesis)
@ApiBaseResponse(ThesisDetailResponse, HttpStatus.OK)
async assignThesis(
@Param('id') id: string,
@Body() dto: AssignThesisDto,
): Promise<ThesisDetailResponse> {
return await this.service.assignThesis(id, dto);
}
}
50 changes: 50 additions & 0 deletions src/domains/theses/controllers/thesis-publish.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
Controller,
Get,
HttpCode,
HttpStatus,
Param,
UseGuards,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';

import { JwtAccessAuthGuard, RoleGuard } from '@/auth';
import { ApiArrayResponse, ApiBaseResponse } from '@/common';
import { THESIS_API_TAGS, THESIS_CONSTANTS } from '@/theses/constants';
import { ThesisPublishDocs } from '@/theses/docs';
import { ThesisDetailResponse } from '@/theses/responses';
import { ThesisPublishService } from '@/theses/services';

@UseGuards(JwtAccessAuthGuard, RoleGuard)
@ApiBearerAuth()
@ApiTags(THESIS_API_TAGS)
@Controller(THESIS_CONSTANTS.BASE)
export class ThesisPublishController {
constructor(private readonly service: ThesisPublishService) {}

@HttpCode(HttpStatus.OK)
@Get()
@ApiOperation(ThesisPublishDocs.findAll)
@ApiArrayResponse(ThesisDetailResponse, HttpStatus.OK)
async findAll(): Promise<ThesisDetailResponse[]> {
return await this.service.findAll();
}

@HttpCode(HttpStatus.OK)
@Get(':id')
@ApiOperation(ThesisPublishDocs.findOne)
@ApiBaseResponse(ThesisDetailResponse, HttpStatus.OK)
async findOne(@Param('id') id: string): Promise<ThesisDetailResponse> {
return await this.service.findOne(id);
}

@HttpCode(HttpStatus.OK)
@Get('semester/:semesterId')
@ApiOperation(ThesisPublishDocs.findAllBySemesterId)
@ApiArrayResponse(ThesisDetailResponse, HttpStatus.OK)
async findAllBySemesterId(
@Param('semesterId') semesterId: string,
): Promise<ThesisDetailResponse[]> {
return await this.service.findAllBySemesterId(semesterId);
}
}
3 changes: 3 additions & 0 deletions src/domains/theses/docs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from '@/theses/docs/thesis-publish.doc';
export * from '@/theses/docs/thesis-lecturer.doc';
export * from '@/theses/docs/thesis-moderator.doc';
28 changes: 28 additions & 0 deletions src/domains/theses/docs/thesis-lecturer.doc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ApiOperationOptions } from '@nestjs/swagger';

export const ThesisLecturerDocs = {
create: {
summary: 'Create new thesis',
description: `Create a new thesis project proposal.\n\n- **Authorization:** Lecturer or Moderator only.\n- **Validations:**\n - All required fields (title, description, domain, skills, etc.) must be provided.\n - Title must be unique within the semester.\n - Skill IDs must exist.\n - Lecturer cannot create more than the maximum allowed theses in the semester (maxThesesPerLecturer).\n- **Business logic:**\n - Checks current thesis count for lecturer in the semester before creation.\n - Creates thesis record, initial version, and required skills.\n - Assigns the creator as the first supervisor.\n - Uses transactions for consistency.\n - Triggers cache invalidation.\n- **Error handling:**\n - 409 if title is not unique, creation fails, or maxThesesPerLecturer is reached.\n - 404 if skill IDs or semester do not exist.\n- **Logging:** Logs all creation attempts and errors.\n\n**Status Codes:**\n- **201 Created:** Thesis successfully created\n- **400 Bad Request:** Invalid input data or validation errors\n- **401 Unauthorized:** Invalid or missing authentication token\n- **403 Forbidden:** User does not have lecturer/moderator role\n- **404 Not Found:** Semester or skill IDs do not exist\n- **409 Conflict:** Title not unique or max theses limit reached\n- **500 Internal Server Error:** Database or server error`,
} as ApiOperationOptions,

findAllByLecturerId: {
summary: 'Get theses by lecturer',
description: `Retrieve all thesis projects supervised or created by a specific lecturer.\n\n- **Authorization:** Lecturer or Moderator only.\n- **Business logic:**\n - Returns theses associated with the lecturer, including supervision assignments, proposals, project status, and student enrollments.\n - No caching for list operations.\n- **Error handling:**\n - 404 if lecturer not found.\n - 500 on database errors.\n- **Logging:** Logs all fetch attempts and errors.\n\n**Status Codes:**\n- **200 OK:** Successfully retrieved lecturer's theses\n- **401 Unauthorized:** Invalid or missing authentication token\n- **403 Forbidden:** User does not have lecturer/moderator role\n- **404 Not Found:** Lecturer does not exist\n- **500 Internal Server Error:** Database or server error`,
} as ApiOperationOptions,

update: {
summary: 'Update thesis information',
description: `Update detailed information of a specific thesis project.\n\n- **Authorization:** Lecturer or Moderator only.\n- **Validations:**\n - Only the creator/assigned lecturer can update.\n - All required fields must be valid.\n - Skill IDs must exist.\n- **Business logic:**\n - Updates thesis properties and creates new version if supporting document is provided.\n - Updates required skills.\n - Uses transactions for consistency.\n - Triggers cache invalidation.\n- **Error handling:**\n - 403 if not the creator/lecturer.\n - 404 if thesis not found.\n - 409 if update fails.\n- **Logging:** Logs all update attempts and errors.\n\n**Status Codes:**\n- **200 OK:** Thesis successfully updated\n- **400 Bad Request:** Invalid input data or validation errors\n- **401 Unauthorized:** Invalid or missing authentication token\n- **403 Forbidden:** User does not have permission to update this thesis\n- **404 Not Found:** Thesis or skill IDs do not exist\n- **409 Conflict:** Update operation failed due to business rule violation\n- **500 Internal Server Error:** Database or server error`,
} as ApiOperationOptions,

submitForReview: {
summary: 'Submit thesis for review',
description: `Submit a thesis project for administrative review and approval.\n\n- **Authorization:** Lecturer or Moderator only.\n- **Validations:**\n - Only the creator/assigned lecturer can submit.\n - Thesis must be in New or Rejected status.\n- **Business logic:**\n - Changes thesis status to Pending.\n - Triggers cache invalidation and sends notification email.\n- **Error handling:**\n - 403 if not the creator/lecturer.\n - 404 if thesis not found.\n - 409 if already pending/approved or invalid status.\n- **Logging:** Logs all submission attempts, notifications, and errors.\n\n**Status Codes:**\n- **200 OK:** Thesis successfully submitted for review\n- **401 Unauthorized:** Invalid or missing authentication token\n- **403 Forbidden:** User does not have permission to submit this thesis\n- **404 Not Found:** Thesis does not exist\n- **409 Conflict:** Thesis is not in a valid status for submission (must be New or Rejected)\n- **500 Internal Server Error:** Database or server error`,
} as ApiOperationOptions,

remove: {
summary: 'Delete thesis project',
description: `Permanently remove a thesis project from the system.\n\n- **Authorization:** Lecturer or Moderator only.\n- **Validations:**\n - Only the creator/assigned lecturer can delete.\n - Thesis must be in New or Rejected status.\n- **Business logic:**\n - Deletes thesis and all associated data.\n - Triggers cache invalidation.\n- **Error handling:**\n - 403 if not the creator/lecturer.\n - 404 if thesis not found.\n - 409 if status is not New or Rejected.\n- **Logging:** Logs all deletion attempts and errors.\n\n**Status Codes:**\n- **200 OK:** Thesis successfully deleted\n- **401 Unauthorized:** Invalid or missing authentication token\n- **403 Forbidden:** User does not have permission to delete this thesis\n- **404 Not Found:** Thesis does not exist\n- **409 Conflict:** Thesis is not in a valid status for deletion (must be New or Rejected)\n- **500 Internal Server Error:** Database or server error`,
} as ApiOperationOptions,
};
18 changes: 18 additions & 0 deletions src/domains/theses/docs/thesis-moderator.doc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ApiOperationOptions } from '@nestjs/swagger';

export const ThesisModeratorDocs = {
publishTheses: {
summary: 'Publish theses for student selection',
description: `Publish or unpublish a batch of thesis projects, making them available or unavailable for student selection.\n\n- **Authorization:** Moderator only.\n- **Validations:**\n - All thesis IDs must exist.\n - Only approved theses can be published.\n - Each thesis must have exactly 2 supervisors to be published.\n - Cannot unpublish theses already selected by groups.\n- **Business logic:**\n - Updates publication status for each thesis.\n - Checks supervisor count for each thesis before publishing.\n - Sends notification emails to lecturers.\n - Triggers cache invalidation.\n- **Error handling:**\n - 404 if thesis not found.\n - 409 if not approved, does not have 2 supervisors, already published/unpublished, or selected by group.\n- **Logging:** Logs all publish/unpublish attempts, notifications, and errors.\n\n**Status Codes:**\n- **200 OK:** Theses successfully published/unpublished\n- **400 Bad Request:** Invalid input data or missing thesis IDs\n- **401 Unauthorized:** Invalid or missing authentication token\n- **403 Forbidden:** User does not have moderator role\n- **404 Not Found:** One or more thesis IDs do not exist\n- **409 Conflict:** Theses not approved, missing supervisors, already in target state, or selected by groups\n- **500 Internal Server Error:** Database or server error`,
} as ApiOperationOptions,

reviewThesis: {
summary: 'Review and approve/reject thesis',
description: `Conduct administrative review of a submitted thesis project, with approval or rejection.\n\n- **Authorization:** Moderator only.\n- **Validations:**\n - Thesis must be in Pending status and not published.\n - Only Approved or Rejected status allowed.\n- **Business logic:**\n - Updates thesis status and sends notification email.\n - Triggers cache invalidation.\n- **Error handling:**\n - 404 if thesis not found.\n - 409 if already published or invalid status.\n- **Logging:** Logs all review attempts, notifications, and errors.\n\n**Status Codes:**\n- **200 OK:** Thesis successfully reviewed and status updated\n- **400 Bad Request:** Invalid input data or status value\n- **401 Unauthorized:** Invalid or missing authentication token\n- **403 Forbidden:** User does not have moderator role\n- **404 Not Found:** Thesis does not exist\n- **409 Conflict:** Thesis is published or not in Pending status\n- **500 Internal Server Error:** Database or server error`,
} as ApiOperationOptions,

assignThesis: {
summary: 'Assign thesis to group',
description: `Assign a thesis project to a specific student group.\n\n- **Authorization:** Moderator only.\n- **Validations:**\n - Thesis must be approved and published.\n - Group must exist, be in the same semester, and not already have a thesis.\n - Thesis must not already be assigned.\n- **Business logic:**\n - Assigns thesis to group and updates records.\n - Triggers cache invalidation and sends notification emails.\n- **Error handling:**\n - 404 if thesis or group not found.\n - 409 if already assigned, not approved/published, or group already has thesis.\n- **Logging:** Logs all assignment attempts, notifications, and errors.\n\n**Status Codes:**\n- **200 OK:** Thesis successfully assigned to group\n- **400 Bad Request:** Invalid input data or missing group ID\n- **401 Unauthorized:** Invalid or missing authentication token\n- **403 Forbidden:** User does not have moderator role\n- **404 Not Found:** Thesis or group does not exist\n- **409 Conflict:** Thesis already assigned, not approved/published, group already has thesis, or semester mismatch\n- **500 Internal Server Error:** Database or server error`,
} as ApiOperationOptions,
};
18 changes: 18 additions & 0 deletions src/domains/theses/docs/thesis-publish.doc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ApiOperationOptions } from '@nestjs/swagger';

export const ThesisPublishDocs = {
findAll: {
summary: 'Get all theses',
description: `Retrieve a comprehensive list of all thesis projects.\n\n- **Authorization:** Authenticated users only.\n- **Business logic:**\n - Returns detailed information including versions, required skills, and metadata.\n - No caching for list operations to ensure real-time data.\n- **Error handling:**\n - 500 on database errors.\n- **Logging:** Logs all fetch attempts and errors.\n\n**Status Codes:**\n- **200 OK:** Successfully retrieved theses\n- **401 Unauthorized:** Invalid or missing authentication token\n- **500 Internal Server Error:** Database or server error`,
} as ApiOperationOptions,

findAllBySemesterId: {
summary: 'Get theses by semester',
description: `Retrieve all thesis projects associated with a specific semester.\n\n- **Authorization:** Authenticated users only.\n- **Business logic:**\n - Returns thesis information filtered by semester, including project details, status, supervisor assignments, and student enrollments.\n - No caching for list operations.\n- **Error handling:**\n - 404 if semester not found.\n - 500 on database errors.\n- **Logging:** Logs all fetch attempts and errors.\n\n**Status Codes:**\n- **200 OK:** Successfully retrieved theses for the semester\n- **401 Unauthorized:** Invalid or missing authentication token\n- **404 Not Found:** Semester does not exist\n- **500 Internal Server Error:** Database or server error`,
} as ApiOperationOptions,

findOne: {
summary: 'Get thesis by ID',
description: `Retrieve detailed information about a specific thesis project by its unique identifier.\n\n- **Authorization:** Authenticated users only.\n- **Business logic:**\n - Returns thesis data including title, description, domain, required skills, status, supervision details, student assignments, milestones, and progress.\n - Uses cache-aside pattern for performance.\n- **Error handling:**\n - 404 if thesis not found.\n - 500 on database/cache errors.\n- **Logging:** Logs all fetch attempts, cache hits, and errors.\n\n**Status Codes:**\n- **200 OK:** Successfully retrieved thesis details\n- **401 Unauthorized:** Invalid or missing authentication token\n- **404 Not Found:** Thesis does not exist\n- **500 Internal Server Error:** Database or server error`,
} as ApiOperationOptions,
};
5 changes: 0 additions & 5 deletions src/domains/theses/dto/index.ts

This file was deleted.

5 changes: 5 additions & 0 deletions src/domains/theses/dtos/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from '@/theses/dtos/create-thesis.dto';
export * from '@/theses/dtos/review-thesis.dto';
export * from '@/theses/dtos/update-thesis.dto';
export * from '@/theses/dtos/publish-theses.dto';
export * from '@/theses/dtos/assign-thesis.dto';
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export class PublishThesisDto {
@ApiProperty({ required: true })
@IsArray()
@IsString({ each: true })
thesesIds: string[];
thesisIds: string[];

@ApiProperty({ required: true })
@IsBoolean()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PartialType } from '@nestjs/swagger';

import { CreateThesisDto } from '@/theses/dto/create-thesis.dto';
import { CreateThesisDto } from '@/theses/dtos/create-thesis.dto';

export class UpdateThesisDto extends PartialType(CreateThesisDto) {}
Loading