Checkpoint: Phase 14: Fixed hardcoded header metrics (UPTIME/NODES/AGENTS/CPU/MEM) — connected to real tRPC dashboard.stats endpoint with 30s polling. Fixed seed idempotency — now checks by isSystem=true instead of total count. Added dashboard.test.ts with 13 new tests. All 82 tests pass.

This commit is contained in:
Manus
2026-03-20 21:00:51 -04:00
parent cbb93a8fdb
commit 16b101537c
6 changed files with 351 additions and 17 deletions

View File

@@ -23,6 +23,7 @@ import {
} from "lucide-react";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { motion, AnimatePresence } from "framer-motion";
import { trpc } from "@/lib/trpc";
const NAV_ITEMS = [
{ path: "/", icon: LayoutDashboard, label: "Дашборд" },
@@ -38,6 +39,30 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
const [location] = useLocation();
const [collapsed, setCollapsed] = useState(false);
// Real-time cluster stats — refresh every 30s
const { data: stats } = trpc.dashboard.stats.useQuery(undefined, {
refetchInterval: 30_000,
staleTime: 25_000,
});
// Format memory: prefer GB if >= 1024 MB
const formatMem = (mb: number) => {
if (mb >= 1024) return `${(mb / 1024).toFixed(1)} GB`;
return `${mb} MB`;
};
// CPU color: green < 60%, amber 60-80%, red > 80%
const cpuColor = (pct: number) =>
pct > 80 ? "text-red-400" : pct > 60 ? "text-neon-amber" : "text-neon-green";
// MEM color: green < 50%, amber 50-80%, red > 80%
const memColor = (mb: number) => {
// We don't know total limit here, use absolute thresholds
if (mb > 6000) return "text-red-400";
if (mb > 3000) return "text-neon-amber";
return "text-neon-green";
};
return (
<div className="flex h-screen overflow-hidden bg-background">
{/* Sidebar */}
@@ -124,16 +149,22 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
{/* Connection status */}
<div className="px-3 py-3 border-t border-border/50">
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-neon-green pulse-indicator" />
<div
className={`w-2 h-2 rounded-full ${
stats?.gatewayOnline ? "bg-neon-green pulse-indicator" : "bg-red-400"
}`}
/>
<AnimatePresence>
{!collapsed && (
<motion.span
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="font-mono text-[11px] text-neon-green"
className={`font-mono text-[11px] ${
stats?.gatewayOnline ? "text-neon-green" : "text-red-400"
}`}
>
GATEWAY ONLINE
{stats?.gatewayOnline ? "GATEWAY ONLINE" : "GATEWAY OFFLINE"}
</motion.span>
)}
</AnimatePresence>
@@ -146,15 +177,40 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
{/* Top status bar */}
<header className="h-14 border-b border-border/50 bg-sidebar flex items-center justify-between px-6">
<div className="flex items-center gap-6">
<StatusMetric icon={Activity} label="UPTIME" value="14d 7h 23m" color="text-neon-green" />
<StatusMetric icon={Server} label="NODES" value="4 / 4" color="text-primary" />
<StatusMetric icon={Bot} label="AGENTS" value="7 active" color="text-primary" />
<StatusMetric icon={Cpu} label="CPU" value="34%" color="text-neon-green" />
<StatusMetric icon={HardDrive} label="MEM" value="6.2 GB" color="text-neon-amber" />
<StatusMetric
icon={Activity}
label="UPTIME"
value={stats?.uptime ?? "—"}
color="text-neon-green"
/>
<StatusMetric
icon={Server}
label="NODES"
value={stats ? stats.nodes : "—"}
color="text-primary"
/>
<StatusMetric
icon={Bot}
label="AGENTS"
value={stats ? `${stats.agents} active` : "—"}
color="text-primary"
/>
<StatusMetric
icon={Cpu}
label="CPU"
value={stats ? `${stats.cpuPct}%` : "—"}
color={stats ? cpuColor(stats.cpuPct) : "text-muted-foreground"}
/>
<StatusMetric
icon={HardDrive}
label="MEM"
value={stats ? formatMem(stats.memUseMB) : "—"}
color={stats ? memColor(stats.memUseMB) : "text-muted-foreground"}
/>
</div>
<div className="flex items-center gap-3">
<div className="flex items-center gap-2 px-3 py-1.5 rounded-md bg-secondary/50 border border-border/50">
<Wifi className="w-3.5 h-3.5 text-neon-green" />
<Wifi className={`w-3.5 h-3.5 ${stats?.gatewayOnline ? "text-neon-green" : "text-red-400"}`} />
<span className="font-mono text-[11px] text-muted-foreground">
goclaw-swarm:18789
</span>