From df750723af4629fb338ca0e2bf6bee38bbab9660 Mon Sep 17 00:00:00 2001 From: JakkrapartXD Date: Wed, 11 Feb 2026 14:36:54 +0700 Subject: [PATCH] feat: add quiz attempt logic to getlessonContent - Check allow_multiple_attempts setting when returning quiz data - If allow_multiple_attempts is false AND user has attempted: - Return only quiz metadata and latest score - Do NOT return questions - If allow_multiple_attempts is true OR user has not attempted: - Return full quiz with questions - Include latest_attempt data if exists - Add latest_attempt field to quiz response with score and attempt info --- .../src/services/CoursesStudent.service.ts | 51 +++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/Backend/src/services/CoursesStudent.service.ts b/Backend/src/services/CoursesStudent.service.ts index d7c23190..87679ab3 100644 --- a/Backend/src/services/CoursesStudent.service.ts +++ b/Backend/src/services/CoursesStudent.service.ts @@ -583,6 +583,27 @@ export class CoursesStudentService { logger.error(`Failed to list attachments from MinIO: ${err}`); } + // Check quiz attempts if this is a QUIZ lesson + let latestQuizAttempt = null; + let shouldReturnQuestions = true; + + if (lesson.quiz) { + // Get latest quiz attempt for this user + latestQuizAttempt = await prisma.quizAttempt.findFirst({ + where: { + user_id: decoded.id, + quiz_id: lesson.quiz.id, + }, + orderBy: { + started_at: 'desc', + }, + }); + + // If allow_multiple_attempts is false AND user has attempted + if (!lesson.quiz.allow_multiple_attempts && latestQuizAttempt) { + shouldReturnQuestions = false; + } + } return { code: 200, @@ -599,7 +620,7 @@ export class CoursesStudentService { require_pass_quiz: lesson.require_pass_quiz, video_url, // Presigned URL for video attachments: attachmentsWithUrls, - quiz: lesson.quiz ? { + quiz: lesson.quiz ? (shouldReturnQuestions ? { id: lesson.quiz.id, title: lesson.quiz.title as { th: string; en: string }, description: lesson.quiz.description as { th: string; en: string } | null, @@ -609,6 +630,14 @@ export class CoursesStudentService { shuffle_choices: lesson.quiz.shuffle_choices, is_skippable: lesson.quiz.is_skippable, show_answers_after_completion: lesson.quiz.show_answers_after_completion, + allow_multiple_attempts: lesson.quiz.allow_multiple_attempts, + latest_attempt: latestQuizAttempt ? { + score: latestQuizAttempt.score, + is_passed: latestQuizAttempt.is_passed, + attempt_number: latestQuizAttempt.attempt_number, + started_at: latestQuizAttempt.started_at, + completed_at: latestQuizAttempt.completed_at, + } : undefined, questions: lesson.quiz.questions.map(q => ({ id: q.id, question: q.question as { th: string; en: string }, @@ -621,7 +650,21 @@ export class CoursesStudentService { sort_order: c.sort_order, })), })), - } : null, + } : { + // Only return quiz metadata and latest score, no questions + id: lesson.quiz.id, + title: lesson.quiz.title as { th: string; en: string }, + description: lesson.quiz.description as { th: string; en: string } | null, + passing_score: lesson.quiz.passing_score, + allow_multiple_attempts: lesson.quiz.allow_multiple_attempts, + latest_attempt: latestQuizAttempt ? { + score: latestQuizAttempt.score, + is_passed: latestQuizAttempt.is_passed, + attempt_number: latestQuizAttempt.attempt_number, + started_at: latestQuizAttempt.started_at, + completed_at: latestQuizAttempt.completed_at, + } : undefined, + }) : null, prev_lesson_id: prevLessonId, next_lesson_id: nextLessonId, }, @@ -1374,8 +1417,8 @@ export class CoursesStudentService { })); // Find best score and latest attempt - const bestScore = attempts.length > 0 - ? Math.max(...attempts.map(a => a.score)) + const bestScore = attempts.length > 0 + ? Math.max(...attempts.map(a => a.score)) : null; const latestAttempt = attemptsData.length > 0 ? attemptsData[0] : null;