feat: implement StatusDot component for status indicators in approvals and agents
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
77
frontend/src/components/atoms/StatusDot.tsx
Normal file
77
frontend/src/components/atoms/StatusDot.tsx
Normal 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,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user