"use client"; import React from "react"; import Link from "next/link"; import { motion } from "framer-motion"; import { LayoutDashboard, FileText, Files, Send, Layers, Users, Settings, Activity, Search, ChevronRight, ShieldCheck, Workflow, Database, Mail, Server, Shield, BookOpen, PanelLeft, PanelRight, ChevronDown, Plus, Filter, Eye, EyeOff, SlidersHorizontal, Columns3, X, ExternalLink } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { Progress } from "@/components/ui/progress"; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel } from "@/components/ui/dropdown-menu"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { Switch } from "@/components/ui/switch"; import { API_BASE } from "@/lib/api"; // Sea palette const sea = { light: "#E6F7FB", light2: "#F3FBFD", mid: "#2A7F98", dark: "#0D5C75", textDark: "#0E2932" }; // RBAC helper (client-side; backend truth comes from /auth/me) function can(user, perm){ const set = new Set(user?.permissions || []); return set.has(perm); } function Tag({ children }) { return ( {children} ); } function SidebarItem({ label, icon: Icon, active=false, badge }) { return ( ); } function KPI({ label, value, icon: Icon, onClick }) { return (
{label}
{value}
); } function PreviewDrawer({ open, onClose, children }) { return (
รายละเอียด
{children}
); } export default function DashboardPage(){ // ดึง user จริงจาก /auth/me (layout ฝั่ง SSR ตรวจสิทธิ์แล้ว) const [user, setUser] = React.useState(null); const [sidebarOpen, setSidebarOpen] = React.useState(true); const [densityCompact, setDensityCompact] = React.useState(false); const [showCols, setShowCols] = React.useState({ type:true, id:true, title:true, status:true, due:true, owner:true, actions:true }); const [previewOpen, setPreviewOpen] = React.useState(false); const [filters, setFilters] = React.useState({ type:"All", status:"All", overdue:false }); const [activeQuery, setActiveQuery] = React.useState({}); React.useEffect(()=>{ fetch(`${API_BASE}/auth/me`, { credentials: "include" }) .then(r=> r.ok ? r.json() : null) .then(data => setUser(data?.user || null)) .catch(()=> setUser(null)); },[]); const quickLinks = [ { label: "สร้าง RFA", icon: FileText, perm: "rfa:create", href: "/rfas/new" }, { label: "อัปโหลด Drawing", icon: Layers, perm: "drawing:upload", href: "/drawings/upload" }, { label: "สร้าง Transmittal", icon: Send, perm: "transmittal:create", href: "/transmittals/new" }, { label: "บันทึกหนังสือสื่อสาร", icon: Mail, perm: "correspondence:create", href: "/correspondences/new" }, ]; const nav = [ { label: "แดชบอร์ด", icon: LayoutDashboard }, { label: "Drawings", icon: Layers }, { label: "RFAs", icon: FileText }, { label: "Transmittals", icon: Send }, { label: "Contracts & Volumes", icon: BookOpen }, { label: "Correspondences", icon: Files }, { label: "ผู้ใช้/บทบาท", icon: Users, perm: "users:manage" }, { label: "Reports", icon: Activity }, { label: "Workflow (n8n)", icon: Workflow, perm: "workflow:view" }, { label: "Health", icon: Server, perm: "health:view" }, { label: "Admin", icon: Settings, perm: "admin:view" }, ]; const kpis = [ { key: "rfa-pending", label: "RFAs รออนุมัติ", value: 12, icon: FileText, query: { type: "RFA", status: "pending" } }, { key: "drawings", label: "แบบ (Drawings) ล่าสุด", value: 326, icon: Layers, query: { type: "Drawing" } }, { key: "trans-month", label: "Transmittals เดือนนี้", value: 18, icon: Send, query: { type: "Transmittal", month: "current" } }, { key: "overdue", label: "เกินกำหนด (Overdue)", value: 5, icon: Activity, query: { overdue: true } }, ]; const recent = [ { type: "RFA", code: "RFA-LCP3-0012", title: "ปรับปรุงรายละเอียดเสาเข็มท่าเรือ", who: "สุรเชษฐ์ (Editor)", when: "เมื่อวาน 16:40" }, { type: "Drawing", code: "DWG-C-210A-Rev.3", title: "แปลนโครงสร้างท่าเรือส่วนที่ 2", who: "วรวิชญ์ (Admin)", when: "วันนี้ 09:15" }, { type: "Transmittal", code: "TR-2025-0916-04", title: "ส่งแบบ Rebar Shop Drawing ชุด A", who: "Supansa (Viewer)", when: "16 ก.ย. 2025" }, { type: "Correspondence", code: "CRSP-58", title: "แจ้งเลื่อนประชุมตรวจแบบ", who: "Kitti (Editor)", when: "15 ก.ย. 2025" }, ]; const items = [ {t:"RFA", id:"RFA-LCP3-0013", title:"ยืนยันรายละเอียดท่อระบายน้ำ", status:"Pending", due:"20 ก.ย. 2025", owner:"คุณแดง"}, {t:"Drawing", id:"DWG-S-115-Rev.1", title:"Section เสาเข็มพื้นที่ส่วนที่ 1", status:"Review", due:"19 ก.ย. 2025", owner:"วิทยา"}, {t:"Transmittal", id:"TR-2025-0915-03", title:"ส่งแบบโครงสร้างท่าเรือ ชุด B", status:"Sent", due:"—", owner:"สุธิดา"}, ]; const visibleItems = items.filter(r => { if (filters.type !== "All" && r.t !== filters.type) return false; if (filters.status !== "All" && r.status !== filters.status) return false; if (filters.overdue && r.due === "—") return false; return true; }); const onKpiClick = (q) => { setActiveQuery(q); if (q?.type) setFilters(f => ({...f, type:q.type})); if (q?.overdue) setFilters(f => ({...f, overdue:true})); }; return (
{/* Top System + Quick Actions */}
Document Management System
โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 — ส่วนที่ 1–4
Phase 3Port Infrastructure ระบบ {can(user,'admin:view') && Admin} {can(user,'users:manage') && ผู้ใช้/บทบาท} {can(user,'health:view') && Health } {can(user,'workflow:view') && Workflow (n8n) } {quickLinks.map(({label, icon:Icon, perm, href}) => ( can(user,perm) ? ( {label} ) : (
{label}
ไม่มีสิทธิ์ใช้งาน ({perm})
) ))} Import / Bulk upload
{/* Sidebar */} {sidebarOpen && ( )} {/* Content */}
{kpis.map((k)=> onKpiClick(k.query)} />)}
ผลลัพธ์จากตัวกรอง: {filters.type}/{filters.status}{filters.overdue?" • Overdue":""}
{Object.keys(showCols).map(key => ( setShowCols(s=>({...s, [key]: !s[key]}))}> {(showCols[key]?:)}{key} ))}
{showCols.type && } {showCols.id && } {showCols.title && } {showCols.status && } {showCols.due && } {showCols.owner && } {showCols.actions && } {visibleItems.length === 0 && ()} {visibleItems.map(row => ( setPreviewOpen(true)}> {showCols.type && } {showCols.id && } {showCols.title && } {showCols.status && } {showCols.due && } {showCols.owner && } {showCols.actions && } ))}
ประเภทรหัสชื่อเรื่องสถานะกำหนดส่งผู้รับผิดชอบจัดการ
ไม่พบรายการตามตัวกรองที่เลือก
{row.t}{row.id}{row.title}{row.status}{row.due}{row.owner}
เคล็ดลับ: ใช้ปุ่ม ↑/↓ เลื่อนแถว, Enter เปิด, / โฟกัสค้นหา
ภาพรวม รายงาน
สถานะโครงการ
Phase 3 • ส่วนที่ 1–4
ความคืบหน้าโดยรวม
ส่วนที่ 1
เสร็จ 70%
ส่วนที่ 2
เสร็จ 58%
ส่วนที่ 3–4
เสร็จ 59%
System Health
QNAP • Container Station
Nginx Reverse Proxy Healthy
MariaDB 10.11 OK
n8n (Postgres) OK
RBAC Enforcement Enabled
กิจกรรมล่าสุด
AdminEditorViewer
{recent.map((r)=> (
{r.type} • {r.code}
{r.title}
{r.who}
{r.when}
))}
Report A: RFA → Drawings → Revisions
รวมทุก Drawing Revision + Code
Report B: ไทม์ไลน์ RFA vs Drawing Rev
อิง Query #2 ที่กำหนดไว้
Sea-themed Dashboard • Sidebar ซ่อนได้ • RBAC แสดง/ซ่อน • Faceted search • KPI click-through • Preview drawer • Column visibility/Density
{/* Drawer */} setPreviewOpen(false)}>
รหัส: RFA-LCP3-0013
ชื่อเรื่อง: ยืนยันรายละเอียดท่อระบายน้ำ
สถานะ: Pending
แนบไฟล์: 2 รายการ (PDF, DWG)
{can(user,'rfa:create') && }
); } สิ่งสำคัญในไฟล์: Topbar: ปุ่ม System (Admin/Users/Health/Workflow) + New (RFA/Transmittal/Correspondence/Upload Drawing) — ซ่อน/ปิดตาม permissions Sidebar: Search + ตัวกรอง (Type/Status/Overdue) + เมนูพร้อม badge Main: KPI การ์ดคลิกได้ → sync ฟิลเตอร์, ตารางพร้อม Density toggle / Column visibility, และ Preview Drawer ใช้ API_BASE จาก @/lib/api (คอนเทนเนอร์ตั้งค่า NEXT_PUBLIC_API_BASE) คงธีมโทนน้ำทะเลเข้ม/อ่อนตามที่กำหน