diff --git a/backend/src/lessons/dto/lesson-response.dto.ts b/backend/src/lessons/dto/lesson-response.dto.ts index 7c7ace7..488af15 100644 --- a/backend/src/lessons/dto/lesson-response.dto.ts +++ b/backend/src/lessons/dto/lesson-response.dto.ts @@ -37,6 +37,8 @@ export class LessonResponseDto { this.videoStartTimestamp = lesson.videoStartTimestamp || null; this.order = lesson.order; this.courseId = lesson.courseId; + this.hasQuiz = lesson.hasQuiz ?? false; + this.quizId = lesson.quizId ?? null; this.createdAt = lesson.createdAt; this.updatedAt = lesson.updatedAt; } diff --git a/backend/src/lessons/lessons.controller.ts b/backend/src/lessons/lessons.controller.ts index 3b8bbc4..e1cedda 100644 --- a/backend/src/lessons/lessons.controller.ts +++ b/backend/src/lessons/lessons.controller.ts @@ -13,7 +13,6 @@ import { } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger'; import { LessonsService } from './lessons.service'; - import { JwtAuthGuard } from '../common/guards/jwt-auth.guard'; import { RolesGuard } from '../common/guards/roles.guard'; import { PaginationDto } from '../common/dto/pagination.dto'; @@ -63,6 +62,7 @@ export class LessonsController { }; } + // NOTE: /course/:courseId must be declared BEFORE /:id to avoid shadowing @Get('course/:courseId') @ApiOperation({ summary: 'Get lessons for a specific course' }) @ApiResponse({ status: 200, description: 'Lessons retrieved successfully' }) @@ -95,7 +95,7 @@ export class LessonsController { @ApiResponse({ status: 200, description: 'Lesson details retrieved successfully', type: LessonResponseDto }) @ApiResponse({ status: 404, description: 'Lesson not found' }) async findOne(@Param('id') id: string): Promise { - const lesson = await this.lessonsService.findOne(id); + const lesson = await this.lessonsService.findOneWithQuizFlag(id); return new LessonResponseDto(lesson); } diff --git a/backend/src/lessons/lessons.module.ts b/backend/src/lessons/lessons.module.ts index 7e1557e..2dec2bd 100644 --- a/backend/src/lessons/lessons.module.ts +++ b/backend/src/lessons/lessons.module.ts @@ -3,13 +3,14 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { PassportModule } from '@nestjs/passport'; import { Course } from '../courses/entities/course.entity'; import { Lesson } from './entities/lesson.entity'; +import { Quiz } from '../quizzes/entities/quiz.entity'; import { LessonsController } from './lessons.controller'; import { LessonsService } from './lessons.service'; import { AuthModule } from '../auth/auth.module'; @Module({ imports: [ - TypeOrmModule.forFeature([Lesson, Course]), + TypeOrmModule.forFeature([Lesson, Course, Quiz]), PassportModule, AuthModule, ], diff --git a/backend/src/lessons/lessons.service.ts b/backend/src/lessons/lessons.service.ts index 9e9d529..fe5aa48 100644 --- a/backend/src/lessons/lessons.service.ts +++ b/backend/src/lessons/lessons.service.ts @@ -2,7 +2,7 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Course } from '../courses/entities/course.entity'; import { Lesson } from './entities/lesson.entity'; -import { Repository } from 'typeorm'; +import { In, Repository } from 'typeorm'; import { PaginationService } from '../common/services/pagination.service'; import { PaginatedResult } from '../common/services/pagination.service'; import { CreateLessonDto } from './dto/create-lesson.dto'; @@ -15,6 +15,8 @@ export class LessonsService { private lessonRepository: Repository, @InjectRepository(Course) private courseRepository: Repository, + @InjectRepository(Quiz) + private quizRepository: Repository, private readonly paginationService: PaginationService, ) {} @@ -76,7 +78,7 @@ export class LessonsService { courseId: string, page: number, limit: number, - ): Promise> { + ): Promise> { const course = await this.courseRepository.findOne({ where: { id: courseId }, }); @@ -85,7 +87,7 @@ export class LessonsService { throw new NotFoundException(`Course with ID ${courseId} not found`); } - return this.paginationService.paginate( + const result = await this.paginationService.paginate( this.lessonRepository, { page, limit }, { @@ -93,6 +95,21 @@ export class LessonsService { order: { order: 'ASC', createdAt: 'ASC' }, }, ); + + const lessonIds = result.data.map((l) => l.id); + const quizzes = lessonIds.length + ? await this.quizRepository.find({ where: { lessonId: In(lessonIds) }, select: ['id', 'lessonId'] }) + : []; + const quizMap = new Map(quizzes.map((q) => [q.lessonId, q.id])); + + return { + ...result, + data: result.data.map((l) => ({ + ...l, + hasQuiz: quizMap.has(l.id), + quizId: quizMap.get(l.id) ?? null, + })), + }; } async findOne(id: string): Promise { @@ -108,6 +125,21 @@ export class LessonsService { return lesson; } + async findOneWithQuizFlag(id: string): Promise { + const lesson = await this.lessonRepository.findOne({ + where: { id }, + relations: ['course'], + }); + + if (!lesson) { + throw new NotFoundException(`Lesson with ID ${id} not found`); + } + + const quiz = await this.quizRepository.findOne({ where: { lessonId: id }, select: ['id'] }); + + return { ...lesson, hasQuiz: !!quiz, quizId: quiz?.id ?? null }; + } + async update(id: string, updateLessonDto: UpdateLessonDto): Promise { const lesson = await this.lessonRepository.findOne({ where: { id },