// File: src/app/(protected)/dashboard/page.tsx "use client"; /** * EOP Dashboard — Commerce + Marketplace + Chat + IoT + AI Orchestration * - สรุปภาพรวมยอดขาย (GMV/Orders/AOV/Refunds) * - Channel Mix (Marketplace / D2C / Social/Chat) * - Best Sellers & Low Stock * - Live Ops: Fulfillment SLA, IoT Device Health * - AI Flows: ล่าสุด/สถานะรัน, Queue/Latency * - Alerts/Tasks * * NOTE: ตอนนี้ใช้ mock data เพื่อโชว์ design/ UX. * เชื่อม API ได้โดยแทนที่ hooks ด้านล่าง (fetch/*) ให้ดึงจริงจาก proxy ของคุณ */ import { useMemo, useState } from "react"; /* =========================== Types (ภายในไฟล์) =========================== */ type Range = "today" | "7d" | "30d"; type Channel = "all" | "shopee" | "lazada" | "tiktok" | "d2c" | "chat"; type Kpi = { label: string; value: number | string; delta?: number; hint?: string; suffix?: string }; type MixItem = { label: string; value: number; key: Channel }; type Sku = { sku: string; name: string; units: number; gmv: number; stock?: number; thumbnail?: string }; type Fulfillment = { metric: string; value: string; delta?: number; hint?: string }; type Device = { id: string; site: string; role: "scanner" | "labeler" | "sorter" | "gateway"; status: "ok" | "warn" | "down"; lastSeen: string }; type Flow = { id: string; name: string; runsToday: number; successRate: number; avgLatencyMs: number; lastRun: string; status: "ok" | "warn" | "error" }; type Alert = { id: string; title: string; detail: string; when: string; level: "info" | "warn" | "error" }; /* =========================== Mock Data =========================== */ const KPI_BY_RANGE: Record = { today: [ { label: "GMV", value: 186_420, delta: +6.8, suffix: "฿", hint: "รวมทุกช่องทาง" }, { label: "Orders", value: 942, delta: +4.3 }, { label: "AOV", value: 198, delta: +2.1, suffix: "฿" }, { label: "Refunds", value: 1.2, delta: -0.2, suffix: "%", hint: "อัตราคืนสินค้า" }, ], "7d": [ { label: "GMV", value: 1_126_500, delta: +8.9, suffix: "฿" }, { label: "Orders", value: 5_814, delta: +5.1 }, { label: "AOV", value: 194, delta: +1.4, suffix: "฿" }, { label: "Refunds", value: 1.6, delta: -0.1, suffix: "%" }, ], "30d": [ { label: "GMV", value: 4_812_900, delta: +12.3, suffix: "฿" }, { label: "Orders", value: 24_310, delta: +7.7 }, { label: "AOV", value: 198, delta: +2.0, suffix: "฿" }, { label: "Refunds", value: 1.4, delta: -0.4, suffix: "%" }, ], }; const MIX_ALL: MixItem[] = [ { label: "Shopee", value: 34, key: "shopee" }, { label: "Lazada", value: 28, key: "lazada" }, { label: "TikTok", value: 16, key: "tiktok" }, { label: "Direct (D2C)", value: 12, key: "d2c" }, { label: "Social/Chat", value: 10, key: "chat" }, ]; const BEST_SELLERS: Sku[] = [ { sku: "CDIOR-50ML", name: "Dior Lip Glow 001 Pink", units: 382, gmv: 145_000, stock: 128 }, { sku: "CDIOR-999", name: "Dior Rouge 999 Velvet", units: 274, gmv: 118_500, stock: 42 }, { sku: "CDIOR-FOUND", name: "Forever Skin Glow 1N", units: 190, gmv: 86_200, stock: 18 }, { sku: "CDIOR-MASC", name: "Diorshow Iconic Overcurl", units: 156, gmv: 62_700, stock: 9 }, { sku: "CDIOR-SET", name: "Holiday Gift Set 2025", units: 120, gmv: 96_000, stock: 6 }, ]; const LOW_STOCK: Sku[] = [ { sku: "CDIOR-999", name: "Dior Rouge 999 Velvet", units: 274, gmv: 118_500, stock: 42 }, { sku: "CDIOR-FOUND", name: "Forever Skin Glow 1N", units: 190, gmv: 86_200, stock: 18 }, { sku: "CDIOR-MASC", name: "Diorshow Iconic Overcurl", units: 156, gmv: 62_700, stock: 9 }, { sku: "CDIOR-SET", name: "Holiday Gift Set 2025", units: 120, gmv: 96_000, stock: 6 }, ]; const SLA: Fulfillment[] = [ { metric: "SLA Ship-on-time", value: "97.2%", delta: +0.4, hint: "ภายใน 24ชม." }, { metric: "Picked in 2h", value: "92.5%", delta: -1.1 }, { metric: "Packaging Defects", value: "0.7%", delta: -0.2 }, { metric: "Delivery < 48h", value: "88.9%", delta: +2.8 }, ]; const DEVICES: Device[] = [ { id: "GW-01", site: "BKK-DC", role: "gateway", status: "ok", lastSeen: "just now" }, { id: "SC-12", site: "BKK-DC", role: "scanner", status: "ok", lastSeen: "12s ago" }, { id: "LB-03", site: "BKK-DC", role: "labeler", status: "warn", lastSeen: "1m ago" }, { id: "ST-07", site: "BKK-DC", role: "sorter", status: "ok", lastSeen: "5s ago" }, { id: "SC-02", site: "CNX-HUB", role: "scanner", status: "down", lastSeen: "14m ago" }, ]; const FLOWS: Flow[] = [ { id: "F-CHAT-01", name: "Chat → Quote → Order", runsToday: 482, successRate: 96.8, avgLatencyMs: 740, lastRun: "1m", status: "ok" }, { id: "F-ROUTER-02", name: "Marketplace Router", runsToday: 3120, successRate: 99.2, avgLatencyMs: 210, lastRun: "15s", status: "ok" }, { id: "F-VISION-01", name: "IoT Vision QC", runsToday: 260, successRate: 92.4, avgLatencyMs: 980, lastRun: "2m", status: "warn" }, { id: "F-RMA-01", name: "Returns Classifier", runsToday: 58, successRate: 88.3, avgLatencyMs: 1320, lastRun: "4m", status: "error" }, ]; const ALERTS: Alert[] = [ { id: "al1", title: "Low stock: Holiday Gift Set", detail: "Stock: 6 units • risk OOS in 2 days", when: "just now", level: "warn" }, { id: "al2", title: "AI Flow degraded: IoT Vision QC", detail: "Latency > 1s for last 15 mins", when: "5m ago", level: "error" }, { id: "al3", title: "Channel API quota nearing limit", detail: "TikTok Shop at 83% for today", when: "18m ago", level: "warn" }, ]; /* =========================== Helpers =========================== */ const fmt = (n: number | string) => typeof n === "number" ? n.toLocaleString() : n; const THB = (n: number) => `฿${n.toLocaleString()}`; const sign = (d?: number) => (d && d > 0 ? "+" : d && d < 0 ? "–" : ""); const barWidth = (n: number) => Math.max(6, Math.min(100, n)); function chipStatus(ok: boolean | "warn" | "error") { if (ok === "warn") return "bg-amber-50 text-amber-700 border border-amber-200"; if (ok === "error") return "bg-red-50 text-red-700 border border-red-200"; return "bg-emerald-50 text-emerald-700 border border-emerald-200"; } function deviceDot(s: Device["status"]) { return s === "ok" ? "bg-emerald-500" : s === "warn" ? "bg-amber-500" : "bg-red-500"; } /* =========================== Component =========================== */ export default function DashboardPage() { const [range, setRange] = useState("7d"); const [channel, setChannel] = useState("all"); const kpis = useMemo(() => KPI_BY_RANGE[range], [range]); const mix = useMemo(() => MIX_ALL, []); const topSkus = useMemo(() => BEST_SELLERS, []); const lowStock = useMemo(() => LOW_STOCK, []); const sla = useMemo(() => SLA, []); const devices = useMemo(() => DEVICES, []); const flows = useMemo(() => FLOWS, []); const alerts = useMemo(() => ALERTS, []); return (

EOP Dashboard

Commerce overview — marketplaces, social/chat, IoT & AI orchestration

{(["today", "7d", "30d"] as const).map((r) => ( ))}
{(["all", "shopee", "lazada", "tiktok", "d2c", "chat"] as const).map((c) => ( ))}
{/* KPI Cards */}
{kpis.map((k) => (
{k.label}{k.hint ? ` • ${k.hint}` : ""}
{k.suffix === "฿" ? THB(Number(k.value)) : `${fmt(k.value)}${k.suffix ?? ""}`}
{typeof k.delta === "number" && (
= 0 ? "bg-emerald-50 text-emerald-700 border border-emerald-200" : "bg-red-50 text-red-700 border border-red-200", ].join(" ")} > {sign(k.delta)} {Math.abs(k.delta).toFixed(1)}%
)}
))}
{/* Channel Mix + Top SKUs */}
{/* Channel Mix */}

Channel mix

by GMV
    {mix.map((m) => (
  • {m.label}
    {m.value}%
  • ))}
{/* Best Sellers */}

Best sellers

{topSkus.map((s) => ( ))}
SKU Name Units GMV Stock
{s.sku} {s.name} {fmt(s.units)} {THB(s.gmv)} {fmt(s.stock ?? 0)}
{/* Low stock banner */}
Low stock:{" "} {lowStock .filter((x) => (x.stock ?? 0) <= 20) .slice(0, 3) .map((x) => `${x.name} (${x.stock})`) .join(", ")}{" "} — plan replenishment & marketplace buffers
{/* Fulfillment & IoT */}
{/* Fulfillment SLA */}

Fulfillment SLA

warehouse & delivery
    {sla.map((f) => (
  • {f.metric}
    {typeof f.delta === "number" && ( = 0 ? "bg-emerald-50 text-emerald-700 border border-emerald-200" : "bg-red-50 text-red-700 border border-red-200", ].join(" ")} > {sign(f.delta)} {Math.abs(f.delta).toFixed(1)}% )} {f.value}
    {f.hint &&
    {f.hint}
    }
  • ))}
{/* IoT Device Health */}

IoT device health

    {devices.map((d) => (
  • {d.id} • {d.role}
    {d.site} • {d.lastSeen}
    {d.status}
  • ))}
{/* AI Orchestration & Alerts */}
{/* AI Flows */}

AI flows

{flows.map((f) => ( ))}
Flow Runs (today) Success Avg Latency Status
{f.name}
last: {f.lastRun} ago
{fmt(f.runsToday)} {f.successRate.toFixed(1)}% {f.avgLatencyMs} ms {f.status}
{/* Alerts / Tasks */}

Alerts

    {alerts.map((a) => (
  • {a.title}
    {a.detail}
    {a.when}
  • ))}
{/* Footer action row */}
); }