elearning/frontend_management/components/course/StructureTab.vue
Missez 941b195813
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
feat: Introduce admin pages for pending course review and course details, and instructor pages for course management and lesson quizzes.
2026-02-10 15:40:03 +07:00

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>