Dashboard: rebuild into an operator cockpit (0.38.0)
Replace the analytics-style dashboard (stack-health funnel, uptime %, operations/day grid, AI pill) with an attention-driven fleet cockpit: - New /api/dashboard/fleet endpoint: server-side fan-out across the local host and every agent into one payload — a prioritized "needs attention" list, headline KPIs, an honest stack-status breakdown and a per-host resource rollup. Each agent uses its own DB session so the fan-out is concurrency-safe; failures degrade to "offline" instead of stalling. - New frontend: AttentionStrip, FleetKpiRow, StackStatusBar and HostResourceTable; Dashboard.tsx rewritten around them. - Remove the funnel/summary endpoints, the uptime sampler loop and the ops-activity machinery; delete the now-unused chart components. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
5c46e40866
commit
c830d28b65
@@ -0,0 +1,96 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import { Server, Boxes, Container, HeartPulse, ArrowUpCircle, Archive } from "lucide-react";
|
||||
import type { FleetKpis } from "@/api/dashboard";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type Tone = "default" | "warn" | "error";
|
||||
|
||||
function Kpi({
|
||||
icon,
|
||||
label,
|
||||
value,
|
||||
sub,
|
||||
tone = "default",
|
||||
to,
|
||||
}: {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
value: React.ReactNode;
|
||||
sub?: string;
|
||||
tone?: Tone;
|
||||
to?: string;
|
||||
}) {
|
||||
const toneText =
|
||||
tone === "error"
|
||||
? "text-red-600 dark:text-red-400"
|
||||
: tone === "warn"
|
||||
? "text-sp-amber"
|
||||
: "text-sp-text-1";
|
||||
const inner = (
|
||||
<div className="sp-card flex h-full flex-col gap-1 p-4">
|
||||
<div className="flex items-center gap-1.5 text-sp-text-3">
|
||||
<span className="[&>svg]:h-3.5 [&>svg]:w-3.5">{icon}</span>
|
||||
<span className="sp-label">{label}</span>
|
||||
</div>
|
||||
<p className={cn("sp-display text-3xl leading-none", toneText)}>{value}</p>
|
||||
{sub && <p className="text-xs text-sp-text-3">{sub}</p>}
|
||||
</div>
|
||||
);
|
||||
return to ? (
|
||||
<Link to={to} className="block transition-transform hover:-translate-y-0.5">
|
||||
{inner}
|
||||
</Link>
|
||||
) : (
|
||||
inner
|
||||
);
|
||||
}
|
||||
|
||||
export function FleetKpiRow({ kpis }: { kpis: FleetKpis }) {
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-6">
|
||||
<Kpi
|
||||
icon={<Server />}
|
||||
label="Hosts"
|
||||
value={`${kpis.hosts_online}/${kpis.hosts_total}`}
|
||||
sub="online"
|
||||
tone={kpis.hosts_online < kpis.hosts_total ? "error" : "default"}
|
||||
/>
|
||||
<Kpi
|
||||
icon={<Boxes />}
|
||||
label="Stacks"
|
||||
value={`${kpis.stacks_running}/${kpis.stacks_total}`}
|
||||
sub={kpis.stacks_partial > 0 ? `${kpis.stacks_partial} partial` : "running"}
|
||||
tone={kpis.stacks_partial > 0 ? "warn" : "default"}
|
||||
/>
|
||||
<Kpi
|
||||
icon={<Container />}
|
||||
label="Containers"
|
||||
value={`${kpis.containers_running}/${kpis.containers_total}`}
|
||||
sub="running"
|
||||
/>
|
||||
<Kpi
|
||||
icon={<HeartPulse />}
|
||||
label="Unhealthy"
|
||||
value={kpis.unhealthy}
|
||||
sub={kpis.unhealthy > 0 ? "need a look" : "all healthy"}
|
||||
tone={kpis.unhealthy > 0 ? "error" : "default"}
|
||||
/>
|
||||
<Kpi
|
||||
icon={<ArrowUpCircle />}
|
||||
label="Updates"
|
||||
value={kpis.updates_available}
|
||||
sub={kpis.updates_available > 0 ? "available" : "up to date"}
|
||||
tone={kpis.updates_available > 0 ? "warn" : "default"}
|
||||
to={kpis.updates_available > 0 ? "/images" : undefined}
|
||||
/>
|
||||
<Kpi
|
||||
icon={<Archive />}
|
||||
label="Backups"
|
||||
value={kpis.backups_failing > 0 ? kpis.backups_failing : "OK"}
|
||||
sub={kpis.backups_failing > 0 ? "failing/overdue" : "on schedule"}
|
||||
tone={kpis.backups_failing > 0 ? "error" : "default"}
|
||||
to={kpis.backups_failing > 0 ? "/settings" : undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user