All checks were successful
Build and Deploy Frontend Learner / Build Frontend Learner Docker Image (push) Successful in 33s
Build and Deploy Frontend Learner / Deploy E-learning Frontend Learner to Dev Server (push) Successful in 3s
Build and Deploy Frontend Learner / Notify Deployment Status (push) Successful in 1s
141 lines
4.8 KiB
Vue
141 lines
4.8 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 { t } = useI18n()
|
|
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">${t('enrollment.alreadyEnrolledHint', { course: `<b class="text-blue-600">"${getLocalizedText(course.value.title)}"</b>` })}</div>`,
|
|
html: true,
|
|
ok: {
|
|
label: t('common.ok'),
|
|
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">${t('enrollment.alreadyEnrolledHint', { course: `<b class="text-blue-600">"${getLocalizedText(course.value.title)}"</b>` })}</div>`,
|
|
html: true,
|
|
ok: {
|
|
label: t('common.ok'),
|
|
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>
|