feat: add classroom learning interface with video playback, progress tracking, and a dedicated quiz page.
All checks were successful
Build and Deploy Frontend Learner / Build Frontend Learner Docker Image (push) Successful in 52s
Build and Deploy Frontend Learner / Deploy E-learning Frontend Learner to Dev Server (push) Successful in 3s
Build and Deploy Frontend Learner / Notify Deployment Status (push) Successful in 1s

This commit is contained in:
supalerk-ar66 2026-02-10 13:14:01 +07:00
parent 350e3c27b3
commit 220dc0148d
2 changed files with 13 additions and 27 deletions

View file

@ -606,16 +606,20 @@ onBeforeUnmount(() => {
<p class="text-slate-600 dark:text-slate-400 text-base md:text-lg leading-relaxed mb-6" v-if="currentLesson.description">{{ getLocalizedText(currentLesson.description) }}</p>
<!-- Lesson Content Area (Text/HTML) -->
<div v-if="currentLesson.type === 'QUIZ'" class="p-8 bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-slate-800 dark:to-slate-800/50 rounded-2xl border border-blue-100 dark:border-white/5 text-center">
<div class="bg-white dark:bg-slate-700 w-20 h-20 rounded-full flex items-center justify-center mx-auto mb-4 shadow-sm text-blue-500 dark:text-blue-300">
<div v-if="currentLesson.type === 'QUIZ'" class="p-8 bg-gradient-to-br from-blue-50/50 to-indigo-50/50 dark:from-slate-800/50 dark:to-slate-900/50 rounded-2xl border border-blue-100 dark:border-white/5 text-center">
<div class="bg-white dark:bg-slate-800 w-20 h-20 rounded-full flex items-center justify-center mx-auto mb-4 shadow-sm text-blue-500 dark:text-blue-400 border dark:border-white/10">
<q-icon name="quiz" size="40px" />
</div>
<h2 class="text-xl font-bold mb-2 text-slate-900 dark:text-white">{{ $t('quiz.startTitle', 'แบบทดสอบท้ายบทเรียน') }}</h2>
<p class="text-slate-500 dark:text-slate-400 mb-6 max-w-md mx-auto">{{ getLocalizedText(currentLesson.quiz?.description || currentLesson.description) }}</p>
<div class="flex justify-center flex-wrap gap-3 text-sm text-slate-600 dark:text-slate-300 mb-8">
<span v-if="currentLesson.quiz?.questions?.length" class="px-3 py-1 bg-white dark:bg-slate-900 rounded-full border border-gray-200 dark:border-white/10 shadow-sm flex items-center gap-1.5"><q-icon name="format_list_numbered" size="14px" class="text-blue-500" /> {{ currentLesson.quiz.questions.length }} </span>
<span v-if="currentLesson.quiz?.time_limit" class="px-3 py-1 bg-white dark:bg-slate-900 rounded-full border border-gray-200 dark:border-white/10 shadow-sm flex items-center gap-1.5"><q-icon name="schedule" size="14px" class="text-orange-500" /> {{ currentLesson.quiz.time_limit }} นาท</span>
<div class="flex justify-center flex-wrap gap-3 text-sm mb-8">
<span v-if="currentLesson.quiz?.questions?.length" class="px-3 py-1 bg-white dark:bg-white rounded-full border border-gray-200 dark:border-white/10 shadow-sm flex items-center gap-1.5 text-slate-900 font-bold">
<q-icon name="format_list_numbered" size="14px" class="text-blue-600" /> {{ currentLesson.quiz.questions.length }}
</span>
<span v-if="currentLesson.quiz?.time_limit" class="px-3 py-1 bg-white dark:bg-white rounded-full border border-gray-200 dark:border-white/10 shadow-sm flex items-center gap-1.5 text-slate-900 font-bold">
<q-icon name="schedule" size="14px" class="text-orange-600" /> {{ currentLesson.quiz.time_limit }} นาท
</span>
</div>
<q-btn

View file

@ -65,7 +65,7 @@ const getQuestionStatusClass = (index: number, questionId: number) => {
}
// 4. Not Started = Grey
return 'bg-slate-200 text-slate-400 border-slate-300 dark:bg-white/5 dark:border-white/10 dark:text-slate-500 hover:bg-slate-300 dark:hover:bg-white/10'
return 'bg-slate-200 text-slate-400 border-slate-300 dark:bg-white/5 dark:border-white/5 dark:text-slate-600 hover:bg-slate-300 dark:hover:bg-white/10'
}
const jumpToQuestion = (targetIndex: number) => {
@ -377,7 +377,7 @@ const getCorrectChoiceId = (questionId: number) => {
<div class="flex justify-center mb-10">
<div class="w-20 h-20 rounded-3xl bg-blue-50 dark:bg-blue-500/10 border border-blue-100 dark:border-blue-500/20 flex items-center justify-center shadow-inner">
<q-icon name="quiz" size="2rem" color="primary" />
<q-icon name="quiz" size="2.5rem" color="primary" class="dark:text-blue-400" />
</div>
</div>
@ -466,25 +466,7 @@ const getCorrectChoiceId = (questionId: number) => {
</button>
</div>
<div class="mb-8 px-2">
<div class="text-xs font-bold text-slate-400 uppercase tracking-widest mb-3">
{{ $t('quiz.statusLabel', 'สถานะข้อสอบ') }}
</div>
<div class="flex flex-wrap items-center gap-4">
<div class="flex items-center gap-2 text-sm font-medium text-slate-500 dark:text-slate-400">
<div class="w-2.5 h-2.5 rounded-full bg-blue-500 ring-2 ring-blue-100 dark:ring-blue-900/30"></div> {{ $t('quiz.statusCurrent', 'Current') }}
</div>
<div class="flex items-center gap-2 text-sm font-medium text-slate-500 dark:text-slate-400">
<div class="w-2.5 h-2.5 rounded-full bg-emerald-500"></div> {{ $t('quiz.statusCompleted', 'Completed') }}
</div>
<div class="flex items-center gap-2 text-sm font-medium text-slate-500 dark:text-slate-400">
<div class="w-2.5 h-2.5 rounded-full bg-orange-500"></div> {{ $t('quiz.statusSkipped', 'Skipped') }}
</div>
<div class="flex items-center gap-2 text-sm font-medium text-slate-500 dark:text-slate-400">
<div class="w-2.5 h-2.5 rounded-full bg-slate-200 dark:bg-slate-700"></div> {{ $t('quiz.statusNotStarted', 'Not Started') }}
</div>
</div>
</div>
<!-- Controls -->
<div class="flex justify-between items-center pt-8 border-t border-slate-100 dark:border-white/5">
@ -602,7 +584,7 @@ const getCorrectChoiceId = (questionId: number) => {
:class="{
'border-emerald-500 bg-emerald-50 dark:bg-emerald-500/10': choice.id === getCorrectChoiceId(question.id),
'border-red-500 bg-red-50 dark:bg-red-500/10': userAnswers[question.id] === choice.id && choice.id !== getCorrectChoiceId(question.id),
'border-slate-100 dark:border-white/5 opacity-60': userAnswers[question.id] !== choice.id && choice.id !== getCorrectChoiceId(question.id)
'border-slate-100 dark:border-white/5 opacity-80 dark:opacity-40': userAnswers[question.id] !== choice.id && choice.id !== getCorrectChoiceId(question.id)
}"
>
<!-- Indicator Icon -->