feat: Implement initial application layout, header, course discovery, and dashboard pages with Thai localization.

This commit is contained in:
supalerk-ar66 2026-02-09 14:47:02 +07:00
parent 0fc67bf9c3
commit 38969c7169
5 changed files with 11 additions and 7 deletions

View file

@ -22,12 +22,13 @@ const searchText = ref('')
<q-toolbar class="bg-transparent text-slate-800 dark:text-white h-16 px-4"> <q-toolbar class="bg-transparent text-slate-800 dark:text-white h-16 px-4">
<!-- Mobile Menu Toggle --> <!-- Mobile Menu Toggle -->
<q-btn <q-btn
v-show="$q.screen.lt.md"
flat flat
round round
dense dense
icon="menu" icon="menu"
@click="emit('toggleSidebar')" @click="emit('toggleSidebar')"
class="md:hidden mr-2 text-slate-700 dark:text-white" class="mr-2 text-slate-700 dark:text-white"
aria-label="Menu" aria-label="Menu"
/> />

View file

@ -140,7 +140,8 @@
"emailVerified": "ยืนยันอีเมลสำเร็จ!", "emailVerified": "ยืนยันอีเมลสำเร็จ!",
"emailVerifiedDesc": "บัญชีของคุณได้รับการยืนยันเรียบร้อยแล้ว", "emailVerifiedDesc": "บัญชีของคุณได้รับการยืนยันเรียบร้อยแล้ว",
"invalidToken": "Token ยืนยันตัวตนไม่ถูกต้อง", "invalidToken": "Token ยืนยันตัวตนไม่ถูกต้อง",
"tokenExpired": "Token หมดอายุหรือล้มเหลว" "tokenExpired": "Token หมดอายุหรือล้มเหลว",
"logout": "ออกจากระบบ"
}, },
"language": { "language": {
"label": "ภาษา / Language", "label": "ภาษา / Language",

View file

@ -29,7 +29,7 @@ const toggleLeftDrawer = () => {
v-model="leftDrawerOpen" v-model="leftDrawerOpen"
show-if-above show-if-above
bordered bordered
:width="260" :width="280"
class="!bg-white dark:!bg-[#1e293b] border-r border-slate-200 dark:border-slate-700" class="!bg-white dark:!bg-[#1e293b] border-r border-slate-200 dark:border-slate-700"
> >
<AppSidebar /> <AppSidebar />

View file

@ -173,7 +173,7 @@ onMounted(() => {
<!-- RIGHT CONTENT: Course Grid --> <!-- RIGHT CONTENT: Course Grid -->
<div class="flex-1 w-full"> <div class="flex-1 w-full">
<template v-if="filteredCourses.length > 0"> <div v-if="filteredCourses.length > 0" class="flex flex-col gap-12">
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6"> <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
<CourseCard <CourseCard
v-for="course in filteredCourses" v-for="course in filteredCourses"
@ -185,7 +185,7 @@ onMounted(() => {
</div> </div>
<!-- Pagination Controls --> <!-- Pagination Controls -->
<div v-if="totalPages > 1" class="flex justify-center mt-12 pb-10"> <div v-if="totalPages > 1" class="flex justify-center pb-10">
<q-pagination <q-pagination
v-model="currentPage" v-model="currentPage"
:max="totalPages" :max="totalPages"
@ -199,7 +199,7 @@ onMounted(() => {
@update:model-value="loadCourses" @update:model-value="loadCourses"
/> />
</div> </div>
</template> </div>
<!-- Empty State --> <!-- Empty State -->
<div <div

View file

@ -58,7 +58,9 @@ onMounted(async () => {
<div class="welcome-section mb-8 overflow-hidden relative rounded-3xl p-8 md:p-10 text-white shadow-lg dark:shadow-2xl dark:shadow-blue-900/20 transition-all"> <div class="welcome-section mb-8 overflow-hidden relative rounded-3xl p-8 md:p-10 text-white shadow-lg dark:shadow-2xl dark:shadow-blue-900/20 transition-all">
<div class="relative z-10 flex flex-col md:flex-row justify-between items-center gap-8"> <div class="relative z-10 flex flex-col md:flex-row justify-between items-center gap-8">
<div> <div>
<h1 class="text-4xl md:text-5xl font-black mb-3 slide-up tracking-tight text-white dark:text-white">{{ $t('dashboard.welcomeTitle') }}, {{ currentUser?.firstName }}!</h1> <ClientOnly>
<h1 class="text-4xl md:text-5xl font-black mb-3 slide-up tracking-tight text-white dark:text-white">{{ $t('dashboard.welcomeTitle') }}, {{ currentUser?.firstName }}!</h1>
</ClientOnly>
<p class="text-lg slide-up font-medium text-blue-100" style="animation-delay: 0.1s;">{{ $t('dashboard.welcomeSubtitle') }}</p> <p class="text-lg slide-up font-medium text-blue-100" style="animation-delay: 0.1s;">{{ $t('dashboard.welcomeSubtitle') }}</p>
</div> </div>
<div class="stats-mini flex gap-6 slide-up" style="animation-delay: 0.2s;"/> <div class="stats-mini flex gap-6 slide-up" style="animation-delay: 0.2s;"/>