FleetHealth PR 2 (frontend): human-oriented landing + advanced menu
Build with clawstor cache / Cargo build (clawstor-cached) (pull_request) Successful in 18s

Reworks the SPA around the new DashboardStatus fields. The
landing is now a fleet-health dashboard aimed at a layperson —
disk gauges, mount ✓/✗, cache hit rate, next scheduled job.
No hex, no primitives.

Nav restructure:
* Primary: 'Fleet health' (just the landing).
* 'View Advanced ▾' dropdown reveals: Blobs / Tags / Refs /
  Snapshots / Ref-tracking. Routes moved under /advanced/*.

New components:
* StorageBar — horizontal used/total bar with pinned/evictable
  split, health-color threshold at 60/85%.
* NodeCard — traffic-light dot + disk + hot tier bars + mount
  state + cache hit rate + next-timer countdown. Whole card
  is a link into node detail.

New CommandCenter:
* Fleet-wide storage roll-up card (sum of every node's disk).
* Grid of NodeCards.
* 10s poll cadence retained from previous version.

Existing StorageBrowser + RefTrackingPage moved behind
/advanced/* routes; internal component code untouched.
This commit is contained in:
Omar Sobh
2026-07-14 17:23:25 -07:00
parent 9d6badf2aa
commit 22af481c3e
10 changed files with 479 additions and 157 deletions
+20
View File
@@ -0,0 +1,20 @@
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { fmtBytes } from '../lib/api';
/// Big horizontal storage bar. Read-only. Renders green (pinned,
/// safe) + amber (used, evictable) + slate (free). Health color
/// on the label based on fill %.
export function StorageBar({ label, used, total, pinned }) {
const safeTotal = Math.max(total, 1);
const pct = Math.min(100, Math.round((used / safeTotal) * 100));
const pinnedPct = pinned
? Math.min(100, Math.round((pinned / safeTotal) * 100))
: 0;
const evictablePct = Math.max(0, pct - pinnedPct);
const bar = pct < 60 ? 'ok' : pct < 85 ? 'warn' : 'err';
const barText = {
ok: 'text-emerald-300',
warn: 'text-amber-300',
err: 'text-red-300',
}[bar];
return (_jsxs("div", { children: [_jsxs("div", { className: "flex items-baseline justify-between text-xs mb-1", children: [_jsx("span", { className: "text-slate-500 uppercase tracking-wider", children: label }), _jsxs("span", { className: `font-mono ${barText}`, children: [fmtBytes(used), " / ", fmtBytes(total), " \u00B7 ", pct, "%"] })] }), _jsxs("div", { className: "h-2.5 w-full rounded-full bg-slate-800 overflow-hidden flex", children: [pinnedPct > 0 && (_jsx("div", { className: "bg-emerald-500 h-full", style: { width: `${pinnedPct}%` }, title: `Pinned: ${fmtBytes(pinned)}` })), evictablePct > 0 && (_jsx("div", { className: `h-full ${bar === 'err' ? 'bg-red-500' : bar === 'warn' ? 'bg-amber-500' : 'bg-emerald-600'}`, style: { width: `${evictablePct}%` }, title: `Used: ${fmtBytes(used - (pinned ?? 0))}` }))] })] }));
}