From 350e3c27b36c9ad83db498d80b7873257f604ff1 Mon Sep 17 00:00:00 2001 From: supalerk-ar66 Date: Tue, 10 Feb 2026 11:21:29 +0700 Subject: [PATCH 1/2] feat: Add new classroom learning page with course content display, video playback, progress tracking, lesson access control, and course announcements. --- Frontend-Learner/pages/classroom/learning.vue | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/Frontend-Learner/pages/classroom/learning.vue b/Frontend-Learner/pages/classroom/learning.vue index baeda65c..7552b2a0 100644 --- a/Frontend-Learner/pages/classroom/learning.vue +++ b/Frontend-Learner/pages/classroom/learning.vue @@ -133,15 +133,23 @@ const resetAndNavigate = (path: string) => { } } -// Logic loadLesson แยกออกมา +// Logic loadLesson ให้ลื่นไหลแบบ SPA const handleLessonSelect = (lessonId: number) => { if (currentLesson.value?.id === lessonId) return - const url = new URL(window.location.href) - url.searchParams.set('lesson_id', lessonId.toString()) - resetAndNavigate(url.toString()) + + // 1. เปลี่ยน URL แบบนุ่มนวล + router.push({ query: { ...route.query, lesson_id: lessonId.toString() } }) + + // 2. โหลดเนื้อหาใหม่โดยไม่ Refresh หน้า + loadLesson(lessonId) + + // ปิด Sidebar บนมือถือเมื่อเลือกบทเรียน + if (sidebarOpen.value) { + sidebarOpen.value = false + } } -// Logic สำหรับการกดย้อนกลับหรืออกจากหน้าเรียน +// Logic สำหรับการกดย้อนกลับหรือออกจากหน้าเรียน (ใช้ Hard Reload ตามเดิม) const handleExit = (path: string) => { resetAndNavigate(path) } @@ -155,24 +163,25 @@ const handleExit = (path: string) => { const loadCourseData = async () => { if (!courseId.value) return isLoading.value = true - // Reset states before loading new course - courseData.value = null - currentLesson.value = null - announcements.value = [] try { const res = await fetchCourseLearningInfo(courseId.value) if (res.success) { courseData.value = res.data - // Auto-load logic: ถ้ายังไม่ได้เลือกบทเรียน ให้โหลดบทแรกที่ไม่ล็อคมาแสดง - if (!currentLesson.value) { - const firstChapter = res.data.chapters[0] - if (firstChapter && firstChapter.lessons.length > 0) { - // Find first unlocked or just first - const availableLesson = firstChapter.lessons.find((l: any) => !l.is_locked) || firstChapter.lessons[0] - loadLesson(availableLesson.id) - } + // Auto-load logic: เช็คจาก URL ก่อน ถ้าไม่มีค่อยหาบทแรก + const urlLessonId = route.query.lesson_id ? Number(route.query.lesson_id) : null + + if (urlLessonId) { + // ถ้ามีใน URL ให้โหลดบทนั้นเลย + loadLesson(urlLessonId) + } else if (!currentLesson.value) { + // ถ้าไม่มีใน URL ให้หาบทแรกที่ไม่ล็อค + const firstChapter = res.data.chapters[0] + if (firstChapter && firstChapter.lessons.length > 0) { + const availableLesson = firstChapter.lessons.find((l: any) => !l.is_locked) || firstChapter.lessons[0] + loadLesson(availableLesson.id) + } } // Fetch Announcements @@ -507,7 +516,7 @@ onBeforeUnmount(() => { - +
From 220dc0148d89a137a6ccdd2211b739a9d05139ad Mon Sep 17 00:00:00 2001 From: supalerk-ar66 Date: Tue, 10 Feb 2026 13:14:01 +0700 Subject: [PATCH 2/2] feat: add classroom learning interface with video playback, progress tracking, and a dedicated quiz page. --- Frontend-Learner/pages/classroom/learning.vue | 14 ++++++---- Frontend-Learner/pages/classroom/quiz.vue | 26 +++---------------- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/Frontend-Learner/pages/classroom/learning.vue b/Frontend-Learner/pages/classroom/learning.vue index 7552b2a0..9999f491 100644 --- a/Frontend-Learner/pages/classroom/learning.vue +++ b/Frontend-Learner/pages/classroom/learning.vue @@ -606,16 +606,20 @@ onBeforeUnmount(() => {

{{ getLocalizedText(currentLesson.description) }}

-
-
+
+

{{ $t('quiz.startTitle', 'แบบทดสอบท้ายบทเรียน') }}

{{ getLocalizedText(currentLesson.quiz?.description || currentLesson.description) }}

-
- {{ currentLesson.quiz.questions.length }} ข้อ - {{ currentLesson.quiz.time_limit }} นาที +
+ + {{ currentLesson.quiz.questions.length }} ข้อ + + + {{ currentLesson.quiz.time_limit }} นาที +
{ } // 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) => {
- +
@@ -466,25 +466,7 @@ const getCorrectChoiceId = (questionId: number) => {
-
-
- {{ $t('quiz.statusLabel', 'สถานะข้อสอบ') }} -
-
-
-
{{ $t('quiz.statusCurrent', 'Current') }} -
-
-
{{ $t('quiz.statusCompleted', 'Completed') }} -
-
-
{{ $t('quiz.statusSkipped', 'Skipped') }} -
-
-
{{ $t('quiz.statusNotStarted', 'Not Started') }} -
-
-
+
@@ -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) }" >