86 lines
3.1 KiB
Vue
86 lines
3.1 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* @file CurriculumSidebar.vue
|
|
* @description Sidebar Component for displaying course curriculum (Chapters & Lessons)
|
|
* Handles lesson navigation, locked status display, and unread announcement badge.
|
|
*/
|
|
|
|
const props = defineProps<{
|
|
modelValue: boolean; // Sidebar open state (v-model)
|
|
courseData: any;
|
|
currentLessonId?: number;
|
|
isLoading: boolean;
|
|
hasUnreadAnnouncements: boolean;
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:modelValue', value: boolean): void;
|
|
(e: 'select-lesson', lessonId: number): void;
|
|
(e: 'open-announcements'): void;
|
|
}>();
|
|
|
|
// Helper for localization
|
|
const getLocalizedText = (text: any) => {
|
|
if (!text) return ''
|
|
if (typeof text === 'string') return text
|
|
return text.th || text.en || ''
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<q-drawer
|
|
:model-value="modelValue"
|
|
@update:model-value="(val) => emit('update:modelValue', val)"
|
|
show-if-above
|
|
bordered
|
|
side="left"
|
|
:width="320"
|
|
:breakpoint="1024"
|
|
class="bg-[var(--bg-surface)]"
|
|
content-class="bg-[var(--bg-surface)]"
|
|
>
|
|
<div v-if="courseData" class="h-full scroll">
|
|
<q-list class="pb-10">
|
|
|
|
|
|
<template v-for="chapter in courseData.chapters" :key="chapter.id">
|
|
<q-item-label header class="bg-slate-100 dark:bg-slate-800 text-[var(--text-main)] font-bold sticky top-0 z-10 border-b dark:border-white/5 text-sm py-4">
|
|
{{ getLocalizedText(chapter.title) }}
|
|
</q-item-label>
|
|
|
|
<q-item
|
|
v-for="lesson in chapter.lessons"
|
|
:key="lesson.id"
|
|
clickable
|
|
v-ripple
|
|
:active="currentLessonId === lesson.id"
|
|
active-class="bg-blue-50 text-blue-700 dark:bg-blue-900/20 dark:text-blue-200 font-medium"
|
|
class="border-b border-gray-50 dark:border-white/5"
|
|
@click="!lesson.is_locked && emit('select-lesson', lesson.id)"
|
|
:disable="lesson.is_locked"
|
|
>
|
|
<q-item-section avatar v-if="lesson.is_locked">
|
|
<q-icon name="lock" size="xs" color="grey" />
|
|
</q-item-section>
|
|
|
|
<q-item-section>
|
|
<q-item-label class="text-sm md:text-base line-clamp-2 text-[var(--text-main)]">
|
|
{{ getLocalizedText(lesson.title) }}
|
|
</q-item-label>
|
|
</q-item-section>
|
|
|
|
<q-item-section side>
|
|
<q-icon v-if="lesson.is_completed || lesson.progress?.is_completed" name="check_circle" color="positive" size="xs" />
|
|
<q-icon v-else-if="currentLessonId === lesson.id" name="play_circle" color="primary" size="xs" />
|
|
<q-icon v-else name="radio_button_unchecked" color="grey-4" size="xs" />
|
|
</q-item-section>
|
|
</q-item>
|
|
</template>
|
|
</q-list>
|
|
</div>
|
|
<div v-else-if="isLoading" class="p-6 text-center text-slate-500">
|
|
<q-spinner color="primary" size="2em" />
|
|
<div class="mt-2 text-xs">{{ $t('classroom.loadingCurriculum') }}</div>
|
|
</div>
|
|
</q-drawer>
|
|
</template>
|