feat: Add new classroom learning page with course content display, video playback, progress tracking, lesson access control, and course announcements.

This commit is contained in:
supalerk-ar66 2026-02-10 11:21:29 +07:00
parent 8c495f3871
commit 350e3c27b3

View file

@ -133,15 +133,23 @@ const resetAndNavigate = (path: string) => {
} }
} }
// Logic loadLesson // Logic loadLesson SPA
const handleLessonSelect = (lessonId: number) => { const handleLessonSelect = (lessonId: number) => {
if (currentLesson.value?.id === lessonId) return if (currentLesson.value?.id === lessonId) return
const url = new URL(window.location.href)
url.searchParams.set('lesson_id', lessonId.toString()) // 1. URL
resetAndNavigate(url.toString()) 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) => { const handleExit = (path: string) => {
resetAndNavigate(path) resetAndNavigate(path)
} }
@ -155,24 +163,25 @@ const handleExit = (path: string) => {
const loadCourseData = async () => { const loadCourseData = async () => {
if (!courseId.value) return if (!courseId.value) return
isLoading.value = true isLoading.value = true
// Reset states before loading new course
courseData.value = null
currentLesson.value = null
announcements.value = []
try { try {
const res = await fetchCourseLearningInfo(courseId.value) const res = await fetchCourseLearningInfo(courseId.value)
if (res.success) { if (res.success) {
courseData.value = res.data courseData.value = res.data
// Auto-load logic: // Auto-load logic: URL
if (!currentLesson.value) { const urlLessonId = route.query.lesson_id ? Number(route.query.lesson_id) : null
const firstChapter = res.data.chapters[0]
if (firstChapter && firstChapter.lessons.length > 0) { if (urlLessonId) {
// Find first unlocked or just first // URL
const availableLesson = firstChapter.lessons.find((l: any) => !l.is_locked) || firstChapter.lessons[0] loadLesson(urlLessonId)
loadLesson(availableLesson.id) } 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 // Fetch Announcements
@ -507,7 +516,7 @@ onBeforeUnmount(() => {
<!-- Header --> <!-- Header -->
<q-header bordered class="bg-[var(--bg-surface)] border-b border-gray-200 dark:border-white/5 text-[var(--text-main)] h-14"> <q-header bordered class="bg-[var(--bg-surface)] border-b border-gray-200 dark:border-white/5 text-[var(--text-main)] h-14">
<q-toolbar> <q-toolbar>
<q-btn flat round dense icon="menu" class="lg:hidden mr-2 text-slate-900 dark:text-white" @click="toggleSidebar" /> <q-btn flat round dense icon="menu" class="mr-2 text-slate-900 dark:text-white hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors" @click="toggleSidebar" />
<!-- Back Button & Branding --> <!-- Back Button & Branding -->
<div class="flex items-center gap-2 mr-6"> <div class="flex items-center gap-2 mr-6">