feat(web): workflow runs list page and navigation links
Server Deploy / deploy (push) Successful in 1m29s

This commit is contained in:
2026-07-20 14:52:13 +01:00
parent 99bf093f00
commit dcc901b0d2
2 changed files with 86 additions and 3 deletions
+78
View File
@@ -0,0 +1,78 @@
"use client";
import Link from "next/link";
import { useParams } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { api, WorkflowRun } from "@/lib/api";
import { Card, Table, Thead, Tbody, Tr, Th, Td, Badge } from "@/components/ui";
type BadgeVariant = "success" | "warning" | "danger" | "neutral" | "accent";
const statusVariant: Record<string, BadgeVariant> = {
success: "success",
failed: "danger",
running: "warning",
cancelled: "neutral",
queued: "neutral",
};
export default function WorkflowRunsPage() {
const { id } = useParams<{ id: string }>();
const { data: wf } = useQuery({ queryKey: ["workflow", id], queryFn: () => api.getWorkflow(id) });
const { data: runs, isLoading, error } = useQuery({ queryKey: ["runs", id], queryFn: () => api.listRuns(id) });
return (
<div className="p-8">
<div className="mb-6">
<Link href={`/workflows/${id}`} className="text-sm text-text-secondary hover:text-text-primary">
Back to builder
</Link>
<h1 className="mt-2 text-2xl font-bold text-text-primary">Runs · {wf?.name ?? ""}</h1>
</div>
<Card padding={false}>
{isLoading ? (
<div className="flex items-center justify-center py-20">
<div className="h-8 w-8 animate-spin rounded-full border-2 border-border border-t-accent" />
</div>
) : error ? (
<div className="py-20 text-center text-danger">Failed to load runs. Is the backend running?</div>
) : runs && runs.length > 0 ? (
<Table>
<Thead>
<Tr>
<Th>Run</Th>
<Th>Status</Th>
<Th>Started</Th>
<Th>By</Th>
<Th>Servers</Th>
</Tr>
</Thead>
<Tbody>
{runs.map((r: WorkflowRun) => (
<Tr key={r.run_id}>
<Td>
<Link
href={`/workflows/${id}/runs/${r.run_id}`}
className="font-mono text-text-primary hover:text-signal"
>
{r.run_id.slice(0, 8)}
</Link>
</Td>
<Td>
<Badge variant={statusVariant[r.status] ?? "neutral"}>{r.status}</Badge>
</Td>
<Td className="text-text-secondary">{new Date(r.started_at).toLocaleString()}</Td>
<Td className="text-text-secondary">{r.triggered_by}</Td>
<Td className="text-text-secondary">{r.server_runs.length}</Td>
</Tr>
))}
</Tbody>
</Table>
) : (
<div className="py-16 text-center text-text-secondary">No runs yet.</div>
)}
</Card>
</div>
);
}
+8 -3
View File
@@ -82,9 +82,14 @@ export default function WorkflowsPage() {
<span className="text-text-secondary">{w.steps.length}</span>
</Td>
<Td>
<Link href={`/workflows/${w.workflow_id}`}>
<Button variant="ghost" size="sm">Open </Button>
</Link>
<div className="flex items-center justify-end gap-2">
<Link href={`/workflows/${w.workflow_id}/runs`}>
<Button variant="ghost" size="sm">Runs</Button>
</Link>
<Link href={`/workflows/${w.workflow_id}`}>
<Button variant="ghost" size="sm">Open </Button>
</Link>
</div>
</Td>
</Tr>
))}