feat: Implement initial e-learning platform frontend structure including dashboard, course management, authentication, and common UI components.

This commit is contained in:
supalerk-ar66 2026-02-27 10:05:33 +07:00
parent aceeb80d9a
commit ad11c6b7c5
44 changed files with 720 additions and 578 deletions

View file

@ -1,23 +1,22 @@
<script setup lang="ts">
/**
* @file LandingHeader.vue
* @description The main header for the public landing pages.
* Features a transparent background that becomes solid/glass upon scrolling.
* Responsive: Collapses into a drawer on mobile (md breakpoint).
* @description คอมโพเนนตแถบเมนานบน (Header Navigation) สำหรบหน Landing Page และหนาเปดอนๆ
* รองรบการเปลยนภาษา เปลยนโหมดสวาง/ และเขาถงเมนใช (Profile/Logout)
*/
const text = ref('');
// Track scrolling state to adjust header styling
// (scroll) Header
const isScrolled = ref(false)
const { isAuthenticated } = useAuth()
// Mobile Drawer State
// (Mobile Drawer State)
// / (Mobile Drawer)
const mobileMenuOpen = ref(false)
const handleScroll = () => {
isScrolled.value = window.scrollY > 20
}
// Lock body scroll when mobile menu is open
// (Lock body scroll)
watch(mobileMenuOpen, (isOpen) => {
if (isOpen) {
document.body.style.overflow = 'hidden'
@ -32,21 +31,21 @@ onMounted(() => {
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
document.body.style.overflow = '' // Cleanup
document.body.style.overflow = '' // (Cleanup)
})
</script>
<template>
<!--
Header Container
- Transitions between transparent and glass effect based on scroll.
คอนเทนเนอรของ Header (Header Container)
- เปลยนจากสใส (transparent) เปนเอฟเฟกตกระจก (glass effect) เมอเลอนเมาสลง
-->
<header
class="fixed top-0 left-0 right-0 z-[100] transition-all"
:class="[isScrolled ? 'h-20 glass-nav shadow-lg' : 'h-20 bg-transparent duration-300 border-b border-b-grey-7 ']"
>
<div class="container mx-auto px-6 md:px-12 h-full flex items-center justify-between">
<!-- Left Section: Logo -->
<!-- านซาย: โลโกแบรนด (Left Section: Logo) -->
<NuxtLink to="/" class="flex items-center gap-3 group">
<div class="bg-blue-600 text-white font-black rounded-full px-6 w-10 h-10 flex items-center justify-center group-hover:scale-110 transition-transform">
<q-icon name="o_school" size="24px" />
@ -67,7 +66,7 @@ onUnmounted(() => {
</div>
</NuxtLink>
<!-- Desktop Navigation (Visible by default, hidden on mobile via CSS 'desktop-nav') -->
<!-- การนำทางสำหรบเดสกอป (แสดงผลเปนคาเรมต, อนบนมอถอผาน CSS 'desktop-nav') -->
<!-- <nav class="flex desktop-nav items-center gap-8 text-[16px] font-medium">
<NuxtLink
to="/browse"
@ -89,10 +88,10 @@ onUnmounted(() => {
<!-- Desktop Action Buttons (Visible by default, hidden on mobile via CSS 'desktop-nav') -->
<!-- มปฏการสำหรบเดสกอป (แสดงผลเปนคาเรมต, อนบนมอถอผาน CSS 'desktop-nav') -->
<div class="flex desktop-nav items-center gap-4">
<template v-if="!isAuthenticated">
<!-- Login Button -->
<!-- มเขาสระบบ (Login Button) -->
<NuxtLink
to="/auth/login"
class="px-5 py-4 rounded-full text-slate-700 font-semibold text-sm transition-all hover:-translate-y-0.5"
@ -101,7 +100,7 @@ onUnmounted(() => {
{{ $t('auth.login') }}
</NuxtLink>
<!-- Register Button -->
<!-- มสมครสมาช (Register Button) -->
<NuxtLink
to="/auth/register"
class="px-5 py-4 rounded-full bg-blue-600 text-white font-semibold text-sm hover:shadow-blue-600/40 hover:-translate-y-0.5 transition-all"
@ -120,7 +119,7 @@ onUnmounted(() => {
</template>
</div>
<!-- Mobile Menu Button (Visible on Mobile) -->
<!-- มเปดเมนบนมอถ (แสดงผลเฉพาะบน Mobile) -->
<button
class="md:hidden mobile-toggle ml-auto relative z-[120] w-10 h-10 flex items-center justify-center rounded-full transition-colors"
:class="[isScrolled ? 'text-white hover:bg-white/10' : 'text-slate-900 hover:bg-slate-100', mobileMenuOpen ? 'text-slate-900 z-[120]' : '']"
@ -132,7 +131,7 @@ onUnmounted(() => {
</div>
</header>
<!-- Mobile Navigation Drawer (Teleported to body to avoid z-index/clipping issues with Header) -->
<!-- นชกเมนานขางสำหรบมอถ (Mobile Navigation Drawer - แยกสวนไปย body เพอไมใหญหา z-index หรอถกบ) -->
<Teleport to="body">
<div v-if="mobileMenuOpen">
<div
@ -146,7 +145,7 @@ onUnmounted(() => {
:class="[mobileMenuOpen ? 'translate-x-0' : 'translate-x-full']"
>
<div class="p-6 pt-8 flex flex-col gap-6 h-full overflow-y-auto relative">
<!-- Close Button -->
<!-- มปดเมน (Close Button) -->
<button
class="absolute top-6 right-6 w-8 h-8 flex items-center justify-center rounded-full bg-slate-100 text-slate-500 hover:bg-slate-200 transition-colors"
@click="mobileMenuOpen = false"
@ -154,7 +153,7 @@ onUnmounted(() => {
<q-icon name="close" size="20px" />
</button>
<!-- Mobile Links -->
<!-- งกสำหรบมอถ (Mobile Links) -->
<nav class="flex flex-col gap-2 mt-8">
<NuxtLink
to="/"
@ -210,14 +209,14 @@ onUnmounted(() => {
</template>
<style scoped>
/* Glassmorphism Effect for Scrolled Header */
/* เอฟเฟกต์ Glassmorphism สำหรับ Header ตอนเลื่อนเมาส์ลง */
.glass-nav {
background: rgba(15, 23, 42, 0.95); /* Darker background for legibility */
background: rgba(15, 23, 42, 0.95); /* พื้นหลังเข้มขึ้นเพื่อให้อ่านตัวหนังสือชัดเจน */
backdrop-filter: blur(16px);
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
/* Premium Primary Button Styling */
/* สไตล์ปุ่มหลัก (Primary Button) แบบพรีเมียม */
.btn-primary-premium {
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
color: white;
@ -236,7 +235,7 @@ onUnmounted(() => {
box-shadow: 0 8px 20px -4px rgba(37, 99, 235, 0.5);
}
/* Secondary Premium Button Styling */
/* สไตล์ปุ่มดรอง (Secondary Button) แบบพรีเมียม */
.btn-secondary-premium {
padding: 0.75rem 1.5rem;
border-radius: 0.75rem;
@ -260,11 +259,11 @@ onUnmounted(() => {
}
/*
Force Visibility Logic to bypass potential Tailwind Build issues
Ensures Desktop and Mobile parts are strictly separated
โลจกบงคบการแสดงผล เพอแกญหาการคอมไฟลของ Tailwind
นยนวาสวน Desktop และ Mobile เลยเอาตแยกจากกนอยางชดเจน
*/
.desktop-nav {
display: flex; /* Default to visible */
display: flex; /* แสดงผลเป็นค่าเริ่มต้น */
}
@media (max-width: 767.98px) {