Update frontend new build dev, proc
This commit is contained in:
@@ -121,8 +121,8 @@ services:
|
|||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
cpus: "1.0"
|
cpus: "2.0"
|
||||||
memory: 1G
|
memory: 2G
|
||||||
environment:
|
environment:
|
||||||
TZ: "Asia/Bangkok"
|
TZ: "Asia/Bangkok"
|
||||||
NODE_ENV: "development"
|
NODE_ENV: "development"
|
||||||
|
|||||||
9
frontend/.editorconfig
Normal file
9
frontend/.editorconfig
Normal file
@@ -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
|
||||||
15
frontend/.eslintrc.json
Normal file
15
frontend/.eslintrc.json
Normal file
@@ -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/"]
|
||||||
|
}
|
||||||
11
frontend/.prettierrc.json
Normal file
11
frontend/.prettierrc.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// File: .prettierrc.json
|
||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"printWidth": 100,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"endOfLine": "lf"
|
||||||
|
}
|
||||||
@@ -44,7 +44,7 @@ COPY --from=deps /app/node_modules /app/node_modules
|
|||||||
# ไม่กำหนด USER ที่นี่ ปล่อยให้ compose คุม (ตอนนี้คุณใช้ user: "1000:1000")
|
# ไม่กำหนด USER ที่นี่ ปล่อยให้ compose คุม (ตอนนี้คุณใช้ user: "1000:1000")
|
||||||
ENV NODE_ENV=development
|
ENV NODE_ENV=development
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
CMD ["npm", "run", "dev"]
|
CMD ["npm", "run", "dev"]yy
|
||||||
|
|
||||||
############ Build (production) ############
|
############ Build (production) ############
|
||||||
FROM deps AS builder
|
FROM deps AS builder
|
||||||
|
|||||||
@@ -2,16 +2,85 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
/* shadcn base tokens */
|
/* ====== shadcn/ui theme (light + dark) ====== */
|
||||||
:root { --radius: 0.5rem; }
|
:root {
|
||||||
@layer base {
|
--background: 210 40% 98%;
|
||||||
html { -webkit-font-smoothing: antialiased; }
|
--foreground: 220 15% 15%;
|
||||||
: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%; }
|
/* โทน “น้ำทะเล” ตามธีมของคุณ */
|
||||||
|
--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 ของโปรเจ็ค */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.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 {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border; }
|
@apply border-border;
|
||||||
|
}
|
||||||
|
html,
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground; } }
|
@apply h-full;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground antialiased;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utility: container max width (ช่วยเรื่อง layout) */
|
||||||
|
.container {
|
||||||
|
@apply mx-auto px-4;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
// app/layout.jsx
|
// app/layout.jsx
|
||||||
|
import "./globals.css";
|
||||||
|
import { Inter } from "next/font/google";
|
||||||
|
|
||||||
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "DMS",
|
title: "DMS",
|
||||||
description: "Document Management System",
|
description: "Document Management System",
|
||||||
@@ -6,8 +11,59 @@ export const metadata = {
|
|||||||
|
|
||||||
export default function RootLayout({ children }) {
|
export default function RootLayout({ children }) {
|
||||||
return (
|
return (
|
||||||
<body className="min-h-screen bg-[linear-gradient(180deg,#F3FBFD_0%,#E6F7FB_100%)]">
|
<html lang="en" suppressHydrationWarning>
|
||||||
{children}
|
<body
|
||||||
|
className={`${inter.className} min-h-screen bg-background text-foreground`}
|
||||||
|
>
|
||||||
|
<div className="flex min-h-screen">
|
||||||
|
{/* Sidebar */}
|
||||||
|
<aside className="w-64 bg-primary text-primary-foreground p-4">
|
||||||
|
<h1 className="text-xl font-bold mb-6">📂 DMS</h1>
|
||||||
|
<nav className="space-y-2">
|
||||||
|
<a
|
||||||
|
href="/dashboard"
|
||||||
|
className="block px-3 py-2 rounded hover:bg-accent"
|
||||||
|
>
|
||||||
|
Dashboard
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/correspondences"
|
||||||
|
className="block px-3 py-2 rounded hover:bg-accent"
|
||||||
|
>
|
||||||
|
Correspondences
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/users"
|
||||||
|
className="block px-3 py-2 rounded hover:bg-accent"
|
||||||
|
>
|
||||||
|
Users
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/health"
|
||||||
|
className="block px-3 py-2 rounded hover:bg-accent"
|
||||||
|
>
|
||||||
|
Health
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<main className="flex-1 flex flex-col">
|
||||||
|
{/* Top Navbar */}
|
||||||
|
<header className="h-14 bg-secondary text-secondary-foreground flex items-center justify-between px-6 shadow-sm">
|
||||||
|
<span className="font-medium">Laem Chabang Port Phase 3</span>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<button className="px-3 py-1 rounded bg-accent text-accent-foreground hover:opacity-90">
|
||||||
|
Quick Action
|
||||||
|
</button>
|
||||||
|
<span className="text-sm">superadmin</span>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section className="p-6 flex-1">{children}</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,64 @@
|
|||||||
// app/page.jsx
|
// app/page.jsx
|
||||||
import { redirect } from "next/navigation";
|
"use client";
|
||||||
export default function Home() {
|
|
||||||
redirect("/dashboard");
|
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 (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<h2 className="text-2xl font-bold">Welcome to DMS</h2>
|
||||||
|
|
||||||
|
<Tabs defaultValue="overview" className="w-full">
|
||||||
|
<TabsList>
|
||||||
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||||
|
<TabsTrigger value="activity">Activity</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
<TabsContent value="overview">
|
||||||
|
<div className="grid md:grid-cols-3 gap-4 mt-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>📑 RFAs</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-3xl font-bold">24</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>📐 Drawings</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-3xl font-bold">112</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>📤 Transmittals</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-3xl font-bold">8</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="activity">
|
||||||
|
<div className="mt-4 space-y-3">
|
||||||
|
<p>
|
||||||
|
✔️ User <b>editor01</b> uploaded Drawing D-2025-07
|
||||||
|
</p>
|
||||||
|
<p>✔️ Transmittal T-2025-02 issued to Contractor</p>
|
||||||
|
<p>✔️ RFA R-2025-03 marked as Resolved</p>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
<Button className="mt-6">Go to Dashboard</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,23 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
reactStrictMode: true,
|
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;
|
module.exports = nextConfig;
|
||||||
@@ -1,34 +1,40 @@
|
|||||||
{
|
{
|
||||||
"name": "dms-frontend",
|
"name": "dms-frontend",
|
||||||
"version": "0.6.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engines": {
|
|
||||||
"node": ">=20.0.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 3000",
|
"dev": "next dev -p 3000",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start -p 3000",
|
"start": "next start -p 3000",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"shadcn": "shadcn"
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"class-variance-authority": "^0.7.1",
|
"next": "15.0.3",
|
||||||
"clsx": "^2.1.1",
|
|
||||||
"framer-motion": "^11.18.2",
|
|
||||||
"lucide-react": "^0.441.0",
|
|
||||||
"next": "^15.5.3",
|
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwindcss": "3.4.14",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"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": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.4.21",
|
"eslint": "9.13.0",
|
||||||
"eslint": "^9.9.0",
|
"eslint-config-next": "15.0.3",
|
||||||
"eslint-config-next": "15.0.1",
|
"prettier": "3.3.3"
|
||||||
"postcss": "^8.5.6",
|
},
|
||||||
"shadcn": "^3.3.1",
|
"engines": {
|
||||||
"tailwindcss": "^3.4.17"
|
"node": ">=20.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,115 +1,74 @@
|
|||||||
/** @type {import("tailwindcss").Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
darkMode: ["class"],
|
darkMode: ["class"],
|
||||||
content: [
|
content: [
|
||||||
"./app/**/*.{js,jsx}",
|
"./app/**/*.{js,jsx,ts,tsx,mdx}",
|
||||||
"./pages/**/*.{js,jsx}",
|
"./components/**/*.{js,jsx,ts,tsx,mdx}",
|
||||||
"./components/**/*.{js,jsx}",
|
"./pages/**/*.{js,jsx,ts,tsx,mdx}", // เผื่อมีโฟลเดอร์ pages
|
||||||
"./src/**/*.{js,jsx}"
|
"./src/**/*.{js,jsx,ts,tsx,mdx}", // เผื่อคุณเก็บ component ใน src
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
|
container: {
|
||||||
|
center: true,
|
||||||
|
padding: "2rem",
|
||||||
|
screens: { "2xl": "1400px" },
|
||||||
|
},
|
||||||
extend: {
|
extend: {
|
||||||
borderRadius: {
|
|
||||||
lg: 'var(--radius)',
|
|
||||||
md: 'calc(var(--radius) - 2px)',
|
|
||||||
sm: 'calc(var(--radius) - 4px)'
|
|
||||||
},
|
|
||||||
colors: {
|
colors: {
|
||||||
background: 'hsl(var(--background))',
|
border: "hsl(var(--border))",
|
||||||
foreground: 'hsl(var(--foreground))',
|
input: "hsl(var(--input))",
|
||||||
card: {
|
ring: "hsl(var(--ring))",
|
||||||
DEFAULT: 'hsl(var(--card))',
|
background: "hsl(var(--background))",
|
||||||
foreground: 'hsl(var(--card-foreground))'
|
foreground: "hsl(var(--foreground))",
|
||||||
},
|
|
||||||
popover: {
|
|
||||||
DEFAULT: 'hsl(var(--popover))',
|
|
||||||
foreground: 'hsl(var(--popover-foreground))'
|
|
||||||
},
|
|
||||||
primary: {
|
primary: {
|
||||||
DEFAULT: 'hsl(var(--primary))',
|
DEFAULT: "hsl(var(--primary))",
|
||||||
foreground: 'hsl(var(--primary-foreground))'
|
foreground: "hsl(var(--primary-foreground))",
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
DEFAULT: 'hsl(var(--secondary))',
|
DEFAULT: "hsl(var(--secondary))",
|
||||||
foreground: 'hsl(var(--secondary-foreground))'
|
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: {
|
destructive: {
|
||||||
DEFAULT: 'hsl(var(--destructive))',
|
DEFAULT: "hsl(var(--destructive))",
|
||||||
foreground: 'hsl(var(--destructive-foreground))'
|
foreground: "hsl(var(--destructive-foreground))",
|
||||||
},
|
},
|
||||||
border: 'hsl(var(--border))',
|
muted: {
|
||||||
input: 'hsl(var(--input))',
|
DEFAULT: "hsl(var(--muted))",
|
||||||
ring: 'hsl(var(--ring))',
|
foreground: "hsl(var(--muted-foreground))",
|
||||||
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))'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
plugins: [require("tailwindcss-animate")]
|
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")],
|
||||||
};
|
};
|
||||||
"EOF"
|
|
||||||
|
|
||||||
cat > postcss.config.js <<EOF
|
|
||||||
module.exports = {
|
|
||||||
plugins: { tailwindcss: {}, autoprefixer: {} }
|
|
||||||
};
|
|
||||||
"EOF"
|
|
||||||
|
|
||||||
cat > app/globals.css <<EOF
|
|
||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
:root { --radius: 0.5rem; }
|
|
||||||
@layer base { html { -webkit-font-smoothing: antialiased; } }
|
|
||||||
"EOF"
|
|
||||||
|
|
||||||
cat > jsconfig.json <<EOF
|
|
||||||
{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["*"] } } }
|
|
||||||
"EOF"
|
|
||||||
|
|
||||||
cat > components.json <<EOF
|
|
||||||
{
|
|
||||||
"": "https://ui.shadcn.com/schema.json",
|
|
||||||
"style": "default",
|
|
||||||
"rsc": true,
|
|
||||||
"tsx": false,
|
|
||||||
"tailwind": {
|
|
||||||
"config": "tailwind.config.js",
|
|
||||||
"css": "app/globals.css",
|
|
||||||
"baseColor": "slate",
|
|
||||||
"cssVariables": true
|
|
||||||
},
|
|
||||||
"aliases": {
|
|
||||||
"components": "@/components",
|
|
||||||
"utils": "@/lib/utils"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"EOF"
|
|
||||||
|
|
||||||
cat > lib/utils.js <<EOF
|
|
||||||
import { clsx } from "clsx";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
export function cn(...inputs){ return twMerge(clsx(inputs)); }
|
|
||||||
"EOF"
|
|
||||||
|
|
||||||
npx -p shadcn@latest shadcn init -t next -y
|
|
||||||
npx -p shadcn@latest shadcn add -y button badge card input tabs progress dropdown-menu tooltip switch
|
|
||||||
|
|
||||||
chown -R 1000:1000 /app
|
|
||||||
|
|
||||||
echo "✅ shadcn + tailwind generated under /app"
|
|
||||||
ls -la components/ui | head
|
|
||||||
|
|||||||
Reference in New Issue
Block a user