// frontend/app//(protected)/dashboard/page.jsx "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"; const sea = { light: "#E6F7FB", light2: "#F3FBFD", mid: "#2A7F98", dark: "#0D5C75", textDark: "#0E2932", }; const can = (user, perm) => new Set(user?.permissions || []).has(perm); const Tag = ({ children }) => ( {children} ); const SidebarItem = ({ label, icon: Icon, active = false, badge }) => ( ); const KPI = ({ label, value, icon: Icon, onClick }) => (
{label}
{value}
); function PreviewDrawer({ open, onClose, children }) { return (
รายละเอียด
{children}
); } export default function DashboardPage() { 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 (
Document Management System
โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 — ส่วนที่ 1–4
Phase 3 Port 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
{sidebarOpen && ( )}
{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
กิจกรรมล่าสุด
Admin Editor Viewer
{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
setPreviewOpen(false)}>
รหัส: RFA-LCP3-0013
ชื่อเรื่อง:{" "} ยืนยันรายละเอียดท่อระบายน้ำ
สถานะ: Pending
แนบไฟล์: 2 รายการ (PDF, DWG)
{can(user, "rfa:create") && ( )}
); }