116 lines
4.2 KiB
Vue
116 lines
4.2 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* @file home.vue
|
|
* @description หน้าแดชบอร์ดหลัก (Dashboard)
|
|
* แสดงข้อความต้อนรับ และคอร์สแนะนำสำหรับผู้เรียน
|
|
*/
|
|
|
|
definePageMeta({
|
|
layout: 'default',
|
|
middleware: 'auth'
|
|
})
|
|
|
|
useHead({
|
|
title: 'Dashboard - e-Learning'
|
|
})
|
|
|
|
const { currentUser } = useAuth()
|
|
const { fetchCourses, getLocalizedText } = useCourse() // Import useCourse
|
|
const { fetchCategories } = useCategory() // Import useCategory
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
|
// Recommended Courses State
|
|
// เก็บข้อมูลคอร์สแนะนำ (สุ่มมา 3 คอร์ส)
|
|
const recommendedCourses = ref<any[]>([])
|
|
|
|
onMounted(async () => {
|
|
// 1. Fetch Categories for mapping
|
|
const catRes = await fetchCategories()
|
|
const catMap = new Map()
|
|
if (catRes.success) {
|
|
catRes.data?.forEach((c: any) => catMap.set(c.id, c.name))
|
|
}
|
|
|
|
// 2. Fetch 3 Random Courses from Server
|
|
// ดึงคอร์สแบบสุ่มจาก Server โดยตรง (ผ่าน API ใหม่ที่เพิ่ม parameter random และ limit)
|
|
const res = await fetchCourses({ random: true, limit: 3, forceRefresh: true })
|
|
|
|
if (res.success && res.data?.length) {
|
|
recommendedCourses.value = res.data.map((c: any) => ({
|
|
id: c.id,
|
|
title: c.title,
|
|
category: catMap.get(c.category_id),
|
|
lessons: c.lessons,
|
|
image: c.thumbnail_url || '',
|
|
badge: '',
|
|
badgeType: ''
|
|
}))
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="page-container">
|
|
<!-- Welcome Header Section -->
|
|
<div class="welcome-section mb-10 overflow-hidden relative rounded-[2.5rem] p-8 md:p-12 text-white shadow-xl dark:shadow-2xl dark:shadow-blue-950/40 transition border border-white/5">
|
|
<div class="relative z-10 flex flex-col md:flex-row justify-between items-center gap-8">
|
|
<div class="text-center md:text-left">
|
|
<ClientOnly>
|
|
<h1 class="text-4xl md:text-6xl font-black mb-4 slide-up tracking-tight text-white drop-shadow-sm">
|
|
{{ $t('dashboard.welcomeTitle') }}, {{ currentUser?.firstName }}!
|
|
</h1>
|
|
</ClientOnly>
|
|
<p class="text-lg md:text-xl slide-up font-medium text-blue-100/90 max-w-xl" style="animation-delay: 0.1s;">
|
|
{{ $t('dashboard.welcomeSubtitle') }}
|
|
</p>
|
|
</div>
|
|
<div class="stats-mini flex gap-6 slide-up" style="animation-delay: 0.2s;"/>
|
|
</div>
|
|
<!-- Decorative Background elements -->
|
|
<div class="absolute inset-0 bg-gradient-to-br from-blue-500 via-blue-600 to-indigo-700 dark:from-[#1e293b] dark:via-[#0f172a] dark:to-[#1e3a8a] -z-0"/>
|
|
<div class="absolute -top-20 -right-20 w-80 h-80 bg-white/10 blur-[100px] rounded-full"/>
|
|
<div class="absolute -bottom-20 -left-20 w-80 h-80 bg-blue-400/10 blur-[100px] rounded-full"/>
|
|
<div class="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-[0.03] mix-blend-overlay"></div>
|
|
</div>
|
|
|
|
<!-- Main Content Area -->
|
|
<div>
|
|
<!-- Section: Recommended Courses -->
|
|
<div class="mb-6">
|
|
<h2 class="text-xl font-black flex items-center gap-3 tracking-tight text-slate-900 dark:text-white">
|
|
<span class="w-1 h-6 bg-emerald-500 rounded-full shadow-sm shadow-emerald-500/50"/>
|
|
{{ $t('menu.recommendedCourses') }}
|
|
</h2>
|
|
</div>
|
|
|
|
<!-- Recommended Grid -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 pb-20">
|
|
<CourseCard
|
|
v-for="course in recommendedCourses"
|
|
:key="course.id"
|
|
v-bind="course"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Loading State for Recommended -->
|
|
<div v-if="recommendedCourses.length === 0" class="flex justify-center py-10 opacity-50">
|
|
<div class="animate-pulse">Loading recommendations...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
@keyframes slide-up {
|
|
from { opacity: 0; transform: translateY(30px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.slide-up {
|
|
animation: slide-up 0.8s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
|
|
opacity: 0;
|
|
}
|
|
</style>
|