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
Adam Moussa 9b4bef1f4c feat(agent-team): redesign dashboard UI (Board + DAG tabs, Tailwind/shadcn)
Component fan-out + integration of the vibe-kanban-inspired redesign:
- Board.tsx + TaskCard.tsx: read-only Kanban (one column per backend stage,
  fallback grouping by phase), toolbar (search/status/tree filters + node-filter
  chip), gate columns flagged; filter logic lifted to taskFilters.ts (tested).
- MapNode.tsx + PipelineMap.tsx: DAG nodes restyled to Tailwind (Langflow-style
  state-driven borders, lucide icons, count pill, loop/gate affordances).
- TaskDrawer.tsx: shadcn rebuild + Magentic-style read-only gate panel
  ('Answer in Slack #agent-team', no submit controls).
- TopBar.tsx: restyled brand + status chips + health dot.
- Retire theme.css; port the .md markdown block into index.css on new tokens.
- Remove dead TaskList; add Board/taskFilters tests.

Typecheck clean, 36 tests pass, build clean. Read-only — no backend changes.
2026-06-24 13:43:10 -04:00

125 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
);
}