diff --git a/docker-compose.yml b/docker-compose.yml index 886f283e..67398d52 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -121,8 +121,8 @@ services: deploy: resources: limits: - cpus: "1.0" - memory: 1G + cpus: "2.0" + memory: 2G environment: TZ: "Asia/Bangkok" NODE_ENV: "development" diff --git a/frontend/.editorconfig b/frontend/.editorconfig new file mode 100644 index 00000000..86a63dc0 --- /dev/null +++ b/frontend/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json new file mode 100644 index 00000000..adb37d33 --- /dev/null +++ b/frontend/.eslintrc.json @@ -0,0 +1,15 @@ +// .eslintrc.json +{ + "root": true, + "extends": ["next/core-web-vitals"], + "parserOptions": { + "ecmaVersion": 2023, + "sourceType": "module" + }, + "rules": { + "react/no-unescaped-entities": "off", + "@next/next/no-img-element": "off", + "no-console": ["warn", { "allow": ["warn", "error"] }] + }, + "ignorePatterns": ["node_modules/", ".next/", "dist/", "coverage/"] +} diff --git a/frontend/.prettierrc.json b/frontend/.prettierrc.json new file mode 100644 index 00000000..6e3680be --- /dev/null +++ b/frontend/.prettierrc.json @@ -0,0 +1,11 @@ +// File: .prettierrc.json +{ + "semi": true, + "singleQuote": false, + "tabWidth": 2, + "printWidth": 100, + "trailingComma": "es5", + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "lf" +} diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev index ba646530..78ac5c91 100644 --- a/frontend/Dockerfile.dev +++ b/frontend/Dockerfile.dev @@ -44,7 +44,7 @@ COPY --from=deps /app/node_modules /app/node_modules # ไม่กำหนด USER ที่นี่ ปล่อยให้ compose คุม (ตอนนี้คุณใช้ user: "1000:1000") ENV NODE_ENV=development EXPOSE 3000 -CMD ["npm", "run", "dev"] +CMD ["npm", "run", "dev"]yy ############ Build (production) ############ FROM deps AS builder diff --git a/frontend/app/globals.css b/frontend/app/globals.css index f969dbf5..5dab8877 100755 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -2,16 +2,85 @@ @tailwind components; @tailwind utilities; -/* shadcn base tokens */ -:root { --radius: 0.5rem; } -@layer base { - html { -webkit-font-smoothing: antialiased; } - :root { --background: 0 0% 100%; --foreground: 0 0% 3.9%; --card: 0 0% 100%; --card-foreground: 0 0% 3.9%; --popover: 0 0% 100%; --popover-foreground: 0 0% 3.9%; --primary: 0 0% 9%; --primary-foreground: 0 0% 98%; --secondary: 0 0% 96.1%; --secondary-foreground: 0 0% 9%; --muted: 0 0% 96.1%; --muted-foreground: 0 0% 45.1%; --accent: 0 0% 96.1%; --accent-foreground: 0 0% 9%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 0 0% 98%; --border: 0 0% 89.8%; --input: 0 0% 89.8%; --ring: 0 0% 3.9%; --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; --radius: 0.5rem; } - .dark { --background: 0 0% 3.9%; --foreground: 0 0% 98%; --card: 0 0% 3.9%; --card-foreground: 0 0% 98%; --popover: 0 0% 3.9%; --popover-foreground: 0 0% 98%; --primary: 0 0% 98%; --primary-foreground: 0 0% 9%; --secondary: 0 0% 14.9%; --secondary-foreground: 0 0% 98%; --muted: 0 0% 14.9%; --muted-foreground: 0 0% 63.9%; --accent: 0 0% 14.9%; --accent-foreground: 0 0% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 0 0% 98%; --border: 0 0% 14.9%; --input: 0 0% 14.9%; --ring: 0 0% 83.1%; --chart-1: 220 70% 50%; --chart-2: 160 60% 45%; --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; } +/* ====== shadcn/ui theme (light + dark) ====== */ +:root { + --background: 210 40% 98%; + --foreground: 220 15% 15%; + + /* โทน “น้ำทะเล” ตามธีมของคุณ */ + --primary: 199 90% 40%; + --primary-foreground: 0 0% 100%; + + --secondary: 199 60% 92%; + --secondary-foreground: 220 15% 20%; + + --muted: 210 20% 96%; + --muted-foreground: 220 10% 35%; + + --accent: 199 95% 48%; + --accent-foreground: 0 0% 100%; + + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 100%; + + --card: 0 0% 100%; + --card-foreground: 220 15% 15%; + + --popover: 0 0% 100%; + --popover-foreground: 220 15% 15%; + + --border: 214 32% 91%; + --input: 214 32% 91%; + --ring: 199 90% 40%; + + --radius: 0.8rem; /* โค้งมนตามแนวทาง UI ของโปรเจ็ค */ } -@layer base { - * { - @apply border-border; } - body { - @apply bg-background text-foreground; } } +.dark { + --background: 220 18% 10%; + --foreground: 0 0% 100%; + + --primary: 199 95% 58%; + --primary-foreground: 220 18% 10%; + + --secondary: 218 14% 20%; + --secondary-foreground: 0 0% 100%; + + --muted: 220 14% 18%; + --muted-foreground: 220 10% 70%; + + --accent: 199 95% 62%; + --accent-foreground: 220 18% 10%; + + --destructive: 0 62% 46%; + --destructive-foreground: 0 0% 100%; + + --card: 220 18% 12%; + --card-foreground: 0 0% 100%; + + --popover: 220 18% 12%; + --popover-foreground: 0 0% 100%; + + --border: 220 14% 28%; + --input: 220 14% 28%; + --ring: 199 95% 62%; +} + +/* Base styling */ +@layer base { + * { + @apply border-border; + } + html, + body { + @apply h-full; + } + body { + @apply bg-background text-foreground antialiased; + } +} + +/* Utility: container max width (ช่วยเรื่อง layout) */ +.container { + @apply mx-auto px-4; +} diff --git a/frontend/app/layout.jsx b/frontend/app/layout.jsx index 2736ea00..ef736bd9 100755 --- a/frontend/app/layout.jsx +++ b/frontend/app/layout.jsx @@ -1,4 +1,9 @@ // app/layout.jsx +import "./globals.css"; +import { Inter } from "next/font/google"; + +const inter = Inter({ subsets: ["latin"] }); + export const metadata = { title: "DMS", description: "Document Management System", @@ -6,8 +11,59 @@ export const metadata = { export default function RootLayout({ children }) { return ( - - {children} - + + +
+ {/* Sidebar */} + + + {/* Main content */} +
+ {/* Top Navbar */} +
+ Laem Chabang Port Phase 3 +
+ + superadmin +
+
+ +
{children}
+
+
+ + ); } diff --git a/frontend/app/page.jsx b/frontend/app/page.jsx index 33db56a4..8b97fb56 100755 --- a/frontend/app/page.jsx +++ b/frontend/app/page.jsx @@ -1,5 +1,64 @@ // app/page.jsx -import { redirect } from "next/navigation"; -export default function Home() { - redirect("/dashboard"); +"use client"; + +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; + +export default function HomePage() { + return ( +
+

Welcome to DMS

+ + + + Overview + Activity + + + +
+ + + 📑 RFAs + + +

24

+
+
+ + + + 📐 Drawings + + +

112

+
+
+ + + + 📤 Transmittals + + +

8

+
+
+
+
+ + +
+

+ ✔️ User editor01 uploaded Drawing D-2025-07 +

+

✔️ Transmittal T-2025-02 issued to Contractor

+

✔️ RFA R-2025-03 marked as Resolved

+
+
+
+ + +
+ ); } diff --git a/frontend/next.config.js b/frontend/next.config.js index 7f06481f..22cd7c37 100755 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -1,6 +1,23 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, - experimental: { serverActions: { allowedOrigins: ["*"] } }, + experimental: { + // เปิด app router (ค่าเริ่มต้นของ Next 15 อยู่แล้ว) + typedRoutes: false, + }, + // รองรับการรันหลัง reverse proxy และกำหนด base URL ผ่าน ENV + env: { + NEXT_PUBLIC_API_BASE: + process.env.NEXT_PUBLIC_API_BASE || "http://localhost:8080", + }, + // ปรับขนาดภาพ/โดเมนถ้าจำเป็น + images: { + remotePatterns: [ + // { protocol: "https", hostname: "lcbp3.np-dms.work" } + ], + }, + // เปิด SWC minify + swcMinify: true, }; -module.exports = nextConfig; \ No newline at end of file + +module.exports = nextConfig; diff --git a/frontend/package.json b/frontend/package.json index f8479129..367e5fa3 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,34 +1,40 @@ { "name": "dms-frontend", - "version": "0.6.0", + "version": "1.0.0", "private": true, - "engines": { - "node": ">=20.0.0" - }, "scripts": { "dev": "next dev -p 3000", "build": "next build", "start": "next start -p 3000", "lint": "next lint", - "shadcn": "shadcn" + "format": "prettier --write ." }, "dependencies": { - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "framer-motion": "^11.18.2", - "lucide-react": "^0.441.0", - "next": "^15.5.3", + "next": "15.0.3", "react": "18.3.1", "react-dom": "18.3.1", - "tailwind-merge": "^2.6.0", - "tailwindcss-animate": "^1.0.7" + "tailwindcss": "3.4.14", + "tailwindcss-animate": "1.0.7", + "postcss": "8.4.47", + "autoprefixer": "10.4.20", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "tailwind-merge": "^2.2.1", + "framer-motion": "^11.2.10", + "lucide-react": "^0.451.0", + "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-tooltip": "^1.1.7", + "@radix-ui/react-tabs": "^1.1.3", + "@radix-ui/react-switch": "^1.1.2", + "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-slot": "^1.1.0" }, "devDependencies": { - "autoprefixer": "^10.4.21", - "eslint": "^9.9.0", - "eslint-config-next": "15.0.1", - "postcss": "^8.5.6", - "shadcn": "^3.3.1", - "tailwindcss": "^3.4.17" + "eslint": "9.13.0", + "eslint-config-next": "15.0.3", + "prettier": "3.3.3" + }, + "engines": { + "node": ">=20.0.0" } } diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index ee1e9407..9fabed7d 100755 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,115 +1,74 @@ -/** @type {import("tailwindcss").Config} */ +/** @type {import('tailwindcss').Config} */ module.exports = { darkMode: ["class"], content: [ - "./app/**/*.{js,jsx}", - "./pages/**/*.{js,jsx}", - "./components/**/*.{js,jsx}", - "./src/**/*.{js,jsx}" + "./app/**/*.{js,jsx,ts,tsx,mdx}", + "./components/**/*.{js,jsx,ts,tsx,mdx}", + "./pages/**/*.{js,jsx,ts,tsx,mdx}", // เผื่อมีโฟลเดอร์ pages + "./src/**/*.{js,jsx,ts,tsx,mdx}", // เผื่อคุณเก็บ component ใน src ], theme: { - extend: { - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)' - }, - colors: { - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))' - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))' - }, - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))' - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))' - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))' - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))' - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))' - }, - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - chart: { - '1': 'hsl(var(--chart-1))', - '2': 'hsl(var(--chart-2))', - '3': 'hsl(var(--chart-3))', - '4': 'hsl(var(--chart-4))', - '5': 'hsl(var(--chart-5))' - } - } - } + container: { + center: true, + padding: "2rem", + screens: { "2xl": "1400px" }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, }, - plugins: [require("tailwindcss-animate")] + plugins: [require("tailwindcss-animate")], }; -"EOF" - -cat > postcss.config.js < app/globals.css < jsconfig.json < components.json < lib/utils.js <