refactor(ui): standardize status/priority colors and improve text legibility
Create shared status-colors.ts module as single source of truth for all status and priority color definitions. Replace hardcoded color classes in StatusIcon, StatusBadge, PriorityIcon, NewIssueDialog, Agents, AgentDetail, and DesignGuide. Fix inconsistent hues (in_progress was yellow in StatusIcon but indigo in StatusBadge, blocked was red vs amber). Bump identifier text from text-xs to text-sm and improve MetricCard label legibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import { adapterLabels, roleLabels } from "../components/agent-config-primitives
|
||||
import { getUIAdapter, buildTranscript } from "../adapters";
|
||||
import type { TranscriptEntry } from "../adapters";
|
||||
import { StatusBadge } from "../components/StatusBadge";
|
||||
import { agentStatusDot, agentStatusDotDefault } from "../lib/status-colors";
|
||||
import { MarkdownBody } from "../components/MarkdownBody";
|
||||
import { CopyText } from "../components/CopyText";
|
||||
import { EntityRow } from "../components/EntityRow";
|
||||
@@ -1103,15 +1104,7 @@ function ConfigSummary({
|
||||
className="flex items-center gap-2 text-sm text-blue-400 hover:underline"
|
||||
>
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className={`absolute inline-flex h-full w-full rounded-full ${
|
||||
r.status === "active"
|
||||
? "bg-green-400"
|
||||
: r.status === "pending_approval"
|
||||
? "bg-amber-400"
|
||||
: r.status === "error"
|
||||
? "bg-red-400"
|
||||
: "bg-neutral-400"
|
||||
}`} />
|
||||
<span className={`absolute inline-flex h-full w-full rounded-full ${agentStatusDot[r.status] ?? agentStatusDotDefault}`} />
|
||||
</span>
|
||||
{r.name}
|
||||
<span className="text-muted-foreground text-xs">({roleLabels[r.role] ?? r.role})</span>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useBreadcrumbs } from "../context/BreadcrumbContext";
|
||||
import { useSidebar } from "../context/SidebarContext";
|
||||
import { queryKeys } from "../lib/queryKeys";
|
||||
import { StatusBadge } from "../components/StatusBadge";
|
||||
import { agentStatusDot, agentStatusDotDefault } from "../lib/status-colors";
|
||||
import { EntityRow } from "../components/EntityRow";
|
||||
import { EmptyState } from "../components/EmptyState";
|
||||
import { relativeTime, cn } from "../lib/utils";
|
||||
@@ -227,19 +228,7 @@ export function Agents() {
|
||||
leading={
|
||||
<span className="relative flex h-2.5 w-2.5">
|
||||
<span
|
||||
className={`absolute inline-flex h-full w-full rounded-full ${
|
||||
agent.status === "running"
|
||||
? "bg-cyan-400 animate-pulse"
|
||||
: agent.status === "active"
|
||||
? "bg-green-400"
|
||||
: agent.status === "paused"
|
||||
? "bg-yellow-400"
|
||||
: agent.status === "pending_approval"
|
||||
? "bg-amber-400"
|
||||
: agent.status === "error"
|
||||
? "bg-red-400"
|
||||
: "bg-neutral-400"
|
||||
}`}
|
||||
className={`absolute inline-flex h-full w-full rounded-full ${agentStatusDot[agent.status] ?? agentStatusDotDefault}`}
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
@@ -325,18 +314,7 @@ function OrgTreeNode({
|
||||
}) {
|
||||
const agent = agentMap.get(node.id);
|
||||
|
||||
const statusColor =
|
||||
node.status === "running"
|
||||
? "bg-cyan-400 animate-pulse"
|
||||
: node.status === "active"
|
||||
? "bg-green-400"
|
||||
: node.status === "paused"
|
||||
? "bg-yellow-400"
|
||||
: node.status === "pending_approval"
|
||||
? "bg-amber-400"
|
||||
: node.status === "error"
|
||||
? "bg-red-400"
|
||||
: "bg-neutral-400";
|
||||
const statusColor = agentStatusDot[node.status] ?? agentStatusDotDefault;
|
||||
|
||||
return (
|
||||
<div style={{ paddingLeft: depth * 24 }}>
|
||||
|
||||
@@ -69,6 +69,11 @@ import {
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuShortcut,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
} from "@/components/ui/popover";
|
||||
import {
|
||||
Sheet,
|
||||
SheetTrigger,
|
||||
@@ -110,6 +115,7 @@ import {
|
||||
import { StatusBadge } from "@/components/StatusBadge";
|
||||
import { StatusIcon } from "@/components/StatusIcon";
|
||||
import { PriorityIcon } from "@/components/PriorityIcon";
|
||||
import { agentStatusDot, agentStatusDotDefault } from "@/lib/status-colors";
|
||||
import { EntityRow } from "@/components/EntityRow";
|
||||
import { EmptyState } from "@/components/EmptyState";
|
||||
import { MetricCard } from "@/components/MetricCard";
|
||||
@@ -287,8 +293,8 @@ export function DesignGuide() {
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Tiny label — text-xs text-muted-foreground
|
||||
</p>
|
||||
<p className="text-xs font-mono text-muted-foreground">
|
||||
Mono identifier — text-xs font-mono text-muted-foreground
|
||||
<p className="text-sm font-mono text-muted-foreground">
|
||||
Mono identifier — text-sm font-mono text-muted-foreground
|
||||
</p>
|
||||
<p className="text-2xl font-bold">Large stat — text-2xl font-bold</p>
|
||||
<p className="font-mono text-xs">Log/code text — font-mono text-xs</p>
|
||||
@@ -435,16 +441,10 @@ export function DesignGuide() {
|
||||
|
||||
<SubSection title="Agent status dots">
|
||||
<div className="flex items-center gap-4 flex-wrap">
|
||||
{[
|
||||
["running", "bg-cyan-400 animate-pulse"],
|
||||
["active", "bg-green-400"],
|
||||
["paused", "bg-yellow-400"],
|
||||
["error", "bg-red-400"],
|
||||
["offline", "bg-neutral-400"],
|
||||
].map(([label, color]) => (
|
||||
{(["running", "active", "paused", "error", "archived"] as const).map((label) => (
|
||||
<div key={label} className="flex items-center gap-2">
|
||||
<span className="relative flex h-2.5 w-2.5">
|
||||
<span className={`inline-flex h-full w-full rounded-full ${color}`} />
|
||||
<span className={`inline-flex h-full w-full rounded-full ${agentStatusDot[label] ?? agentStatusDotDefault}`} />
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">{label}</span>
|
||||
</div>
|
||||
|
||||
@@ -412,7 +412,7 @@ export function IssueDetail() {
|
||||
priority={issue.priority}
|
||||
onChange={(priority) => updateIssue.mutate({ priority })}
|
||||
/>
|
||||
<span className="text-xs font-mono text-muted-foreground">{issue.identifier ?? issue.id.slice(0, 8)}</span>
|
||||
<span className="text-sm font-mono text-muted-foreground">{issue.identifier ?? issue.id.slice(0, 8)}</span>
|
||||
|
||||
{issue.projectId ? (
|
||||
<Link
|
||||
@@ -606,7 +606,7 @@ export function IssueDetail() {
|
||||
<Link
|
||||
key={child.id}
|
||||
to={`/issues/${child.identifier ?? child.id}`}
|
||||
className="flex items-center justify-between px-3 py-2 text-xs hover:bg-accent/20 transition-colors"
|
||||
className="flex items-center justify-between px-3 py-2 text-sm hover:bg-accent/20 transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<StatusIcon status={child.status} />
|
||||
|
||||
Reference in New Issue
Block a user