mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-06 08:18:58 +00:00
Add stage ID to stages URL for addressable stage navigation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
73d205ca13
commit
043a5e2ef0
4 changed files with 23 additions and 18 deletions
|
|
@ -18,7 +18,7 @@ export default [
|
|||
route("runs", "routes/pipelines.tsx"),
|
||||
route("runs/:id", "routes/run-detail.tsx", [
|
||||
index("routes/run-overview.tsx"),
|
||||
route("stages", "routes/run-stages.tsx"),
|
||||
route("stages/:stageId", "routes/run-stages.tsx"),
|
||||
route("files", "routes/run-files-changed.tsx"),
|
||||
route("usage", "routes/run-usage.tsx"),
|
||||
]),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import type { Route } from "./+types/run-detail";
|
|||
|
||||
const tabs = [
|
||||
{ name: "Overview", path: "", count: null },
|
||||
{ name: "Stages", path: "/stages", count: 4 },
|
||||
{ name: "Stages", path: "/stages/detect-drift", count: 4 },
|
||||
{ name: "Files Changed", path: "/files", count: 3 },
|
||||
{ name: "Usage", path: "/usage", count: null },
|
||||
];
|
||||
|
|
@ -71,7 +71,9 @@ export default function RunDetail({ params }: Route.ComponentProps) {
|
|||
<nav className="-mb-px flex gap-6">
|
||||
{tabs.map((tab) => {
|
||||
const tabPath = `${basePath}${tab.path}`;
|
||||
const isActive = pathname === tabPath;
|
||||
const isActive = tab.name === "Stages"
|
||||
? pathname.startsWith(`${basePath}/stages`)
|
||||
: pathname === tabPath;
|
||||
return (
|
||||
<Link
|
||||
key={tab.name}
|
||||
|
|
|
|||
|
|
@ -11,16 +11,17 @@ export const handle = { wide: true };
|
|||
type StageStatus = "completed" | "running" | "pending" | "failed";
|
||||
|
||||
interface Stage {
|
||||
id: string;
|
||||
name: string;
|
||||
status: StageStatus;
|
||||
duration: string;
|
||||
}
|
||||
|
||||
const stages: Stage[] = [
|
||||
{ name: "Detect Drift", status: "completed", duration: "1m 12s" },
|
||||
{ name: "Propose Changes", status: "completed", duration: "2m 34s" },
|
||||
{ name: "Review Changes", status: "completed", duration: "0m 45s" },
|
||||
{ name: "Apply Changes", status: "running", duration: "1m 58s" },
|
||||
{ id: "detect-drift", name: "Detect Drift", status: "completed", duration: "1m 12s" },
|
||||
{ id: "propose-changes", name: "Propose Changes", status: "completed", duration: "2m 34s" },
|
||||
{ id: "review-changes", name: "Review Changes", status: "completed", duration: "0m 45s" },
|
||||
{ id: "apply-changes", name: "Apply Changes", status: "running", duration: "1m 58s" },
|
||||
];
|
||||
|
||||
const statusConfig: Record<StageStatus, { icon: typeof CheckCircleIcon; color: string }> = {
|
||||
|
|
@ -273,9 +274,9 @@ export default function RunOverview() {
|
|||
const config = statusConfig[stage.status];
|
||||
const Icon = config.icon;
|
||||
return (
|
||||
<li key={stage.name}>
|
||||
<li key={stage.id}>
|
||||
<Link
|
||||
to={`/runs/${id}/stages`}
|
||||
to={`/runs/${id}/stages/${stage.id}`}
|
||||
className="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm text-ice-300 transition-colors hover:bg-white/[0.04] hover:text-white"
|
||||
>
|
||||
<Icon className={`size-4 shrink-0 ${config.color} ${stage.status === "running" ? "animate-spin" : ""}`} />
|
||||
|
|
|
|||
|
|
@ -11,16 +11,17 @@ export const handle = { wide: true };
|
|||
type StageStatus = "completed" | "running" | "pending" | "failed";
|
||||
|
||||
interface Stage {
|
||||
id: string;
|
||||
name: string;
|
||||
status: StageStatus;
|
||||
duration: string;
|
||||
}
|
||||
|
||||
const stages: Stage[] = [
|
||||
{ name: "Detect Drift", status: "completed", duration: "1m 12s" },
|
||||
{ name: "Propose Changes", status: "completed", duration: "2m 34s" },
|
||||
{ name: "Review Changes", status: "completed", duration: "0m 45s" },
|
||||
{ name: "Apply Changes", status: "running", duration: "1m 58s" },
|
||||
{ id: "detect-drift", name: "Detect Drift", status: "completed", duration: "1m 12s" },
|
||||
{ id: "propose-changes", name: "Propose Changes", status: "completed", duration: "2m 34s" },
|
||||
{ id: "review-changes", name: "Review Changes", status: "completed", duration: "0m 45s" },
|
||||
{ id: "apply-changes", name: "Apply Changes", status: "running", duration: "1m 58s" },
|
||||
];
|
||||
|
||||
const statusConfig: Record<StageStatus, { icon: typeof CheckCircleIcon; color: string }> = {
|
||||
|
|
@ -41,7 +42,7 @@ type TurnType =
|
|||
| { kind: "assistant"; content: string }
|
||||
| { kind: "tool"; tools: ToolUse[] };
|
||||
|
||||
const selectedStage = stages[0];
|
||||
// selectedStage is resolved from the URL param in RunStages below
|
||||
|
||||
const turns: TurnType[] = [
|
||||
{
|
||||
|
|
@ -148,9 +149,10 @@ function AssistantBlock({ content }: { content: string }) {
|
|||
}
|
||||
|
||||
export default function RunStages() {
|
||||
const { id } = useParams();
|
||||
const { id, stageId } = useParams();
|
||||
const run = findRun(id ?? "");
|
||||
const workflow = run ? workflowData[run.workflow] : undefined;
|
||||
const selectedStage = stages.find((s) => s.id === stageId) ?? stages[0];
|
||||
const selectedConfig = statusConfig[selectedStage.status];
|
||||
const SelectedIcon = selectedConfig.icon;
|
||||
|
||||
|
|
@ -163,11 +165,11 @@ export default function RunStages() {
|
|||
{stages.map((stage) => {
|
||||
const config = statusConfig[stage.status];
|
||||
const Icon = config.icon;
|
||||
const isSelected = stage.name === selectedStage.name;
|
||||
const isSelected = stage.id === selectedStage.id;
|
||||
return (
|
||||
<li key={stage.name}>
|
||||
<li key={stage.id}>
|
||||
<Link
|
||||
to={`/runs/${id}/stages`}
|
||||
to={`/runs/${id}/stages/${stage.id}`}
|
||||
className={`flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors ${
|
||||
isSelected
|
||||
? "bg-white/[0.06] text-white"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue