elearning/Frontend-Learner/pages/course/[id].vue

140 lines
4.9 KiB
Vue

<script setup lang="ts">
/**
* @file [id].vue
* @description Dynamic Course Detail Page.
* Displays detailed information about a specific course based on the ID.
*/
definePageMeta({
layout: 'default',
middleware: 'auth'
})
const route = useRoute()
// ดึง courseId จาก URL params (แปลงเป็น integer)
const courseId = computed(() => parseInt(route.params.id as string))
const { currentUser } = useAuth()
const $q = useQuasar()
const { fetchCourseById, enrollCourse, getLocalizedText } = useCourse()
// ใช้ useAsyncData ดึงข้อมูลคอร์ส Server-side rendering (SSR)
// Key: 'course-{id}' เพื่อให้ cache แยกกันตาม ID
const { data: courseData, error, refresh } = await useAsyncData(`course-${courseId.value}`, () => fetchCourseById(courseId.value))
const course = computed(() => {
return courseData.value?.success ? courseData.value.data : null
})
const isEnrolling = ref(false)
// ฟังก์ชันสำหรับกดปุ่ม "ลงทะเบียนเรียน"
const handleEnroll = async () => {
if (!course.value) return
if (isEnrolling.value) return
// กรณีเคยกดลงทะเบียนไปแล้ว (Check จากสถานะคอร์ส)
if (course.value.enrolled) {
$q.dialog({
message: `<div class="text-slate-800 text-base leading-relaxed">ท่านเคยลงทะเบียนคอร์ส <b class="text-blue-600">"${getLocalizedText(course.value.title)}"</b> นี้ไปเรียบร้อยแล้ว</div>`,
html: true,
ok: {
label: 'ตกลง',
color: 'primary',
rounded: true,
unelevated: true,
padding: '8px 32px'
}
})
return
}
isEnrolling.value = true
// เรียก API ลงทะเบียนเรียน
const res = await enrollCourse(course.value.id)
if (res.success) {
// ถ้าสำเร็จ ให้เปลี่ยนหน้าไปที่ "คอร์สของฉัน" พร้อม params enrolled=true
const targetId = route.params.id || course.value?.id
return navigateTo({
path: '/dashboard/my-courses',
query: {
enrolled: 'true',
course_id: String(targetId)
}
})
} else {
// กรณี API แจ้งว่าเคยลงทะเบียนไปแล้ว (Code 409)
if (res.code === 409) {
$q.dialog({
message: `<div class="text-slate-800 text-base leading-relaxed">ท่านเคยลงทะเบียนคอร์ส <b class="text-blue-600">"${getLocalizedText(course.value.title)}"</b> นี้ไปเรียบร้อยแล้ว</div>`,
html: true,
ok: {
label: 'ตกลง',
color: 'primary',
rounded: true,
unelevated: true,
padding: '8px 32px'
}
})
} else {
// กรณี error ทั่วไป แสดง Toast notification
$q.notify({
type: 'negative',
message: res.error || 'Failed to enroll',
position: 'top',
timeout: 3000,
actions: [{ icon: 'close', color: 'white' }]
})
}
}
isEnrolling.value = false
}
useHead({
title: computed(() => course.value ? `${getLocalizedText(course.value.title)} - E-Learning` : 'Course Details')
})
</script>
<template>
<div class="page-container">
<div v-if="course" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<CourseDetailView
:course="course"
:user="currentUser"
@back="navigateTo('/browse/discovery')"
@enroll="handleEnroll"
/>
</div>
<!-- Loading / Error State -->
<div v-else class="text-center py-20">
<div v-if="error" class="text-red-500 mb-4">
<p class="font-bold">เกดขอผดพลาดในการโหลดขอม</p>
<p class="text-sm opacity-80">{{ error.message }}</p>
</div>
<div v-else class="flex flex-col items-center gap-4 text-slate-400">
<div class="w-10 h-10 border-4 border-blue-500/30 border-t-blue-500 rounded-full animate-spin"/>
<p>กำลงโหลด...</p>
</div>
</div>
</div>
</template>
<style scoped>
@keyframes pulse-slow {
0%, 100% { opacity: 0.1; transform: scale(1); }
50% { opacity: 0.15; transform: scale(1.15); }
}
.animate-pulse-slow {
animation: pulse-slow 10s linear infinite;
}
</style>