All checks were successful
Build and Deploy Frontend Management to Dev Server / Build Frontend Management Docker Image (push) Successful in 33s
Build and Deploy Frontend Management to Dev Server / Deploy E-learning Frontend Management to Dev Server (push) Successful in 2s
Build and Deploy Frontend Management to Dev Server / Notify Deployment Status (push) Successful in 1s
132 lines
3.9 KiB
Vue
132 lines
3.9 KiB
Vue
<template>
|
|
<div>
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h2 class="text-xl font-semibold text-gray-900">โครงสร้างบทเรียน</h2>
|
|
<q-btn
|
|
v-if="course.status === 'DRAFT'"
|
|
color="primary"
|
|
label="จัดการโครงสร้าง"
|
|
@click="navigateTo(`/instructor/courses/${course.id}/structure`)"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Chapters -->
|
|
<div v-if="course.chapters.length === 0" class="text-center py-10 text-gray-500">
|
|
ยังไม่มีบทเรียน
|
|
</div>
|
|
|
|
<div v-else class="space-y-4">
|
|
<q-card
|
|
v-for="chapter in sortedChapters"
|
|
:key="chapter.id"
|
|
flat
|
|
bordered
|
|
class="rounded-lg"
|
|
>
|
|
<q-card-section>
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<div class="font-semibold text-gray-900">
|
|
Chapter {{ chapter.sort_order }}: {{ chapter.title.th }}
|
|
</div>
|
|
<div class="text-sm text-gray-500 mt-1">
|
|
{{ chapter.lessons.length }} บทเรียน
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</q-card-section>
|
|
|
|
<!-- Lessons -->
|
|
<q-list separator class="border-t">
|
|
<q-item
|
|
v-for="lesson in getSortedLessons(chapter)"
|
|
:key="lesson.id"
|
|
class="py-3"
|
|
:class="{ 'cursor-pointer hover:bg-gray-50': course.status === 'APPROVED' }"
|
|
:clickable="course.status === 'APPROVED'"
|
|
:v-ripple="course.status === 'APPROVED'"
|
|
@click="handleLessonClick(lesson)"
|
|
>
|
|
<q-item-section avatar>
|
|
<q-icon
|
|
:name="getLessonIcon(lesson.type)"
|
|
:color="getLessonIconColor(lesson.type)"
|
|
/>
|
|
</q-item-section>
|
|
<q-item-section>
|
|
<q-item-label>
|
|
Lesson {{ chapter.sort_order }}.{{ lesson.sort_order }}: {{ lesson.title.th }}
|
|
</q-item-label>
|
|
</q-item-section>
|
|
|
|
<q-item-section side v-if="course.status === 'APPROVED'">
|
|
<q-icon name="visibility" size="xs" color="grey-5" />
|
|
</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
</q-card>
|
|
</div>
|
|
|
|
<!-- Lesson Preview Dialog -->
|
|
<LessonPreviewDialog
|
|
v-if="previewLesson"
|
|
v-model="showPreview"
|
|
:lesson="previewLesson"
|
|
:course-id="course.id"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { CourseDetailResponse, ChapterResponse, LessonResponse } from '~/services/instructor.service';
|
|
import LessonPreviewDialog from './LessonPreviewDialog.vue';
|
|
|
|
interface Props {
|
|
course: CourseDetailResponse;
|
|
}
|
|
|
|
const props = defineProps<Props>();
|
|
|
|
// Computed
|
|
const sortedChapters = computed(() => {
|
|
return props.course.chapters.slice().sort((a, b) => a.sort_order - b.sort_order);
|
|
});
|
|
|
|
// Methods
|
|
const getChapterDuration = (chapter: ChapterResponse) => {
|
|
return chapter.lessons.reduce((sum, l) => sum + l.duration_minutes, 0);
|
|
};
|
|
|
|
const getSortedLessons = (chapter: ChapterResponse) => {
|
|
return chapter.lessons.slice().sort((a, b) => a.sort_order - b.sort_order);
|
|
};
|
|
|
|
const getLessonIcon = (type: string) => {
|
|
const icons: Record<string, string> = {
|
|
VIDEO: 'play_circle',
|
|
DOCUMENT: 'description',
|
|
QUIZ: 'quiz'
|
|
};
|
|
return icons[type] || 'article';
|
|
};
|
|
|
|
const getLessonIconColor = (type: string) => {
|
|
const colors: Record<string, string> = {
|
|
VIDEO: 'blue',
|
|
DOCUMENT: 'orange',
|
|
QUIZ: 'orange'
|
|
};
|
|
return colors[type] || 'grey';
|
|
};
|
|
|
|
// Preview
|
|
const showPreview = ref(false);
|
|
const previewLesson = ref<LessonResponse | null>(null);
|
|
|
|
const handleLessonClick = (lesson: LessonResponse) => {
|
|
if (props.course.status === 'APPROVED') {
|
|
previewLesson.value = lesson;
|
|
showPreview.value = true;
|
|
}
|
|
};
|
|
</script>
|