mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
refactor(web): rename board column pending → initializing
Clarifies that Submitted/Starting runs are initializing, not just pending. Also refactors run-detail to display the actual run status via runStatusDisplay instead of mapping to board columns. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e79baeab8e
commit
a90038f7a7
6 changed files with 50 additions and 26 deletions
|
|
@ -29,11 +29,11 @@ export interface RunItem {
|
|||
sandboxId?: string;
|
||||
}
|
||||
|
||||
export type ColumnStatus = "working" | "pending" | "review" | "merge" | "running" | "waiting" | "succeeded" | "failed";
|
||||
export type ColumnStatus = "working" | "initializing" | "review" | "merge" | "running" | "waiting" | "succeeded" | "failed";
|
||||
|
||||
export const columnNames: Record<ColumnStatus, string> = {
|
||||
working: "Working",
|
||||
pending: "Pending",
|
||||
initializing: "Initializing",
|
||||
review: "Verify",
|
||||
merge: "Merge",
|
||||
running: "Running",
|
||||
|
|
@ -108,7 +108,7 @@ export function deriveCiStatus(checks: CheckRun[]): CiStatus {
|
|||
|
||||
export const statusColors: Record<ColumnStatus, { dot: string; text: string }> = {
|
||||
working: { dot: "bg-teal-500", text: "text-teal-500" },
|
||||
pending: { dot: "bg-amber", text: "text-amber" },
|
||||
initializing: { dot: "bg-amber", text: "text-amber" },
|
||||
review: { dot: "bg-mint", text: "text-mint" },
|
||||
merge: { dot: "bg-teal-300", text: "text-teal-300" },
|
||||
running: { dot: "bg-teal-500", text: "text-teal-500" },
|
||||
|
|
@ -117,6 +117,33 @@ export const statusColors: Record<ColumnStatus, { dot: string; text: string }> =
|
|||
failed: { dot: "bg-coral", text: "text-coral" },
|
||||
};
|
||||
|
||||
export type RunStatus =
|
||||
| "submitted"
|
||||
| "starting"
|
||||
| "running"
|
||||
| "paused"
|
||||
| "removing"
|
||||
| "succeeded"
|
||||
| "failed"
|
||||
| "dead";
|
||||
|
||||
export const runStatusDisplay: Record<RunStatus, { label: string; dot: string; text: string }> = {
|
||||
submitted: { label: "Submitted", dot: "bg-fg-muted", text: "text-fg-muted" },
|
||||
starting: { label: "Starting", dot: "bg-amber", text: "text-amber" },
|
||||
running: { label: "Running", dot: "bg-teal-500", text: "text-teal-500" },
|
||||
paused: { label: "Paused", dot: "bg-amber", text: "text-amber" },
|
||||
removing: { label: "Removing", dot: "bg-fg-muted", text: "text-fg-muted" },
|
||||
succeeded: { label: "Succeeded", dot: "bg-mint", text: "text-mint" },
|
||||
failed: { label: "Failed", dot: "bg-coral", text: "text-coral" },
|
||||
dead: { label: "Dead", dot: "bg-coral", text: "text-coral" },
|
||||
};
|
||||
|
||||
const knownRunStatuses = new Set<string>(Object.keys(runStatusDisplay));
|
||||
|
||||
export function isRunStatus(s: string): s is RunStatus {
|
||||
return knownRunStatuses.has(s);
|
||||
}
|
||||
|
||||
export const ciConfig: Record<CiStatus, { label: string; dot: string; text: string }> = {
|
||||
passing: { label: "Passing", dot: "bg-mint", text: "text-mint" },
|
||||
failing: { label: "Changes needed", dot: "bg-coral", text: "text-coral" },
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { useEffect } from "react";
|
|||
import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/20/solid";
|
||||
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
|
||||
import { Link, Outlet, useFetcher, useLocation } from "react-router";
|
||||
import { columnNames, mapRunSummaryToRunItem, statusColors } from "../data/runs";
|
||||
import type { ColumnStatus, RunSummaryResponse } from "../data/runs";
|
||||
import { mapRunSummaryToRunItem, runStatusDisplay, isRunStatus } from "../data/runs";
|
||||
import type { RunSummaryResponse } from "../data/runs";
|
||||
import { apiJson } from "../api";
|
||||
import { useDemoMode } from "../lib/demo-mode";
|
||||
import type { PreviewUrlResponse } from "@qltysh/fabro-api-client";
|
||||
|
|
@ -25,17 +25,16 @@ export async function loader({ request, params }: any) {
|
|||
if (!response.ok) return { run: null };
|
||||
const summary: RunSummaryResponse = await response.json();
|
||||
const item = mapRunSummaryToRunItem(summary);
|
||||
const statusMap: Record<string, ColumnStatus> = {
|
||||
running: "working",
|
||||
paused: "pending",
|
||||
completed: "merge",
|
||||
};
|
||||
const status = statusMap[summary.status ?? ""] ?? "working";
|
||||
const rawStatus = summary.status ?? "submitted";
|
||||
const display = isRunStatus(rawStatus)
|
||||
? runStatusDisplay[rawStatus]
|
||||
: { label: rawStatus, dot: "bg-fg-muted", text: "text-fg-muted" };
|
||||
return {
|
||||
run: {
|
||||
...item,
|
||||
status,
|
||||
statusLabel: columnNames[status] ?? summary.status ?? "Unknown",
|
||||
statusLabel: display.label,
|
||||
statusDot: display.dot,
|
||||
statusText: display.text,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -78,8 +77,6 @@ export default function RunDetail({ loaderData, params }: any) {
|
|||
return <p className="py-8 text-center text-sm text-fg-muted">Run not found.</p>;
|
||||
}
|
||||
|
||||
const colors = statusColors[run.status];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<nav className="mb-4 flex items-center gap-1 text-sm text-fg-muted">
|
||||
|
|
@ -97,8 +94,8 @@ export default function RunDetail({ loaderData, params }: any) {
|
|||
<h2 className="text-xl font-semibold text-fg">{run.title}</h2>
|
||||
<div className="mt-2 flex items-center gap-3 text-sm">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<span className={`size-2 rounded-full ${colors.dot}`} />
|
||||
<span className={`font-medium ${colors.text}`}>{run.statusLabel}</span>
|
||||
<span className={`size-2 rounded-full ${run.statusDot}`} />
|
||||
<span className={`font-medium ${run.statusText}`}>{run.statusLabel}</span>
|
||||
</span>
|
||||
<span className="font-mono text-xs text-fg-muted">{run.repo}</span>
|
||||
{run.elapsed && (
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ interface ColumnStyle {
|
|||
|
||||
const columnStyles: Record<string, ColumnStyle> = {
|
||||
working: { accent: "bg-teal-500", iconColor: "text-teal-500", iconType: "branch", actions: ["Watch", "Steer"] },
|
||||
pending: { accent: "bg-amber", iconColor: "text-amber", iconType: "branch", actions: [] },
|
||||
initializing: { accent: "bg-amber", iconColor: "text-amber", iconType: "branch", actions: [] },
|
||||
review: { accent: "bg-mint", iconColor: "text-mint", iconType: "pr", actions: [] },
|
||||
merge: { accent: "bg-teal-300", iconColor: "text-teal-300", iconType: "pr", actions: ["Merge"] },
|
||||
running: { accent: "bg-teal-500", iconColor: "text-teal-500", iconType: "branch", actions: ["Watch", "Steer"] },
|
||||
|
|
|
|||
|
|
@ -3109,7 +3109,7 @@ components:
|
|||
type: string
|
||||
enum:
|
||||
- working
|
||||
- pending
|
||||
- initializing
|
||||
- review
|
||||
- merge
|
||||
|
||||
|
|
|
|||
|
|
@ -2428,7 +2428,7 @@ fn test_secret_store_path() -> PathBuf {
|
|||
|
||||
fn board_column(status: WorkflowRunStatus) -> Option<&'static str> {
|
||||
match status {
|
||||
WorkflowRunStatus::Submitted | WorkflowRunStatus::Starting => Some("pending"),
|
||||
WorkflowRunStatus::Submitted | WorkflowRunStatus::Starting => Some("initializing"),
|
||||
WorkflowRunStatus::Running => Some("running"),
|
||||
WorkflowRunStatus::Paused => Some("waiting"),
|
||||
WorkflowRunStatus::Succeeded => Some("succeeded"),
|
||||
|
|
@ -2439,7 +2439,7 @@ fn board_column(status: WorkflowRunStatus) -> Option<&'static str> {
|
|||
|
||||
fn board_columns() -> serde_json::Value {
|
||||
serde_json::json!([
|
||||
{"id": "pending", "name": "Pending"},
|
||||
{"id": "initializing", "name": "Initializing"},
|
||||
{"id": "running", "name": "Running"},
|
||||
{"id": "waiting", "name": "Waiting"},
|
||||
{"id": "succeeded", "name": "Succeeded"},
|
||||
|
|
@ -8520,8 +8520,8 @@ level = "debug"
|
|||
let body = body_json(response.into_body()).await;
|
||||
assert_eq!(body["pending_control"].as_str(), Some("pause"));
|
||||
|
||||
// Verify the run appears on the board (store has Submitted status → "pending"
|
||||
// column)
|
||||
// Verify the run appears on the board (store has Submitted status →
|
||||
// "initializing" column)
|
||||
let req = Request::builder()
|
||||
.method("GET")
|
||||
.uri(api("/boards/runs"))
|
||||
|
|
@ -8535,7 +8535,7 @@ level = "debug"
|
|||
.iter()
|
||||
.find(|item| item["id"].as_str() == Some(run_id_str.as_str()))
|
||||
.expect("board item should exist");
|
||||
assert_eq!(item["status"].as_str(), Some("pending"));
|
||||
assert_eq!(item["status"].as_str(), Some("initializing"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -9005,7 +9005,7 @@ timeout = "30s"
|
|||
// Status should be a board column, not a lifecycle status
|
||||
let status = item["status"].as_str().unwrap();
|
||||
assert!(
|
||||
["working", "pending", "review", "merge"].contains(&status),
|
||||
["working", "initializing", "review", "merge"].contains(&status),
|
||||
"status should be a board column, got: {status}"
|
||||
);
|
||||
assert!(item["created_at"].is_string());
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
export const BoardColumn = {
|
||||
WORKING: 'working',
|
||||
PENDING: 'pending',
|
||||
INITIALIZING: 'initializing',
|
||||
REVIEW: 'review',
|
||||
MERGE: 'merge'
|
||||
} as const;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue