feat: Introduce core admin and instructor dashboards with dedicated services, pages, and layouts.
This commit is contained in:
parent
af14610442
commit
5442f1beb6
7 changed files with 497 additions and 7 deletions
|
|
@ -63,7 +63,7 @@
|
|||
<div class="mb-6">
|
||||
<q-input
|
||||
v-model="form.description.th"
|
||||
label="คำอธิบาย (ภาษาไทย)"
|
||||
label="คำอธิบาย (ภาษาไทย) *"
|
||||
type="textarea"
|
||||
outlined
|
||||
autogrow
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
<div class="mb-6">
|
||||
<q-input
|
||||
v-model="form.description.en"
|
||||
label="คำอธิบาย (English)"
|
||||
label="คำอธิบาย (English) *"
|
||||
type="textarea"
|
||||
outlined
|
||||
autogrow
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Stats Cards -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
<div class="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6">
|
||||
<div class="bg-white rounded-xl shadow-sm p-5 text-center">
|
||||
<div class="text-3xl font-bold text-primary-600">{{ stats.total }}</div>
|
||||
<div class="text-gray-500 text-sm mt-1">หลักสูตรทั้งหมด</div>
|
||||
|
|
@ -31,6 +31,10 @@
|
|||
<div class="text-3xl font-bold text-gray-500">{{ stats.draft }}</div>
|
||||
<div class="text-gray-500 text-sm mt-1">แบบร่าง</div>
|
||||
</div>
|
||||
<div class="bg-white rounded-xl shadow-sm p-5 text-center">
|
||||
<div class="text-3xl font-bold text-red-600">{{ stats.rejected }}</div>
|
||||
<div class="text-gray-500 text-sm mt-1">ถูกปฏิเสธ</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter Bar -->
|
||||
|
|
@ -126,7 +130,7 @@
|
|||
dense
|
||||
icon="visibility"
|
||||
color="grey"
|
||||
@click="navigateTo(`/instructor/courses/${course.id}`)"
|
||||
@click="handleViewDetails(course)"
|
||||
>
|
||||
<q-tooltip>ดูรายละเอียด</q-tooltip>
|
||||
</q-btn>
|
||||
|
|
@ -162,6 +166,36 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rejection Details Dialog -->
|
||||
<q-dialog v-model="rejectionDialog">
|
||||
<q-card style="min-width: 400px">
|
||||
<q-card-section class="row items-center q-pb-none">
|
||||
<div class="text-h6 text-red">หลักสูตรถูกปฏิเสธ</div>
|
||||
<q-space />
|
||||
<q-btn icon="close" flat round dense v-close-popup />
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section>
|
||||
<div class="text-subtitle1 font-bold mb-2">เหตุผลการปฏิเสธ:</div>
|
||||
<div class="bg-red-50 p-4 rounded-lg text-red-800 border border-red-100">
|
||||
{{ selectedRejectionCourse?.rejection_reason || 'ไม่ระบุเหตุผล' }}
|
||||
</div>
|
||||
<div class="text-gray-500 text-sm mt-4">
|
||||
คุณสามารถแก้ไขหลักสูตรและส่งขออนุมัติใหม่ได้ โดยการคืนสถานะเป็นแบบร่าง
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="text-primary q-pt-none q-pb-md q-px-md">
|
||||
<q-btn flat label="ยกเลิก" v-close-popup color="grey" />
|
||||
<q-btn
|
||||
label="คืนสถานะเป็นแบบร่าง"
|
||||
color="primary"
|
||||
@click="returnToDraft"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -196,7 +230,8 @@ const stats = computed(() => ({
|
|||
total: courses.value.length,
|
||||
approved: courses.value.filter(c => c.status === 'APPROVED').length,
|
||||
pending: courses.value.filter(c => c.status === 'PENDING').length,
|
||||
draft: courses.value.filter(c => c.status === 'DRAFT').length
|
||||
draft: courses.value.filter(c => c.status === 'DRAFT').length,
|
||||
rejected: courses.value.filter(c => c.status === 'REJECTED').length
|
||||
}));
|
||||
|
||||
// Filtered courses
|
||||
|
|
@ -296,6 +331,41 @@ const confirmDelete = (course: CourseResponse) => {
|
|||
});
|
||||
};
|
||||
|
||||
// Rejection Dialog
|
||||
const rejectionDialog = ref(false);
|
||||
const selectedRejectionCourse = ref<CourseResponse | null>(null);
|
||||
|
||||
const handleViewDetails = (course: CourseResponse) => {
|
||||
if (course.status === 'REJECTED') {
|
||||
selectedRejectionCourse.value = course;
|
||||
rejectionDialog.value = true;
|
||||
} else {
|
||||
navigateTo(`/instructor/courses/${course.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
const returnToDraft = async () => {
|
||||
if (!selectedRejectionCourse.value) return;
|
||||
|
||||
try {
|
||||
const response = await instructorService.setCourseDraft(selectedRejectionCourse.value.id);
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
message: response.message || 'คืนสถานะเป็นแบบร่างสำเร็จ',
|
||||
position: 'top'
|
||||
});
|
||||
rejectionDialog.value = false;
|
||||
selectedRejectionCourse.value = null;
|
||||
fetchCourses(); // Refresh list
|
||||
} catch (error: any) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: error.data?.message || 'ไม่สามารถคืนสถานะได้',
|
||||
position: 'top'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Lifecycle
|
||||
onMounted(() => {
|
||||
fetchCourses();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue