feat: implement StatusDot component for status indicators in approvals and agents

This commit is contained in:
Abhimanyu Saharan
2026-02-11 17:27:20 +05:30
parent 162a3bbf40
commit 15c26277c5
3 changed files with 86 additions and 14 deletions

View File

@@ -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() {
>
<div className="relative flex h-9 w-9 items-center justify-center rounded-full bg-slate-100 text-xs font-semibold text-slate-700">
{agentAvatarLabel(agent)}
<span
<StatusDot
status={agent.status}
variant="agent"
className={cn(
"absolute -right-0.5 -bottom-0.5 h-2.5 w-2.5 rounded-full border-2 border-white",
isWorking
? "bg-emerald-500"
: agent.status === "online"
? "bg-green-500"
: "bg-slate-300",
isWorking && "ring-2 ring-emerald-200",
)}
/>
</div>

View File

@@ -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({
</div>
<div className="flex items-center gap-3 rounded-lg border border-slate-200 bg-slate-50 px-4 py-3">
<span
<StatusDot
status={selectedApproval.status}
variant="approval"
className={cn(
"h-2 w-2 rounded-full",
statusDotClass(selectedApproval.status),
)}
/>
<div>

View File

@@ -0,0 +1,77 @@
import { cn } from "@/lib/utils";
type StatusDotVariant = "agent" | "approval" | "task";
const AGENT_STATUS_DOT_CLASS_BY_STATUS: Record<string, string> = {
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<string, string> = {
approved: "bg-emerald-500",
rejected: "bg-rose-500",
pending: "bg-amber-500",
};
const TASK_STATUS_DOT_CLASS_BY_STATUS: Record<string, string> = {
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<string, string>
> = {
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<StatusDotVariant, string> = {
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 (
<span
aria-hidden="true"
className={cn(
"inline-block h-2.5 w-2.5 rounded-full",
statusDotClass(status, variant),
className,
)}
/>
);
}