Spaces:
Sleeping
Sleeping
| import { | |
| Controller, | |
| Get, | |
| Post, | |
| Patch, | |
| Delete, | |
| Body, | |
| Param, | |
| Query, | |
| UseGuards, | |
| HttpStatus, | |
| HttpCode, | |
| ParseIntPipe, | |
| } from '@nestjs/common'; | |
| import { | |
| ApiTags, | |
| ApiOperation, | |
| ApiResponse, | |
| ApiBearerAuth, | |
| ApiParam, | |
| ApiQuery, | |
| ApiBody, | |
| } from '@nestjs/swagger'; | |
| import { CoursesService } from '../services/courses.service'; | |
| import { CreateCourseDto, UpdateCourseDto, CreatePrerequisiteDto } from '../dtos'; | |
| import { CourseStatus } from '../enums'; | |
| import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard'; | |
| import { RolesGuard } from '../../auth/guards/roles.guard'; | |
| import { Roles } from '../../auth/roles.decorator'; | |
| import { RoleName } from '../../auth/entities/role.entity'; | |
| ('📖 Courses') | |
| ('api/courses') | |
| export class CoursesController { | |
| constructor(private readonly coursesService: CoursesService) {} | |
| () | |
| ({ | |
| summary: 'List all courses', | |
| description: ` | |
| ## List All Courses | |
| Retrieves a paginated list of courses with optional filters. | |
| ### Access Control | |
| - **Authentication Required**: No (Public endpoint) | |
| - **Roles Required**: None (accessible to all) | |
| ### Filtering Options | |
| - \`departmentId\`: Filter by department | |
| - \`level\`: Filter by course level (100, 200, 300, etc.) | |
| - \`status\`: Filter by course status | |
| - \`search\`: Search by course name or code | |
| ### Pagination | |
| - \`page\`: Page number (default: 1) | |
| - \`limit\`: Items per page (default: 20, max: 100) | |
| `, | |
| }) | |
| ({ name: 'departmentId', required: false, type: Number }) | |
| ({ name: 'level', required: false, type: String, example: '300' }) | |
| ({ | |
| name: 'status', | |
| required: false, | |
| type: String, | |
| schema: { type: 'string', enum: Object.values(CourseStatus) }, | |
| }) | |
| ({ name: 'search', required: false, type: String }) | |
| ({ name: 'page', required: false, type: Number, example: 1 }) | |
| ({ name: 'limit', required: false, type: Number, example: 20 }) | |
| ({ status: 200, description: 'Paginated list of courses' }) | |
| async findAll( | |
| ('departmentId', new ParseIntPipe({ optional: true })) | |
| departmentId?: number, | |
| ('level') level?: string, | |
| ('status') status?: CourseStatus, | |
| ('search') search?: string, | |
| ('page', new ParseIntPipe({ optional: true })) page = 1, | |
| ('limit', new ParseIntPipe({ optional: true })) limit = 20, | |
| ) { | |
| return this.coursesService.findAll( | |
| departmentId, | |
| level, | |
| status, | |
| search, | |
| page, | |
| limit, | |
| ); | |
| } | |
| ('department/:deptId') | |
| ({ | |
| summary: 'Get courses by department', | |
| description: ` | |
| ## Get Courses by Department | |
| Retrieves all courses offered by a specific department. | |
| ### Access Control | |
| - **Authentication Required**: No (Public endpoint) | |
| - **Roles Required**: None | |
| `, | |
| }) | |
| ({ name: 'deptId', description: 'Department ID', type: Number }) | |
| ({ status: 200, description: 'List of courses' }) | |
| ({ status: 404, description: 'Department not found' }) | |
| async findByDepartment( | |
| ('deptId', ParseIntPipe) deptId: number, | |
| ) { | |
| return this.coursesService.findByDepartment(deptId); | |
| } | |
| (':id/recent-activity') | |
| (JwtAuthGuard, RolesGuard) | |
| (RoleName.INSTRUCTOR, RoleName.TA, RoleName.ADMIN, RoleName.IT_ADMIN) | |
| ('JWT-auth') | |
| ({ | |
| summary: 'Get recent course activity', | |
| description: | |
| 'Returns recent timeline items for a course (assignments, materials, submissions, grading).', | |
| }) | |
| ({ name: 'id', description: 'Course ID', type: Number }) | |
| ({ name: 'limit', required: false, type: Number, example: 8 }) | |
| ({ status: 200, description: 'Recent activity list' }) | |
| async getRecentActivity( | |
| ('id', ParseIntPipe) id: number, | |
| ('limit', new ParseIntPipe({ optional: true })) limit = 8, | |
| ) { | |
| return this.coursesService.getRecentActivity(id, limit); | |
| } | |
| (':id') | |
| ({ | |
| summary: 'Get course by ID', | |
| description: ` | |
| ## Get Course Details | |
| Retrieves detailed information about a specific course. | |
| ### Access Control | |
| - **Authentication Required**: No (Public endpoint) | |
| - **Roles Required**: None | |
| ### Response Includes | |
| - Course information (code, name, credits) | |
| - Prerequisites count | |
| - Available sections count | |
| `, | |
| }) | |
| ({ name: 'id', description: 'Course ID', type: Number }) | |
| ({ status: 200, description: 'Course details' }) | |
| ({ status: 404, description: 'Course not found' }) | |
| async findById(('id', ParseIntPipe) id: number) { | |
| const course = await this.coursesService.findById(id); | |
| const prerequisites = await this.coursesService.getPrerequisites(id); | |
| return { | |
| ...course, | |
| prerequisitesCount: prerequisites.length, | |
| sectionsCount: course.sections.length, | |
| }; | |
| } | |
| () | |
| (HttpStatus.CREATED) | |
| (JwtAuthGuard, RolesGuard) | |
| (RoleName.ADMIN, RoleName.IT_ADMIN) | |
| ('JWT-auth') | |
| ({ | |
| summary: 'Create new course', | |
| description: ` | |
| ## Create New Course | |
| Creates a new course in the catalog. | |
| ### Access Control | |
| - **Authentication Required**: ✅ Yes (Bearer Token recommended) | |
| - **Roles Required**: ADMIN, IT_ADMIN, INSTRUCTOR | |
| ### Required Fields | |
| - Course code (unique) | |
| - Course name | |
| - Credit hours | |
| - Department ID | |
| ### Notes | |
| - Course codes must be unique | |
| - New courses are active by default | |
| `, | |
| }) | |
| ({ type: CreateCourseDto }) | |
| ({ status: 201, description: 'Course created successfully' }) | |
| ({ status: 400, description: 'Invalid input data' }) | |
| ({ status: 409, description: 'Course code already exists' }) | |
| async create(() dto: CreateCourseDto) { | |
| return this.coursesService.create(dto); | |
| } | |
| (':id') | |
| (JwtAuthGuard, RolesGuard) | |
| (RoleName.ADMIN, RoleName.IT_ADMIN) | |
| ('JWT-auth') | |
| ({ | |
| summary: 'Update course', | |
| description: ` | |
| ## Update Course | |
| Updates an existing course's information. | |
| ### Access Control | |
| - **Authentication Required**: ✅ Yes (Bearer Token recommended) | |
| - **Roles Required**: ADMIN, IT_ADMIN, INSTRUCTOR | |
| `, | |
| }) | |
| ({ name: 'id', description: 'Course ID', type: Number }) | |
| ({ type: UpdateCourseDto }) | |
| ({ status: 200, description: 'Course updated successfully' }) | |
| ({ status: 400, description: 'Invalid input data' }) | |
| ({ status: 404, description: 'Course not found' }) | |
| async update( | |
| ('id', ParseIntPipe) id: number, | |
| () dto: UpdateCourseDto, | |
| ) { | |
| return this.coursesService.update(id, dto); | |
| } | |
| (':id') | |
| (HttpStatus.NO_CONTENT) | |
| (JwtAuthGuard, RolesGuard) | |
| (RoleName.ADMIN, RoleName.IT_ADMIN) | |
| ('JWT-auth') | |
| ({ | |
| summary: 'Delete course', | |
| description: ` | |
| ## Delete Course (Soft Delete) | |
| Soft deletes a course from the system. | |
| ### Access Control | |
| - **Authentication Required**: ✅ Yes (Bearer Token recommended) | |
| - **Roles Required**: ADMIN, IT_ADMIN | |
| ### Notes | |
| - This performs a soft delete (course is marked as inactive) | |
| - Courses with active enrollments cannot be deleted | |
| `, | |
| }) | |
| ({ name: 'id', description: 'Course ID', type: Number }) | |
| ({ status: 204, description: 'Course deleted successfully' }) | |
| ({ status: 400, description: 'Cannot delete course with enrollments' }) | |
| ({ status: 404, description: 'Course not found' }) | |
| async delete(('id', ParseIntPipe) id: number) { | |
| await this.coursesService.softDelete(id); | |
| } | |
| (':id/prerequisites') | |
| ({ | |
| summary: 'Get course prerequisites', | |
| description: ` | |
| ## Get Course Prerequisites | |
| Retrieves all prerequisite courses for a specific course. | |
| ### Access Control | |
| - **Authentication Required**: No (Public endpoint) | |
| - **Roles Required**: None | |
| ### Response | |
| Returns list of courses that must be completed before enrolling. | |
| `, | |
| }) | |
| ({ name: 'id', description: 'Course ID', type: Number }) | |
| ({ status: 200, description: 'List of prerequisite courses' }) | |
| ({ status: 404, description: 'Course not found' }) | |
| async getPrerequisites(('id', ParseIntPipe) id: number) { | |
| return this.coursesService.getPrerequisites(id); | |
| } | |
| (':id/prerequisites') | |
| (HttpStatus.CREATED) | |
| (JwtAuthGuard, RolesGuard) | |
| (RoleName.ADMIN, RoleName.IT_ADMIN) | |
| ('JWT-auth') | |
| ({ | |
| summary: 'Add prerequisite to course', | |
| description: ` | |
| ## Add Course Prerequisite | |
| Adds a prerequisite requirement to a course. | |
| ### Access Control | |
| - **Authentication Required**: ✅ Yes (Bearer Token recommended) | |
| - **Roles Required**: ADMIN, IT_ADMIN | |
| ### Notes | |
| - Cannot add circular prerequisites | |
| - A course cannot be its own prerequisite | |
| `, | |
| }) | |
| ({ name: 'id', description: 'Course ID', type: Number }) | |
| ({ type: CreatePrerequisiteDto }) | |
| ({ status: 201, description: 'Prerequisite added successfully' }) | |
| ({ status: 400, description: 'Invalid prerequisite or circular dependency' }) | |
| ({ status: 404, description: 'Course not found' }) | |
| async addPrerequisite( | |
| ('id', ParseIntPipe) id: number, | |
| () dto: CreatePrerequisiteDto, | |
| ) { | |
| return this.coursesService.addPrerequisite( | |
| id, | |
| dto.prerequisiteCourseId, | |
| dto.isMandatory, | |
| ); | |
| } | |
| (':id/prerequisites/:prereqId') | |
| (HttpStatus.NO_CONTENT) | |
| (JwtAuthGuard, RolesGuard) | |
| (RoleName.ADMIN, RoleName.IT_ADMIN) | |
| ('JWT-auth') | |
| ({ | |
| summary: 'Remove prerequisite from course', | |
| description: ` | |
| ## Remove Course Prerequisite | |
| Removes a prerequisite requirement from a course. | |
| ### Access Control | |
| - **Authentication Required**: ✅ Yes (Bearer Token recommended) | |
| - **Roles Required**: ADMIN, IT_ADMIN | |
| `, | |
| }) | |
| ({ name: 'id', description: 'Course ID', type: Number }) | |
| ({ name: 'prereqId', description: 'Prerequisite Course ID', type: Number }) | |
| ({ status: 204, description: 'Prerequisite removed successfully' }) | |
| ({ status: 404, description: 'Course or prerequisite not found' }) | |
| async removePrerequisite( | |
| ('id', ParseIntPipe) id: number, | |
| ('prereqId', ParseIntPipe) prereqId: number, | |
| ) { | |
| await this.coursesService.removePrerequisite(id, prereqId); | |
| } | |
| } | |