This repository has been archived on 2026-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
orchestrator/agent-team/web/src/components/TopBar.tsx

126 lines
3.3 KiB
TypeScript
Raw Normal View History

import { cn } from "@/lib/utils";
import type { StateResponse } from "../api";
interface Props {
state: StateResponse | null;
connected: boolean;
generatedAt: string | null;
}
interface ChipDef {
key: keyof StateResponse["summary"];
label: string;
numCls: string;
bgCls: string;
}
const CHIPS: ChipDef[] = [
{
key: "active",
label: "ACTIVE",
numCls: "text-status-active",
bgCls: "bg-status-active-bg",
},
{
key: "waiting",
label: "AWAITING HUMAN",
numCls: "text-status-waiting",
bgCls: "bg-status-waiting-bg",
},
{
key: "parked",
label: "PARKED",
numCls: "text-status-parked",
bgCls: "bg-status-parked-bg",
},
{
key: "open_questions",
label: "OPEN QUESTIONS",
numCls: "text-status-waiting",
bgCls: "bg-status-waiting-bg",
},
{
key: "total",
label: "TOTAL",
numCls: "text-muted-foreground",
bgCls: "bg-muted",
},
];
// Top status bar: brand, summary chips, connection health, and snapshot clock.
export function TopBar({ state, connected, generatedAt }: Props) {
const summary = state?.summary;
const title = connected
? `Live — snapshot at ${generatedAt ?? "—"}`
: `Disconnected — last snapshot at ${generatedAt ?? "—"}`;
return (
<header
className="flex items-center gap-3 py-2 px-3 bg-card border-b border-border"
role="banner"
>
{/* Brand */}
<div className="flex items-center gap-2 shrink-0">
<span
className="w-3 h-3 rounded-full bg-gradient-to-br from-primary to-primary/50 shadow-sm"
aria-hidden
/>
<span className="text-sm font-bold text-foreground tracking-tight leading-none">
agent-team
</span>
<span className="text-xs text-muted-foreground leading-none">
pipeline
</span>
</div>
{/* Divider */}
<div className="w-px h-5 bg-border shrink-0" aria-hidden />
{/* Summary chips */}
<div
className="flex items-center gap-2 flex-1 min-w-0 overflow-x-auto"
role="list"
aria-label="Pipeline summary"
>
{CHIPS.map((c) => (
<div
key={c.key}
role="listitem"
className={cn(
"flex flex-col items-center justify-center rounded-md px-2.5 py-1 shrink-0",
c.bgCls,
)}
>
<span className={cn("text-sm font-bold leading-none tabular-nums", c.numCls)}>
{summary ? summary[c.key] : "–"}
</span>
<span className="text-[9px] font-semibold tracking-widest text-muted-foreground leading-none mt-0.5 whitespace-nowrap">
{c.label}
</span>
</div>
))}
</div>
{/* Health indicator */}
<div
className="flex items-center gap-2 shrink-0 ml-auto"
title={title}
aria-label={title}
>
<span
aria-hidden
className={cn(
"w-2.5 h-2.5 rounded-full",
connected
? "bg-green-500 shadow-[0_0_6px_2px_rgba(34,197,94,0.45)]"
: "bg-red-500",
)}
/>
<span className="text-xs text-muted-foreground tabular-nums">
{generatedAt ?? "—"}
</span>
</div>
</header>
);
}