145 lines
6.6 KiB
Vue
145 lines
6.6 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.
|
|
*/
|
|
|
|
const route = useRoute()
|
|
const courseId = computed(() => parseInt(route.params.id as string))
|
|
const { fetchCourseById } = useCourse()
|
|
|
|
// Fetch course data
|
|
const { data: courseData, error } = await useAsyncData(`course-${courseId.value}`, () => fetchCourseById(courseId.value))
|
|
|
|
const course = computed(() => {
|
|
return courseData.value?.success ? courseData.value.data : null
|
|
})
|
|
|
|
// Helper for localization
|
|
const getLocalizedText = (text: string | { th: string; en: string } | undefined | null) => {
|
|
if (!text) return ''
|
|
if (typeof text === 'string') return text
|
|
return text.th || text.en || ''
|
|
}
|
|
|
|
definePageMeta({
|
|
layout: 'landing',
|
|
auth: false // Explicitly public? Or check logic.
|
|
})
|
|
|
|
useHead({
|
|
title: computed(() => course.value ? `${getLocalizedText(course.value.title)} - E-Learning` : 'Course Details')
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="relative min-h-screen text-slate-200 bg-slate-900 transition-colors pt-32 pb-20">
|
|
|
|
<!-- Background Effects -->
|
|
<div class="fixed inset-0 overflow-hidden pointer-events-none -z-10">
|
|
<div class="absolute top-[-20%] right-[-10%] w-[60%] h-[60%] rounded-full bg-blue-600/10 blur-[140px] animate-pulse-slow"/>
|
|
<div class="absolute bottom-[-20%] left-[-10%] w-[60%] h-[60%] rounded-full bg-indigo-600/10 blur-[140px] animate-pulse-slow" style="animation-delay: 3s;"/>
|
|
</div>
|
|
|
|
<div class="container mx-auto max-w-6xl px-6">
|
|
|
|
<!-- Back Button -->
|
|
<NuxtLink to="/browse" class="inline-flex items-center gap-2 text-slate-400 hover:text-white mb-8 transition-colors">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
|
</svg>
|
|
กลับหน้ารายการคอร์ส
|
|
</NuxtLink>
|
|
|
|
<div v-if="course" class="grid grid-cols-1 lg:grid-cols-12 gap-10">
|
|
|
|
<!-- Main Content (Left Column) -->
|
|
<div class="lg:col-span-8">
|
|
<!-- Hero Video Placeholder / Thumbnail -->
|
|
<div class="relative aspect-video bg-slate-800 rounded-3xl overflow-hidden border border-white/5 shadow-2xl mb-8 group cursor-pointer">
|
|
<div v-if="course.thumbnail_url" class="absolute inset-0">
|
|
<img :src="course.thumbnail_url" class="w-full h-full object-cover opacity-80 group-hover:opacity-60 transition-opacity" />
|
|
</div>
|
|
<div class="absolute inset-0 bg-gradient-to-br from-slate-800/50 to-slate-900/50 flex items-center justify-center">
|
|
<div class="w-20 h-20 rounded-full bg-blue-600/80 backdrop-blur flex items-center justify-center shadow-[0_0_30px_rgba(37,99,235,0.5)] group-hover:scale-110 transition-transform duration-300">
|
|
<div class="ml-1 w-0 h-0 border-t-[12px] border-t-transparent border-l-[20px] border-l-white border-b-[12px] border-b-transparent"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h1 class="text-4xl font-black text-white mb-6 leading-tight">{{ getLocalizedText(course.title) }}</h1>
|
|
<p class="text-slate-400 text-lg leading-relaxed mb-10">
|
|
{{ getLocalizedText(course.description) }}
|
|
</p>
|
|
|
|
<!-- Learning Objectives (Placeholder if not in API) -->
|
|
<!--
|
|
<div class="p-8 rounded-3xl bg-white/5 border border-white/5 mb-10">
|
|
<h3 class="font-bold text-xl text-white mb-6">สิ่งที่คุณจะได้เรียนรู้</h3>
|
|
<ul class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
...
|
|
</ul>
|
|
</div>
|
|
-->
|
|
|
|
<!-- Course Syllabus (Placeholder if not in API) -->
|
|
<div class="p-8 rounded-3xl bg-white/5 border border-white/5">
|
|
<h3 class="font-bold text-xl text-white mb-6">เนื้อหาในคอร์ส</h3>
|
|
<p class="text-slate-500">
|
|
{{ course.lessons ? `${course.lessons} บทเรียน` : 'ยังไม่มีเนื้อหาแสดง' }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar (Right Column): Sticky CTA -->
|
|
<div class="lg:col-span-4">
|
|
<div class="sticky top-28 p-8 rounded-3xl bg-slate-800/80 backdrop-blur-xl border border-white/10 shadow-2xl">
|
|
<div class="mb-8 text-center lg:text-left">
|
|
<span class="text-lg text-slate-500 line-through mr-4" v-if="course.original_price">{{ course.original_price }}</span>
|
|
<span class="text-5xl font-black text-white tracking-tight">{{ course.is_free ? 'ฟรี' : course.price }}</span>
|
|
</div>
|
|
|
|
<NuxtLink to="/auth/register" class="flex items-center justify-center w-full py-4 bg-blue-600 hover:bg-blue-500 text-white rounded-xl font-bold text-lg shadow-lg shadow-blue-600/30 transition-all hover:-translate-y-1 mb-6">
|
|
ลงทะเบียนเรียนทันที
|
|
</NuxtLink>
|
|
|
|
<div class="space-y-4 text-sm text-slate-400">
|
|
<div class="flex justify-between py-3 border-b border-white/5">
|
|
<span>จำนวนบทเรียน</span>
|
|
<span class="font-bold text-white">{{ course.lessons || 0 }} บท</span>
|
|
</div>
|
|
<div class="flex justify-between py-3 border-b border-white/5">
|
|
<span>ใบประกาศนียบัตร</span>
|
|
<span class="font-bold text-white">{{ course.have_certificate ? 'มี' : 'ไม่มี' }}</span>
|
|
</div>
|
|
<div class="flex justify-between py-3 border-b border-white/5">
|
|
<span>ระดับ</span>
|
|
<span class="font-bold text-white">ทั้งหมด</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Loading / Error State -->
|
|
<div v-else class="text-center py-20 text-slate-400">
|
|
<p v-if="error">เกิดข้อผิดพลาดในการโหลดข้อมูล</p>
|
|
<p v-else>กำลังโหลด...</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>
|