feat: Implement instructor course management, including student progress tracking and course content views, alongside admin user management.
This commit is contained in:
parent
c9381b9385
commit
be5b9756be
11 changed files with 2116 additions and 1386 deletions
107
frontend_management/components/course/StructureTab.vue
Normal file
107
frontend_management/components/course/StructureTab.vue
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-xl font-semibold text-gray-900">โครงสร้างบทเรียน</h2>
|
||||
<q-btn
|
||||
color="primary"
|
||||
label="จัดการโครงสร้าง"
|
||||
@click="navigateTo(`/instructor/courses/${courseId}/structure`)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Chapters -->
|
||||
<div v-if="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 }} บทเรียน · {{ getChapterDuration(chapter) }} นาที
|
||||
</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"
|
||||
>
|
||||
<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>
|
||||
<span class="text-sm text-gray-500">{{ lesson.duration_minutes }} นาที</span>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ChapterResponse } from '~/services/instructor.service';
|
||||
|
||||
interface Props {
|
||||
courseId: number;
|
||||
chapters: ChapterResponse[];
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
// Computed
|
||||
const sortedChapters = computed(() => {
|
||||
return props.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';
|
||||
};
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue