- Replace statusDotColor + statusColor (35 lines) with STATUS_STYLES config (9 lines) - Single source of truth for both dot and text colors - All 6 statuses explicitly defined with type-safe Record constraint - Use semantic tokens for duration colors (text-destructive/text-primary)
Unified · Split
| @@ -60,22 +60,17 @@ const formatJsonDocument = (value: string | null): string | null => { · 59 unmodified lines | |||
| 60 | } | 60 | } |
| 61 | }; | 61 | }; |
| 62 | 62 | ||
| 63 | const statusDotColor = (status: ExecutionStatus): string => { | 63 | const STATUS_STYLES = { |
| 64 | switch (status) { | 64 | completed: { dot: "bg-primary", text: "text-primary" }, |
| 65 | case "completed": | 65 | failed: { dot: "bg-destructive", text: "text-destructive" }, |
| 66 | return "bg-primary"; | 66 | running: { dot: "bg-blue-400 animate-pulse", text: "text-blue-400" }, |
| 67 | case "failed": | 67 | waiting_for_interaction: { dot: "bg-amber-400 animate-pulse", text: "text-amber-400" }, |
| 68 | return "bg-destructive"; | 68 | pending: { dot: "bg-muted", text: "text-muted-foreground" }, |
| 69 | case "running": | 69 | cancelled: { dot: "bg-muted", text: "text-muted-foreground" }, |
| 70 | return "bg-blue-400 animate-pulse"; | 70 | } as const satisfies Record<ExecutionStatus, { dot: string; text: string }>; |
| 71 | case "waiting_for_interaction": | 71 | |
| 72 | return "bg-amber-400 animate-pulse"; | 72 | const statusDot = (status: ExecutionStatus): string => STATUS_STYLES[status].dot; |
| 73 | case "pending": | 73 | const statusText = (status: ExecutionStatus): string => STATUS_STYLES[status].text; |
| 74 | return "bg-muted"; | ||
| 75 | case "cancelled": | ||
| 76 | return "bg-muted"; | ||
| 77 | } | ||
| 78 | }; | ||
| 79 | 74 | ||
| 80 | const STATUS_OPTIONS: Array<{ value: string; label: string }> = [ | 75 | const STATUS_OPTIONS: Array<{ value: string; label: string }> = [ |
| 81 | { value: "all", label: "All" }, | 76 | { value: "all", label: "All" }, |
| @@ -93,21 +88,6 @@ const formatDurationMs = (execution: Execution): string | null => { · 11 unmodified lines | |||
| 93 | return ms.toLocaleString(); | 88 | return ms.toLocaleString(); |
| 94 | }; | 89 | }; |
| 95 | 90 | ||
| 96 | const statusColor = (status: ExecutionStatus): string => { | ||
| 97 | switch (status) { | ||
| 98 | case "completed": | ||
| 99 | return "text-primary"; | ||
| 100 | case "failed": | ||
| 101 | return "text-destructive"; | ||
| 102 | case "running": | ||
| 103 | return "text-blue-400"; | ||
| 104 | case "waiting_for_interaction": | ||
| 105 | return "text-amber-400"; | ||
| 106 | default: | ||
| 107 | return "text-muted-foreground"; | ||
| 108 | } | ||
| 109 | }; | ||
| 110 | |||
| 111 | // ── Execution Row ──────────────────────────────────────────────────────── | 91 | // ── Execution Row ──────────────────────────────────────────────────────── |
| 112 | 92 | ||
| 113 | const ExecutionRow = (props: { | 93 | const ExecutionRow = (props: { |
| @@ -130,7 +110,7 @@ const ExecutionRow = (props: { · 16 unmodified lines | |||
| 130 | <span | 110 | <span |
| 131 | className={cn( | 111 | className={cn( |
| 132 | "mt-1.5 size-2 shrink-0 rounded-full", | 112 | "mt-1.5 size-2 shrink-0 rounded-full", |
| 133 | statusDotColor(execution.status), | 113 | statusDot(execution.status), |
| 134 | )} | 114 | )} |
| 135 | /> | 115 | /> |
| 136 | <span className="w-[120px] shrink-0 tabular-nums text-muted-foreground"> | 116 | <span className="w-[120px] shrink-0 tabular-nums text-muted-foreground"> |
| @@ -138,11 +118,11 @@ const ExecutionRow = (props: { · 1 unmodified line | |||
| 138 | </span> | 118 | </span> |
| 139 | <span className="inline-flex w-[130px] shrink-0"> | 119 | <span className="inline-flex w-[130px] shrink-0"> |
| 140 | <span className="text-muted-foreground/60">status:</span> | 120 | <span className="text-muted-foreground/60">status:</span> |
| 141 | <span className={statusColor(execution.status)}>{execution.status.replaceAll("_", " ")}</span> | 121 | <span className={statusText(execution.status)}>{execution.status.replaceAll("_", " ")}</span> |
| 142 | </span> | 122 | </span> |
| 143 | <span className="inline-flex w-[110px] shrink-0"> | 123 | <span className="inline-flex w-[110px] shrink-0"> |
| 144 | <span className="text-muted-foreground/60">duration_ms:</span>{" "} | 124 | <span className="text-muted-foreground/60">duration_ms:</span>{" "} |
| 145 | <span className={durationMs && Number(durationMs.replace(/,/g, "")) > 5000 ? "text-red-400" : "text-emerald-400"}>{durationMs ?? "—"}</span> | 125 | <span className={durationMs && Number(durationMs.replace(/,/g, "")) > 5000 ? "text-destructive" : "text-primary"}>{durationMs ?? "—"}</span> |
| 146 | </span> | 126 | </span> |
| 147 | <span className="min-w-0 flex-1 truncate"> | 127 | <span className="min-w-0 flex-1 truncate"> |
| 148 | <span className="text-muted-foreground/60">code:</span>{" "} | 128 | <span className="text-muted-foreground/60">code:</span>{" "} |
| @@ -186,7 +166,7 @@ const PropertiesTab = (props: { envelope: ExecutionEnvelope }) => { · 37 unmodified lines | |||
| 186 | Status | 166 | Status |
| 187 | </div> | 167 | </div> |
| 188 | <div className="mt-1 flex items-center gap-2"> | 168 | <div className="mt-1 flex items-center gap-2"> |
| 189 | <span className={cn("size-2 rounded-full", statusDotColor(execution.status))} /> | 169 | <span className={cn("size-2 rounded-full", statusDot(execution.status))} /> |
| 190 | <span className="text-sm">{execution.status.replaceAll("_", " ")}</span> | 170 | <span className="text-sm">{execution.status.replaceAll("_", " ")}</span> |
| 191 | </div> | 171 | </div> |
| 192 | </div> | 172 | </div> |