From bc1b5d54abf8022e10636a065dcf63de1f6fa77e Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Sat, 28 Feb 2026 14:53:14 -0500 Subject: [PATCH] Add run detail page, link run cards, and custom workflow icon colors - Add id field to RunItem and assign IDs to all mock runs - Create run detail page at /runs/:id with status, changes, PR, CI info - Link run cards in both /runs kanban board and /workflows/:name/runs list - Add per-workflow icon color with tinted border on workflow cards Co-Authored-By: Claude Opus 4.6 --- apps/arc-web/app/data/runs.ts | 15 ++++++ apps/arc-web/app/routes.ts | 1 + apps/arc-web/app/routes/pipelines.tsx | 7 +-- apps/arc-web/app/routes/run-detail.tsx | 66 +++++++++++++++++++++++ apps/arc-web/app/routes/workflow-runs.tsx | 7 +-- apps/arc-web/app/routes/workflows.tsx | 14 +++-- 6 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 apps/arc-web/app/routes/run-detail.tsx diff --git a/apps/arc-web/app/data/runs.ts b/apps/arc-web/app/data/runs.ts index 875c7cff9..364c81121 100644 --- a/apps/arc-web/app/data/runs.ts +++ b/apps/arc-web/app/data/runs.ts @@ -1,6 +1,7 @@ export type CiStatus = "passing" | "failing" | "pending"; export interface RunItem { + id: string; repo: string; title: string; number?: number; @@ -39,18 +40,21 @@ export const columns: { actions: ["Watch", "Steer"], items: [ { + id: "run-1", repo: "api-server", title: "Add rate limiting to auth endpoints", resources: "4 CPU / 8 GB", elapsed: "7m", }, { + id: "run-2", repo: "web-dashboard", title: "Migrate to React Router v7", resources: "8 CPU / 16 GB", elapsed: "2h 15m", }, { + id: "run-3", repo: "cli-tools", title: "Fix config parsing for nested values", resources: "2 CPU / 4 GB", @@ -67,6 +71,7 @@ export const columns: { actions: ["Answer Question"], items: [ { + id: "run-4", repo: "api-server", title: "Update OpenAPI spec for v3", additions: 567, @@ -74,6 +79,7 @@ export const columns: { elapsed: "1h 12m", }, { + id: "run-5", repo: "shared-types", title: "Add pipeline event types", additions: 145, @@ -91,6 +97,7 @@ export const columns: { actions: ["Resolve"], items: [ { + id: "run-6", repo: "web-dashboard", title: "Add dark mode toggle", number: 889, @@ -101,6 +108,7 @@ export const columns: { comments: 4, }, { + id: "run-7", repo: "infrastructure", title: "Terraform module for Redis cluster", number: 156, @@ -122,6 +130,7 @@ export const columns: { actions: ["Merge"], items: [ { + id: "run-8", repo: "api-server", title: "Implement webhook retry logic", number: 1249, @@ -133,6 +142,7 @@ export const columns: { comments: 7, }, { + id: "run-9", repo: "cli-tools", title: "Add --verbose flag to run command", number: 430, @@ -143,6 +153,7 @@ export const columns: { comments: 2, }, { + id: "run-10", repo: "shared-types", title: "Export utility type helpers", number: 76, @@ -166,6 +177,10 @@ export function allRunsFlat(): RunWithStatus[] { ); } +export function findRun(id: string): RunWithStatus | undefined { + return allRunsFlat().find((r) => r.id === id); +} + export const statusColors: Record = { working: { dot: "bg-teal-500", text: "text-teal-500" }, pending: { dot: "bg-amber", text: "text-amber" }, diff --git a/apps/arc-web/app/routes.ts b/apps/arc-web/app/routes.ts index bdc7d7154..259710cac 100644 --- a/apps/arc-web/app/routes.ts +++ b/apps/arc-web/app/routes.ts @@ -16,6 +16,7 @@ export default [ route("runs", "routes/workflow-runs.tsx"), ]), route("runs", "routes/pipelines.tsx"), + route("runs/:id", "routes/run-detail.tsx"), route("insights", "routes/insights.tsx"), route("settings", "routes/settings.tsx"), ]), diff --git a/apps/arc-web/app/routes/pipelines.tsx b/apps/arc-web/app/routes/pipelines.tsx index fee1d4828..dc298e919 100644 --- a/apps/arc-web/app/routes/pipelines.tsx +++ b/apps/arc-web/app/routes/pipelines.tsx @@ -1,3 +1,4 @@ +import { Link } from "react-router"; import { columns, ciConfig } from "../data/runs"; import type { CiStatus, RunItem } from "../data/runs"; import type { Route } from "./+types/pipelines"; @@ -80,7 +81,7 @@ function PrCard({ actions?: string[]; }) { return ( -
+
@@ -172,7 +173,7 @@ function PrCard({ {pr.ci != null && }
)} -
+ ); } @@ -193,7 +194,7 @@ function BoardColumn({ column }: { column: (typeof columns)[number] }) {
{column.items.map((pr) => ( Run not found.

; + } + + const colors = statusColors[run.status]; + + return ( +
+
+

{run.title}

+
+ + + {run.statusLabel} + + {run.repo} + {run.elapsed && ( + {run.elapsed} + )} +
+
+ +
+ {run.resources && ( +
+ Resources + {run.resources} +
+ )} + {(run.additions != null || run.deletions != null) && ( +
+ Changes + + {run.additions != null && +{run.additions.toLocaleString()}} + {run.additions != null && run.deletions != null && / } + {run.deletions != null && -{run.deletions.toLocaleString()}} + +
+ )} + {run.number != null && ( +
+ Pull Request + #{run.number} +
+ )} + {run.ci && ( +
+ CI + {ciConfig[run.ci].label} +
+ )} +
+
+ ); +} diff --git a/apps/arc-web/app/routes/workflow-runs.tsx b/apps/arc-web/app/routes/workflow-runs.tsx index cf23ab5ba..a3fdb82c8 100644 --- a/apps/arc-web/app/routes/workflow-runs.tsx +++ b/apps/arc-web/app/routes/workflow-runs.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; import { ChevronDownIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline"; +import { Link } from "react-router"; import { allRunsFlat, ciConfig, columns, statusColors } from "../data/runs"; import type { ColumnStatus, RunWithStatus } from "../data/runs"; @@ -16,7 +17,7 @@ function GitPullRequestIcon({ className }: { className?: string }) { function RunRow({ run }: { run: RunWithStatus }) { const colors = statusColors[run.status]; return ( -
+ {run.statusLabel} @@ -52,7 +53,7 @@ function RunRow({ run }: { run: RunWithStatus }) { )} -
+ ); } @@ -96,7 +97,7 @@ export default function WorkflowRuns() {
{filtered.map((run) => ( - + ))} {filtered.length === 0 && (

No runs match "{query}"

diff --git a/apps/arc-web/app/routes/workflows.tsx b/apps/arc-web/app/routes/workflows.tsx index 95cd35528..0cd843084 100644 --- a/apps/arc-web/app/routes/workflows.tsx +++ b/apps/arc-web/app/routes/workflows.tsx @@ -61,13 +61,14 @@ interface Workflow { filename: string; lastRun: string; icon: ComponentType<{ className?: string }>; + color: string; } const workflows: Workflow[] = [ - { name: "Fix Build", slug: "fix_build", filename: "fix_build.dot", lastRun: "2 hours ago", icon: WrenchIcon }, - { name: "Implement Feature", slug: "implement", filename: "implement.dot", lastRun: "4 days ago", icon: CodeBracketIcon }, - { name: "Sync Drift", slug: "sync_drift", filename: "sync_drift.dot", lastRun: "1 day ago", icon: ArrowsRightLeftIcon }, - { name: "Expand Product", slug: "expand", filename: "expand.dot", lastRun: "2 weeks ago", icon: RocketLaunchIcon }, + { name: "Fix Build", slug: "fix_build", filename: "fix_build.dot", lastRun: "2 hours ago", icon: WrenchIcon, color: "#F0A45B" }, + { name: "Implement Feature", slug: "implement", filename: "implement.dot", lastRun: "4 days ago", icon: CodeBracketIcon, color: "#67B2D7" }, + { name: "Sync Drift", slug: "sync_drift", filename: "sync_drift.dot", lastRun: "1 day ago", icon: ArrowsRightLeftIcon, color: "#5AC8A8" }, + { name: "Expand Product", slug: "expand", filename: "expand.dot", lastRun: "2 weeks ago", icon: RocketLaunchIcon, color: "#E86B6B" }, ]; function PlayIcon({ className }: { className?: string }) { @@ -91,7 +92,10 @@ function WorkflowCard({ workflow }: { workflow: Workflow }) { return (
-
+