import { useState, useRef } from "react"; import { useInView } from "@/lib/useInView"; import { Users, Wifi, Zap, Server, ArrowUpRight, Power, RotateCcw, Tag, Shield, Radio, Gauge, TrendingUp, ArrowDown, ArrowUp, } from "lucide-react"; import { Link } from "react-router-dom"; import { motion } from "framer-motion"; import { useOverview, useSystem, useTrafficSeries, useRestartCore, useStopCore, type TrafficRange, } from "@/api/policy-hooks"; import { useAccountQuota } from "@/api/quota-hooks"; import { useNodes, useVersion } from "@/api/hooks"; import { useAuth } from "@/auth/auth"; import { Card } from "@/components/ui"; import { useLiveTraffic } from "@/hooks/useLiveTraffic"; import { LiveIndicator } from "@/components/LiveIndicator"; import { LiveTrafficAreaChart } from "@/components/Charts"; import { GlassCard, StatsCard, StatusBadge, ProtocolDonutChart, formatDailyBandwidth, } from "@/components/vortexui"; import { useI18n } from "@/i18n/i18n"; import { useTitle } from "@/lib/useTitle"; import { AnimatedCounter } from "@/components/AnimatedCounter"; import { cn, formatBytes, formatSpeed } from "@/lib/utils"; import { SystemGauges } from "@/components/SystemGauges"; function fmtUptime(sec: number): string { const d = Math.floor(sec / 86400); const h = Math.floor((sec % 86400) / 3600); const m = Math.floor((sec % 3600) / 60); if (d > 0) return `${d}d ${h}h ${m}m`; if (h > 0) return `${h}h ${m}m`; return `${m}m`; } function daysUntil(iso: string | null): string { if (!iso) return "∞"; const d = Math.ceil((new Date(iso).getTime() - Date.now()) / 86400000); if (d < 0) return "expired"; return `${d}d`; } /* ── SpeedLabel — animated real-time speed display ── */ function SpeedLabel({ value, color }: { value: number; color: string }) { const display = value < 0.5 ? "—" : value >= 1_000_000_000 ? `${(value / 1_000_000_000).toFixed(2)} GB/s` : value >= 1_000_000 ? `${(value / 1_000_000).toFixed(2)} MB/s` : value >= 1_000 ? `${(value / 1_000).toFixed(1)} KB/s` : `${value.toFixed(0)} B/s`; return ( {display} ); } function CoreCard({ name, version, running, onStop, onRestart }: { name: string; version: string; running: boolean; onStop: () => void; onRestart: () => void; }) { return (
{name}
{version && ( {version} )}
); } /* ════════════════════════════════════════════════════ OVERVIEW PAGE ════════════════════════════════════════════════════ */ export function Overview() { useTitle("Overview"); const { sudo } = useAuth(); const statsRef = useRef(null); const statsInView = useInView(statsRef, { once: true }); const accountQuota = useAccountQuota(); const { data, isLoading: overviewLoading } = useOverview(); const sys = useSystem(); const nodesQ = useNodes(); const panelVersion = useVersion().data; const { t } = useI18n(); const [trafficRange, setTrafficRange] = useState("24h"); const trafficSeries = useTrafficSeries(trafficRange); const liveTraffic = useLiveTraffic(trafficRange); const u = data?.users; const onlineCount = data?.nodes.online ?? 0; const totalNodes = data?.nodes.total ?? 0; const byStatus = u?.by_status ?? {}; const totalUsers = u?.total ?? 0; const totalUsed = u?.total_used ?? 0; const s = sys.data; const fleetItems = data?.nodes.items ?? []; const totalConnections = fleetItems.reduce((sum, n) => sum + (n.health?.connections ?? 0), 0); const trafficPoints = trafficSeries.data?.points ?? []; const peakBucket = trafficPoints.length ? Math.max(...trafficPoints.map((p) => p.up + p.down)) : 0; const livePeak = liveTraffic.peakBandwidth; const nodesList = nodesQ.data?.nodes ?? []; const xrayNode = nodesList.find((n) => n.core === "xray"); const singboxNode = nodesList.find((n) => n.core === "singbox"); const xrayVer = xrayNode?.core_version || "—"; const singboxVer = singboxNode?.core_version || "—"; const xrayRunning = xrayNode?.health.core_running ?? false; const singboxRunning = singboxNode?.health.core_running ?? false; const restartCore = useRestartCore(); const stopCore = useStopCore(); const widgets = data?.widgets; const trends = widgets?.trends; const topUsers = widgets?.top_users ?? []; const nodeFleet = widgets?.node_fleet ?? []; const protocolSlices = (widgets?.protocols ?? []).map((p, i) => ({ label: p.label, value: p.count, color: ["#22D3EE", "#3B82F6", "#10B981", "#8B5CF6", "#F59E0B", "#F43F5E"][i % 6], })); const allHealthy = totalNodes > 0 && onlineCount === totalNodes; const standbyNodes = totalNodes - onlineCount; const coreLabel = [ xrayVer !== "—" ? `Xray ${xrayVer}` : null, singboxVer !== "—" ? `sing-box ${singboxVer}` : null, ].filter(Boolean).join(" + "); /* Shield / routing display text */ const probingBlocked = widgets?.probing?.blocked_scanners ?? 0; const probingEnabled = widgets?.probing?.enabled ?? false; const probingText = probingEnabled ? `${probingBlocked.toLocaleString()} DPI Scanners Blocked` : "Probing shield off"; const activeRules = widgets?.routing?.active_rules ?? 0; const routingPacks = widgets?.routing?.routing_packs ?? 0; const routingText = activeRules > 0 ? `${activeRules} Active Rules · ${routingPacks} Packs` : "No routing rules active"; return (
{/* ── HERO ── */} {/* decorative blobs */}
{/* Left — title block */}
{/* Badge row */}
0 ? "warning" : "inactive"} label={allHealthy ? t("overview.allNodesHealthy") : `${onlineCount}/${totalNodes} ${t("overview.online")}`} /> {coreLabel && ( {coreLabel} )}
{/* Title */}

{t("overview.commandTower")} {panelVersion && ( v{panelVersion} )}

{/* Description */}

{overviewLoading || !s ? ( {t("overview.loadingTelemetry")} ) : ( <> Real-time telemetry and anti-censorship control plane running{" "} {allHealthy ? "optimally" : "in partial mode"} across{" "} {totalNodes > 0 ? `${totalNodes} node${totalNodes !== 1 ? "s" : ""}` : "all nodes"}. {" "}Uptime {fmtUptime(s.uptime_seconds)} · {totalConnections} live connections. {peakBucket > 0 && ( {formatBytes(peakBucket, false)}/min )} )}

{/* Right — status cards */}

{t("overview.activeProbingShield")}

{probingText}

{t("overview.smartRoutingRules")}

{routingText}

{/* ── Reseller quota bar ── */} {!sudo && accountQuota.data?.usage && (
{t("reseller.overview.pool")}
{t("reseller.overview.viewDashboard")}
{t("reseller.dashboard.accounts")}: {accountQuota.data.usage.user_count}{accountQuota.data.usage.user_quota > 0 ? ` / ${accountQuota.data.usage.user_quota}` : ""}
{t("reseller.dashboard.assigned")}: {formatBytes(accountQuota.data.usage.traffic_allocated, false)}
{t("reseller.dashboard.consumed")}: {formatBytes(accountQuota.data.usage.traffic_used, false)}
)} {/* ── Live Stat Cards — stagger wave on scroll ── */} } change={trends?.users_pct} icon={} color="cyan" delay={0.05} subLabel={`${(byStatus.active ?? 0).toLocaleString()} ${t("overview.activeShort")}`} sparkline={liveTraffic.chartPoints.slice(-20).map(p => p.down)} sparkColor="#22D3EE" live inView={statsInView} /> } suffix={totalNodes > 0 ? `/ ${totalNodes}` : undefined} change={0} icon={} color="green" delay={0.1} subLabel={standbyNodes > 0 ? `${standbyNodes} ${t("overview.standby")}` : undefined} sparkline={liveTraffic.chartPoints.slice(-20).map(p => p.up)} sparkColor="#10B981" live inView={statsInView} /> formatDailyBandwidth(n)} />} change={trends?.bandwidth_pct} icon={} color="purple" delay={0.15} subLabel={ liveTraffic.speedLabel !== "Idle" ? `${liveTraffic.speedLabel} live` : peakBucket > 0 ? `Peak ${formatBytes(peakBucket, false)}/min` : undefined } sparkline={liveTraffic.chartPoints.slice(-30).map(p => p.down + p.up)} sparkColor="#8B5CF6" progress={liveTraffic.peakBandwidth > 0 ? Math.min(100, (liveTraffic.speedDelta / liveTraffic.peakBandwidth) * 100) : 0} progressColor={liveTraffic.speedDelta > liveTraffic.peakBandwidth * 0.75 ? "danger" : liveTraffic.speedDelta > liveTraffic.peakBandwidth * 0.5 ? "warning" : "success"} live inView={statsInView} /> } change={trends?.sessions_pct} icon={} color="blue" delay={0.2} subLabel={ (byStatus.active ?? 0) > 0 ? `Across ${(byStatus.active ?? 0)} accounts` : t("overview.acrossAccounts") } sparkline={liveTraffic.chartPoints.slice(-20).map(p => p.down + p.up)} sparkColor="#3B82F6" live inView={statsInView} /> {/* ── System Health Indicators ── */}
Nodes: {onlineCount}/{totalNodes}
Connections: {totalConnections}
Xray: {xrayRunning ? "Running" : "Stopped"}
sing-box: {singboxRunning ? "Running" : "Stopped"}
{/* ── Traffic chart + Protocol donut ── */}
{/* Traffic chart — 2/3 width */}

{t("overview.liveTrafficStream")}

{t("overview.trafficDeltaHint")}

{(["24h", "7d", "30d"] as TrafficRange[]).map((r) => ( ))}
{trafficSeries.isLoading ? (
) : (
)} {/* Live Speed & Summary stats */} {(liveTraffic.chartPoints.length > 0 || trafficPoints.length > 0) && ( <> {/* Live speed bar — real-time animated speeds */}
Download {liveTraffic.totalDown > 0 ? ( <>{formatBytes(liveTraffic.totalDown, false)} total ) : '—'}
current
{/* Mini animated bar */}
Upload {liveTraffic.totalUp > 0 ? ( <>{formatBytes(liveTraffic.totalUp, false)} total ) : '—'}
current
{/* Mini animated bar */}
{/* Summary triple */}

Down

{liveTraffic.totalDown > 0 ? formatBytes(liveTraffic.totalDown, false) : formatBytes(trafficPoints.reduce((s, p) => s + p.down, 0), false)}

Up

{liveTraffic.totalUp > 0 ? formatBytes(liveTraffic.totalUp, false) : formatBytes(trafficPoints.reduce((s, p) => s + p.up, 0), false)}

Peak

{livePeak > 0 ? formatSpeed(livePeak) : formatBytes(peakBucket, false) + ("/min")}

)} {/* Protocol breakdown — 1/3 width */}

{t("overview.protocolBreakdown")}

Active connections by transport

{/* ── Node Fleet + Active Users ── */}
{/* Node Fleet Telemetry */}

{t("overview.nodeFleetTelemetry")}

Real-time health · CPU / RAM · connections

View
{overviewLoading ? (
) : nodeFleet.length === 0 ? (

{t("overview.noNodesEnrolled")}

) : (
{nodeFleet.map((node) => { const load = Math.max(node.cpu_percent ?? 0, node.mem_percent ?? 0); const loadColor = load > 75 ? "bg-red-500" : load > 50 ? "bg-amber-400" : "bg-green-500"; const loadBgColor = load > 75 ? "bg-red-500/20" : load > 50 ? "bg-amber-400/20" : "bg-green-500/20"; const loadText = load > 75 ? "text-red-400" : load > 50 ? "text-amber-300" : "text-green-400"; return (

{node.name}

{node.core === "singbox" ? "sing-box" : "Xray"} · {node.location} {node.ping_ms > 0 && `· ${node.ping_ms}ms`}

{node.users_count > 0 && ( {node.users_count} users )}
CPU / RAM {load.toFixed(0)}%
Load {node.cpu_percent?.toFixed(0)}% CPU · {node.mem_percent?.toFixed(0)}% RAM
); })}
)} {/* Active Users Pool */}

{t("overview.activeUsersPool")}

Real-time usage accounting · subscriptions

All
{overviewLoading ? (
) : topUsers.length === 0 ? (

{t("overview.noUsersYet")}

) : (
{topUsers.map((user) => { const usedPct = user.data_limit > 0 ? Math.min(100, (user.used_traffic / user.data_limit) * 100) : 0; return (
{user.username.slice(0, 2).toUpperCase()}

{user.username}

{user.protocol_label || "—"} · Expires in {daysUntil(user.expire_at ?? null)}

{formatBytes(user.used_traffic, false)}

{user.data_limit > 0 && ( <>

/ {formatBytes(user.data_limit, false)}

80 ? "bg-gradient-to-r from-orange-500 to-red-500" : "bg-gradient-to-r from-primary to-accent" )} style={{ width: `${usedPct}%` }} />
)}
{user.data_limit > 0 && usedPct > 60 && (
Usage 80 ? "text-orange-400" : "text-primary")}>{usedPct.toFixed(0)}% used
)}
); })}
)}
{/* ── System Gauges ── */} {/* ── Core Engine Controls ── */} {(xrayNode || singboxNode) && (
{xrayNode && ( restartCore.mutate(xrayNode.id)} onStop={() => stopCore.mutate(xrayNode.id)} /> )} {singboxNode && ( restartCore.mutate(singboxNode.id)} onStop={() => stopCore.mutate(singboxNode.id)} /> )}
)}
); }