"use client"; import { useQuery } from "@tanstack/react-query"; import { Button, Card, Spinner } from "@heroui/react"; import { Thunderbolt, PlugConnection, CreditCard, ChartColumn, TagDollar, Person, ArrowRotateRight, } from "@gravity-ui/icons"; import { useSession } from "@/lib/auth-client"; import { api, type Stats, type UserStats, type Transaction, type ChargePoint } from "@/lib/api"; // ── Helpers ──────────────────────────────────────────────────────────────── function timeAgo(dateStr: string | null | undefined): string { if (!dateStr) return "—"; const ms = Date.now() - new Date(dateStr).getTime(); if (ms < 60_000) return "刚刚"; if (ms < 3_600_000) return `${Math.floor(ms / 60_000)} 分钟前`; if (ms < 86_400_000) return `${Math.floor(ms / 3_600_000)} 小时前`; return new Date(dateStr).toLocaleDateString("zh-CN", { month: "short", day: "numeric" }); } function cpOnline(cp: ChargePoint): boolean { if (!cp.lastHeartbeatAt) return false; return Date.now() - new Date(cp.lastHeartbeatAt).getTime() < 120_000; } // ── StatCard ─────────────────────────────────────────────────────────────── type CardColor = "accent" | "success" | "warning" | "default"; const colorStyles: Record = { accent: { border: "border-accent", bg: "bg-accent/10", icon: "text-accent" }, success: { border: "border-success", bg: "bg-success/10", icon: "text-success" }, warning: { border: "border-warning", bg: "bg-warning/10", icon: "text-warning" }, default: { border: "border-border", bg: "bg-default", icon: "text-muted" }, }; function StatusDot({ color }: { color: "success" | "warning" | "muted" }) { const cls = color === "success" ? "bg-success" : color === "warning" ? "bg-warning" : "bg-muted/40"; return ; } function StatCard({ title, value, footer, icon: Icon, color = "default", }: { title: string; value: string | number; footer?: React.ReactNode; icon?: React.ComponentType<{ className?: string }>; color?: CardColor; }) { const s = colorStyles[color]; return (

{title}

{Icon && (
)}

{value}

{footer && (
{footer}
)}
); } // ── Panel wrapper ────────────────────────────────────────────────────────── function Panel({ title, children }: { title: string; children: React.ReactNode }) { return (

{title}

{children}
); } // ── RecentTransactions ──────────────────────────────────────────────────── function RecentTransactions({ txns }: { txns: Transaction[] }) { if (txns.length === 0) { return
暂无充电记录
; } return ( ); } // ── ChargePointStatus ───────────────────────────────────────────────────── function ChargePointStatus({ cps }: { cps: ChargePoint[] }) { if (cps.length === 0) { return
暂无充电桩
; } return ( ); } // ── Page ─────────────────────────────────────────────────────────────────── export default function DashboardPage() { const { data: sessionData, isPending } = useSession(); const isAdmin = sessionData?.user?.role === "admin"; const { data, isPending: queryPending, isFetching: refreshing, refetch } = useQuery({ queryKey: ["dashboard", isAdmin], queryFn: async () => { const [statsRes, txRes, cpsData] = await Promise.all([ api.stats.get(), api.transactions.list({ limit: 6 }), isAdmin ? api.chargePoints.list() : Promise.resolve([] as ChargePoint[]), ]); return { stats: statsRes, txns: txRes.data, cps: cpsData }; }, refetchInterval: 3_000, enabled: !isPending, }); if (isPending || queryPending) { return (

概览

加载中…

); } // ── Admin view ──────────────────────────────────────────────────────────── if (isAdmin) { const s = data?.stats as Stats | undefined; const todayKwh = s ? (s.todayEnergyWh / 1000).toFixed(1) : "—"; const todayRevenue = s ? `¥${(s.todayRevenue / 100).toFixed(2)}` : "—"; const offlineCount = (s?.totalChargePoints ?? 0) - (s?.onlineChargePoints ?? 0); return (

概览

实时运营状态

{/* Today's live metrics */}
当日 00:00 起累计} /> 当日累计输出电能} /> 已完成订单金额合计} /> {s?.activeTransactions ? "充电进行中" : "当前空闲"} } />
{/* Infrastructure metrics */}
{s?.onlineChargePoints ?? 0} 在线 · {offlineCount} 离线 } /> 已注册卡片总量} /> 系统用户总数} />
{/* Detail panels */}
); } // ── User view ───────────────────────────────────────────────────────────── const s = data?.stats as UserStats | undefined; const totalYuan = s ? (s.totalBalance / 100).toFixed(2) : "—"; const todayKwh = s ? (s.todayEnergyWh / 1000).toFixed(2) : "—"; return (

概览

{sessionData?.user?.name ?? sessionData?.user?.email} 的账户概览

已绑定的储值卡数量} /> 所有储值卡余额合计} /> {s?.activeTransactions ? "充电中" : "当前空闲"} } /> 今日 {s?.todayTransactions ?? 0} 次} />
); }