feat: Implement initial UI design system, theming, and classroom learning page.

This commit is contained in:
supalerk-ar66 2026-01-27 11:31:08 +07:00
parent 8b403f906a
commit 90d50dc84a
4 changed files with 78 additions and 21 deletions

View file

@ -58,11 +58,14 @@
/* Dark Mode (applied when `.dark` class is present on <html>) */
.dark {
--bg-body: #0f172a;
--bg-surface: #1e293b; /* User requested specific color */
/* Deep Oceanic 3-Level Surfaces */
--bg-body: #020617; /* Slate 950: Deep Sea */
--bg-surface: #0f172a; /* Slate 900: Sidebar/Main Cards */
--bg-elevated: #1e293b; /* Slate 800: Inner Cards/Highlights */
--text-main: #f8fafc; /* text-slate-50: Brighter white for main text */
--text-secondary: #cbd5e1; /* text-slate-300: Lighter grey for secondary text */
--border-color: #334155; /* border-slate-700: Higher contrast border */
--text-secondary: #94a3b8; /* text-slate-300: Lighter grey for secondary text */
--border-color: rgba(255, 255, 255, 0.08); /* White with low opacity for subtle borders */
--neutral-50: #1e293b;
--neutral-100: #334155;
--neutral-200: #475569;

View file

@ -240,10 +240,10 @@ onBeforeUnmount(() => {
</script>
<template>
<q-layout view="hHh LpR lFf" class="bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100">
<q-layout view="hHh LpR lFf" class="bg-[var(--bg-body)] text-[var(--text-main)]">
<!-- Header -->
<q-header bordered class="bg-white dark:bg-slate-900 border-b border-gray-200 dark:border-white/5 text-slate-900 dark:text-white h-14">
<q-header bordered class="bg-[var(--bg-surface)] border-b border-gray-200 dark:border-white/5 text-[var(--text-main)] h-14">
<q-toolbar>
<q-btn flat round dense icon="menu" class="lg:hidden mr-2" @click="toggleSidebar" />
@ -273,7 +273,8 @@ onBeforeUnmount(() => {
side="left"
:width="320"
:breakpoint="1024"
class="bg-white dark:bg-slate-900"
class="bg-[var(--bg-surface)]"
content-class="bg-[var(--bg-surface)]"
>
<div v-if="courseData" class="h-full scroll">
<q-list class="pb-10">
@ -355,13 +356,13 @@ onBeforeUnmount(() => {
</div>
<!-- Lesson Info -->
<div v-if="currentLesson" class="bg-white dark:bg-slate-800 p-6 rounded-2xl shadow-sm border border-slate-100 dark:border-white/5">
<h1 class="text-2xl md:text-3xl font-bold mb-3">{{ getLocalizedText(currentLesson.title) }}</h1>
<p class="text-slate-500 dark:text-slate-400 text-base md:text-lg" v-if="currentLesson.description">{{ getLocalizedText(currentLesson.description) }}</p>
<div v-if="currentLesson" class="bg-[var(--bg-surface)] p-6 rounded-2xl shadow-sm border border-[var(--border-color)] mt-6">
<h1 class="text-2xl md:text-3xl font-bold mb-3 text-[var(--text-main)]">{{ getLocalizedText(currentLesson.title) }}</h1>
<p class="text-[var(--text-secondary)] text-base md:text-lg" v-if="currentLesson.description">{{ getLocalizedText(currentLesson.description) }}</p>
<!-- Lesson Content Area -->
<div v-if="!videoSrc && currentLesson.content" class="mt-6 prose dark:prose-invert max-w-none p-6 bg-slate-50 dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/5">
<div v-html="getLocalizedText(currentLesson.content)" class="text-base md:text-lg leading-relaxed"></div>
<div v-if="!videoSrc && currentLesson.content" class="mt-6 prose dark:prose-invert max-w-none p-6 bg-[var(--bg-elevated)] rounded-xl border border-[var(--border-color)]">
<div v-html="getLocalizedText(currentLesson.content)" class="text-base md:text-lg leading-relaxed text-[var(--text-main)]"></div>
</div>
</div>
</div>

View file

@ -1,10 +1,21 @@
export default defineNuxtPlugin(() => {
// Client-side only theme initialization to prevent flash of wrong theme
if (process.client) {
const saved = localStorage.getItem('theme')
const isDark = saved === 'dark' || (!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)
export default defineNuxtPlugin((nuxtApp) => {
const { $q } = useQuasar()
// ฟังก์ชันสลับโหมดและ Sync กับ Quasar
const updateTheme = (isDark: boolean) => {
if (isDark) {
document.documentElement.classList.add('dark')
if ($q) $q.dark.set(true)
} else {
document.documentElement.classList.remove('dark')
if ($q) $q.dark.set(false)
}
}
// ตอนเริ่มทำงานบน Client
if (process.client) {
const savedTheme = localStorage.getItem('theme')
const isDark = savedTheme === 'dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)
// Apply class immediately for Tailwind
document.documentElement.classList.toggle('dark', isDark)
}
})

View file

@ -127,7 +127,49 @@
---
## 🆕 4. Recent Updates (บันทึกการอัปเดตล่าสุด)
## 🎨 4. Theme & Styling Guide (แนวทางการออกแบบ)
เพื่อให้การพัฒนาในอนาคตเป็นระเบียบและส่งต่อได้ง่าย ระบบมีการจัดการ CSS ดังนี้:
### 4.1 การจัดการ Dark Mode
ระบบใช้การสลับคลาส `.dark` ที่ `<html>` tag โดยอ้างอิงตัวแปรจาก `assets/css/main.css`:
- **Main Background (`--bg-body`):**
- Light: `#f8fafc` (Slate 50)
- Dark: `#0f172a` (Slate 900)
- **Surface/Card Background (`--bg-surface`):**
- Light: `#ffffff`
- Dark: `#1e293b` (Slate 800)
- **Text Primary (`--text-main`):**
- Light: `#0f172a` (Slate 900)
- Dark: `#f8fafc` (Slate 50)
- **Border Color (`--border-color`):**
- Light: `#e2e8f0` (Slate 200)
- Dark: `#334155` (Slate 700)
### 4.2 มาตรฐานการเขียน CSS
1. **ใช้ Tailwind `dark:` เสมอ:** เมื่อต้องการกำหนดสไตล์เฉพาะโหมดมืด (เช่น `dark:bg-slate-800`).
2. **ใช้ CSS Variables:** สำหรับค่าสเตติกที่ใช้ทั้งเว็บ (เช่น `color: var(--primary)`).
3. **ฟอนต์:** ระบบใช้ **"Prompt"** สำหรับหัวข้อและตัวเน้น และ **"Sarabun"** สำหรับเนื้อหาภาษาไทยเพื่อให้พรีเมียมและอ่านง่าย.
---
## ⚡ 5. Performance & Caching (ระบบจัดการความเร็ว)
เพื่อป้องกันปัญหา **429 Too Many Requests** และเพิ่มความเร็ว ระบบมีการทำ **Client-side Caching** ในระดับ Composables:
- **State Management:** ใช้ `useState()` ของ Nuxt เพื่อเก็บข้อมูลในหน่วยความจำ (Shared Memory).
- **Cached Endpoints:**
- `fetchUserProfile`: โหลดเพียงครั้งเดียวเมื่อแอปเริ่มต้น (หรือเมื่อสั่ง Force Refresh).
- `fetchCategories`: โหลดครั้งเดียวและแชร์ใช้ข้ามหน้า.
- `fetchCourses`: เก็บรายชื่อคอร์สทั้งหมดเพื่อลดภาระ Server ในการเปลี่ยนหน้าไปมา.
- **Request Deduping:** มีระบบเช็คสถานะ Loading เพื่อป้องกันการยิง API ตัวเดียวกันซ้ำซ้อนพร้อมกันหลายที่.
---
## 🆕 6. Recent Updates (บันทึกการอัปเดตล่าสุด)
### ✅ Phase 1: Authentication & UI Refactor
@ -158,7 +200,7 @@
---
## 🔍 5. Status & Missing Integrations (ส่วนที่ต้องพัฒนาต่อ)
## 🔍 7. Status & Missing Integrations (ส่วนที่ต้องพัฒนาต่อ)
จากการตรวจสอบล่าสุด รายการดังต่อไปนี้ยังเป็นส่วนที่รอการพัฒนา (Pending):