diff --git a/Frontend-Learner/components/classroom/CurriculumSidebar.vue b/Frontend-Learner/components/classroom/CurriculumSidebar.vue index a7ccddab..382be584 100644 --- a/Frontend-Learner/components/classroom/CurriculumSidebar.vue +++ b/Frontend-Learner/components/classroom/CurriculumSidebar.vue @@ -21,15 +21,40 @@ const emit = defineEmits<{ const { locale } = useI18n() +// State for expansion items +const chapterOpenState = ref>({}) + // Helper for localization const getLocalizedText = (text: any) => { if (!text) return '' if (typeof text === 'string') return text - const currentLocale = locale.value as 'th' | 'en' + // Safe locale access + const currentLocale = (locale?.value || 'th') as 'th' | 'en' return text[currentLocale] || text.th || text.en || '' } +// Helper: Check if lesson is completed +const isLessonCompleted = (lesson: any) => { + return lesson.is_completed === true || lesson.progress?.is_completed === true +} + +// Reactive Chapter Completion Status +// Computes a map of chapterId -> boolean (true if all lessons are completed) +const chapterCompletionStatus = computed(() => { + const status: Record = {} + if (!props.courseData || !props.courseData.chapters) return status + + props.courseData.chapters.forEach((chapter: any) => { + if (chapter.lessons && chapter.lessons.length > 0) { + status[chapter.id] = chapter.lessons.every((l: any) => isLessonCompleted(l)) + } else { + status[chapter.id] = false + } + }) + return status +}) + // Local Progress Calculation const progressPercentage = computed(() => { if (!props.courseData || !props.courseData.chapters) return 0 @@ -38,11 +63,34 @@ const progressPercentage = computed(() => { props.courseData.chapters.forEach((c: any) => { c.lessons.forEach((l: any) => { total++ - if (l.is_completed || l.progress?.is_completed) completed++ + if (isLessonCompleted(l)) completed++ }) }) return total > 0 ? Math.round((completed / total) * 100) : 0 }) + +// Auto-expand chapter containing current lesson +watch(() => props.currentLessonId, (newId) => { + if (newId && props.courseData?.chapters) { + props.courseData.chapters.forEach((chapter: any) => { + const hasLesson = chapter.lessons.some((l: any) => l.id === newId) + if (hasLesson) { + chapterOpenState.value[chapter.id] = true + } + }) + } +}, { immediate: true }) + +// Initialize all chapters as open by default on load +watch(() => props.courseData, (newData) => { + if (newData?.chapters) { + newData.chapters.forEach((chapter: any) => { + if (chapterOpenState.value[chapter.id] === undefined) { + chapterOpenState.value[chapter.id] = true + } + }) + } +}, { immediate: true }) diff --git a/Frontend-Learner/components/discovery/CourseDetailView.vue b/Frontend-Learner/components/discovery/CourseDetailView.vue index fe0146bb..0d26ce87 100644 --- a/Frontend-Learner/components/discovery/CourseDetailView.vue +++ b/Frontend-Learner/components/discovery/CourseDetailView.vue @@ -33,6 +33,19 @@ const formatPrice = (price: number) => { } const enrollmentLoading = ref(false); +const activeTab = ref('curriculum'); + +const totalLessons = computed(() => { + if (!props.course?.chapters) return 0; + return props.course.chapters.reduce((acc: number, chapter: any) => acc + (chapter.lessons?.length || 0), 0); +}); + +const totalDuration = computed(() => { + if (!props.course?.chapters) return 0; + return props.course.chapters.reduce((acc: number, chapter: any) => { + return acc + (chapter.lessons?.reduce((lAcc: number, lesson: any) => lAcc + (lesson.duration_minutes || 0), 0) || 0); + }, 0); +}); const handleEnroll = () => { if(!props.course) return; @@ -42,7 +55,6 @@ const handleEnroll = () => { // In this pattern, we just emit. setTimeout(() => enrollmentLoading.value = false, 2000); // Safety timeout }; -