diff --git a/frontend/src/app/boards/[boardId]/page.tsx b/frontend/src/app/boards/[boardId]/page.tsx index d79d68f1..f775827a 100644 --- a/frontend/src/app/boards/[boardId]/page.tsx +++ b/frontend/src/app/boards/[boardId]/page.tsx @@ -20,6 +20,7 @@ import { } from "lucide-react"; import { Markdown } from "@/components/atoms/Markdown"; +import { StatusDot } from "@/components/atoms/StatusDot"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { TaskBoard } from "@/components/organisms/TaskBoard"; import { DashboardShell } from "@/components/templates/DashboardShell"; @@ -2425,14 +2426,12 @@ export default function BoardDetailPage() { >
{agentAvatarLabel(agent)} -
diff --git a/frontend/src/components/BoardApprovalsPanel.tsx b/frontend/src/components/BoardApprovalsPanel.tsx index 0b02b785..db0d47b0 100644 --- a/frontend/src/components/BoardApprovalsPanel.tsx +++ b/frontend/src/components/BoardApprovalsPanel.tsx @@ -16,6 +16,7 @@ import { useUpdateApprovalApiV1BoardsBoardIdApprovalsApprovalIdPatch, } from "@/api/generated/approvals/approvals"; import type { ApprovalRead } from "@/api/generated/model"; +import { StatusDot } from "@/components/atoms/StatusDot"; import { ChartContainer, ChartTooltip, @@ -85,12 +86,6 @@ const humanizeAction = (value: string) => const formatStatusLabel = (status: string) => status.replace(/_/g, " ").replace(/\b\w/g, (char) => char.toUpperCase()); -const statusDotClass = (status: string) => { - if (status === "approved") return "bg-emerald-500"; - if (status === "rejected") return "bg-rose-500"; - return "bg-amber-500"; -}; - const rubricColors = [ "#0f172a", "#1d4ed8", @@ -584,10 +579,11 @@ export function BoardApprovalsPanel({
-
diff --git a/frontend/src/components/atoms/StatusDot.tsx b/frontend/src/components/atoms/StatusDot.tsx new file mode 100644 index 00000000..552d9490 --- /dev/null +++ b/frontend/src/components/atoms/StatusDot.tsx @@ -0,0 +1,77 @@ +import { cn } from "@/lib/utils"; + +type StatusDotVariant = "agent" | "approval" | "task"; + +const AGENT_STATUS_DOT_CLASS_BY_STATUS: Record = { + online: "bg-emerald-500", + busy: "bg-amber-500", + provisioning: "bg-amber-500", + updating: "bg-sky-500", + deleting: "bg-rose-500", + offline: "bg-slate-400", +}; + +const APPROVAL_STATUS_DOT_CLASS_BY_STATUS: Record = { + approved: "bg-emerald-500", + rejected: "bg-rose-500", + pending: "bg-amber-500", +}; + +const TASK_STATUS_DOT_CLASS_BY_STATUS: Record = { + inbox: "bg-slate-400", + in_progress: "bg-purple-500", + review: "bg-indigo-500", + done: "bg-emerald-500", +}; + +const STATUS_DOT_CLASS_BY_VARIANT: Record< + StatusDotVariant, + Record +> = { + agent: AGENT_STATUS_DOT_CLASS_BY_STATUS, + approval: APPROVAL_STATUS_DOT_CLASS_BY_STATUS, + task: TASK_STATUS_DOT_CLASS_BY_STATUS, +}; + +const DEFAULT_STATUS_DOT_CLASS: Record = { + agent: "bg-slate-300", + approval: "bg-amber-500", + task: "bg-slate-300", +}; + +export const statusDotClass = ( + status: string | null | undefined, + variant: StatusDotVariant = "agent", +) => { + const normalized = (status ?? "").trim().toLowerCase(); + if (!normalized) { + return DEFAULT_STATUS_DOT_CLASS[variant]; + } + return ( + STATUS_DOT_CLASS_BY_VARIANT[variant][normalized] ?? + DEFAULT_STATUS_DOT_CLASS[variant] + ); +}; + +type StatusDotProps = { + status?: string | null; + variant?: StatusDotVariant; + className?: string; +}; + +export function StatusDot({ + status, + variant = "agent", + className, +}: StatusDotProps) { + return ( +