This commit is contained in:
Suphonchai Phoonsawat 2026-08-18 12:52:40 +07:00
commit 41573b3c4f
54 changed files with 32839 additions and 0 deletions

24
web/.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example

75
web/README.md Normal file
View file

@ -0,0 +1,75 @@
# Nuxt Minimal Starter
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install dependencies:
```bash
# npm
npm install
# pnpm
pnpm install
# yarn
yarn install
# bun
bun install
```
## Development Server
Start the development server on `http://localhost:3000`:
```bash
# npm
npm run dev
# pnpm
pnpm dev
# yarn
yarn dev
# bun
bun run dev
```
## Production
Build the application for production:
```bash
# npm
npm run build
# pnpm
pnpm build
# yarn
yarn build
# bun
bun run build
```
Locally preview production build:
```bash
# npm
npm run preview
# pnpm
pnpm preview
# yarn
yarn preview
# bun
bun run preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

23
web/app/app.vue Normal file
View file

@ -0,0 +1,23 @@
<script setup lang="ts">
const { initTheme, toggle, isDark } = useTheme()
onMounted(() => initTheme())
</script>
<template>
<div class="min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-900 dark:text-slate-100">
<header class="flex items-center justify-between border-b border-slate-200 px-6 py-4 dark:border-slate-700">
<NuxtLink to="/" class="text-lg font-semibold">Word Versioning</NuxtLink>
<button
type="button"
class="rounded-md border border-slate-300 px-3 py-1.5 text-sm hover:bg-slate-100 dark:border-slate-600 dark:hover:bg-slate-800"
@click="toggle"
>
{{ isDark ? '☀️ Light' : '🌙 Dark' }}
</button>
</header>
<main class="mx-auto max-w-3xl p-6">
<NuxtPage />
</main>
</div>
</template>

View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View file

@ -0,0 +1,36 @@
export type Theme = 'light' | 'dark'
// ต้องตรงกับ inline script กัน FOUC ใน nuxt.config.ts
const STORAGE_KEY = 'theme-preference'
export function useTheme() {
const theme = useState<Theme>('theme', () => 'light')
const isDark = computed(() => theme.value === 'dark')
function apply(next: Theme) {
theme.value = next
if (import.meta.client) {
document.documentElement.classList.toggle('dark', next === 'dark')
// ข้อยกเว้นเดียวที่ localStorage อนุญาต: theme preference ไม่ใช่ credential (กติกา #4)
localStorage.setItem(STORAGE_KEY, next)
}
}
/** Sync state กับที่ inline script ตั้งไว้ตอนโหลดหน้า (client เท่านั้น) */
function initTheme() {
if (!import.meta.client) return
const stored = localStorage.getItem(STORAGE_KEY)
if (stored === 'light' || stored === 'dark') {
theme.value = stored
} else {
theme.value = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
document.documentElement.classList.toggle('dark', theme.value === 'dark')
}
function toggle() {
apply(theme.value === 'dark' ? 'light' : 'dark')
}
return { theme, isDark, initTheme, toggle }
}

15
web/app/pages/index.vue Normal file
View file

@ -0,0 +1,15 @@
<template>
<section class="space-y-4">
<h1 class="text-2xl font-bold">สร้างเอกสารจาก template</h1>
<p class="text-slate-600 dark:text-slate-300">
เลือก template กรอกค่า fields แล้วดาวน์โหลด .docx ที่เป็น version 1 พร้อม metadata
ไปเปิดต่อใน Word พร้อม add-in
</p>
<!-- Phase 6: รายการ template ที่ active + ลิงก์เข้าหน้า documents/create/[templateId] -->
<div
class="rounded-lg border border-dashed border-slate-300 p-8 text-center text-sm text-slate-500 dark:border-slate-600 dark:text-slate-400"
>
รายการ template จะแสดงที่นี่ (Phase 6)
</div>
</section>
</template>

28
web/nuxt.config.ts Normal file
View file

@ -0,0 +1,28 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: { enabled: true },
// ⚠️ Web portal ต้องใช้ port 5173 เท่านั้น — ห้ามใช้ 3000
// (3000 ถูก add-in dev server ใช้อยู่: https://localhost:3000)
devServer: {
port: 5173,
},
modules: ['@nuxtjs/tailwindcss'],
// ~/assets/css/tailwind.css ถูก @nuxtjs/tailwindcss pick up อัตโนมัติ
app: {
head: {
title: 'Word Versioning',
// กัน dark-mode flash (FOUC) ก่อน Vue hydrate — ต้องใช้คีย์/ค่าเดียวกับ useTheme.ts
script: [
{
innerHTML:
"(function(){try{var t=localStorage.getItem('theme-preference');if(!t){t=window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';}if(t==='dark'){document.documentElement.classList.add('dark');}}catch(e){}})();",
},
],
},
},
})

11259
web/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

18
web/package.json Normal file
View file

@ -0,0 +1,18 @@
{
"name": "web",
"type": "module",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"@nuxtjs/tailwindcss": "^6.14.0",
"nuxt": "^4.5.2",
"vue": "^3.5.41",
"vue-router": "^5.2.0"
}
}

BIN
web/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

2
web/public/robots.txt Normal file
View file

@ -0,0 +1,2 @@
User-Agent: *
Disallow:

6
web/tailwind.config.ts Normal file
View file

@ -0,0 +1,6 @@
import type { Config } from 'tailwindcss'
export default {
// dark mode คุมด้วย class บน <html> (toggle จาก useTheme)
darkMode: 'class',
} satisfies Config

18
web/tsconfig.json Normal file
View file

@ -0,0 +1,18 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"files": [],
"references": [
{
"path": "./.nuxt/tsconfig.app.json"
},
{
"path": "./.nuxt/tsconfig.server.json"
},
{
"path": "./.nuxt/tsconfig.shared.json"
},
{
"path": "./.nuxt/tsconfig.node.json"
}
]
}