All checks were successful
Build and Deploy Frontend Learner / Build Frontend Learner Docker Image (push) Successful in 35s
Build and Deploy Frontend Learner / Deploy E-learning Frontend Learner to Dev Server (push) Successful in 4s
Build and Deploy Frontend Learner / Notify Deployment Status (push) Successful in 1s
116 lines
4 KiB
Vue
116 lines
4 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, is_recommended: 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 !pt-4">
|
|
<!-- Welcome Header Section (Minimalist) -->
|
|
<div class="flex items-center gap-6 mb-10 py-2 animate-fade-in">
|
|
<!-- Avatar with premium shadow -->
|
|
<div class="relative group cursor-pointer" @click="$router.push('/dashboard/profile')">
|
|
<div class="absolute -inset-1 bg-gradient-to-r from-blue-600 to-indigo-600 rounded-full blur opacity-25 group-hover:opacity-50 transition duration-1000 group-hover:duration-200"></div>
|
|
<q-avatar size="84px" class="relative shadow-2xl ring-4 ring-white dark:ring-slate-900 transition-all duration-500 overflow-hidden">
|
|
<img
|
|
:src="currentUser?.photoURL || 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mp&f=y'"
|
|
class="object-cover"
|
|
/>
|
|
</q-avatar>
|
|
</div>
|
|
|
|
<div class="flex flex-col">
|
|
<h1 class="text-2xl md:text-4xl font-black text-slate-900 dark:text-white tracking-tight leading-tight mb-1">
|
|
{{ $t('dashboard.welcomeTitle') }}, {{ currentUser?.firstName }}!
|
|
</h1>
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-slate-500 dark:text-slate-400 text-sm font-medium">{{ $t('dashboard.welcomeSubtitle') }}</span>
|
|
</div>
|
|
</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 fade-in {
|
|
from { opacity: 0; transform: translateX(-20px); }
|
|
to { opacity: 1; transform: translateX(0); }
|
|
}
|
|
|
|
.animate-fade-in {
|
|
animation: fade-in 0.8s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
|
|
}
|
|
</style>
|