feat(web): three-phase progress with live download % on The Floor
Replace the misleading 6-micro-step progress bar with three honest
phases (Searching -> Downloading -> Finishing). Downloading now shows
a live determinate percentage sourced from job.downloadProgress (Slice
A), with a "NN% · done/total tracks" caption when the track count is
known. Searching and Finishing stay indeterminate.
- status.ts: describeJob collapses to {label, kind}; drop step/totalSteps
and the STAGES array.
- progress-bar.tsx: ProgressBar takes {kind, indeterminate?, percent?,
caption?} instead of step/total/stageLabel.
- job-row.tsx: JobRow takes a `bar?: ReactNode` slot instead of
step/total/indeterminate/stageLabel props.
- queue.tsx: builds the per-state bar + meta; downloading meta drops
the track count (now shown in the bar). Retry button + error note
from the retry feature preserved exactly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
-19
@@ -1,16 +1,14 @@
|
||||
export type Kind = "idle" | "working" | "verify" | "done" | "attention";
|
||||
|
||||
// Pipeline stage order (worker Job.currentStage), used to derive a stepped progress.
|
||||
const STAGES = ["intake", "match", "rank", "download", "tag", "import"];
|
||||
|
||||
// Worker Job.state -> a literal, legible label + severity kind. The record-label
|
||||
// metaphor stays in the chrome; these labels never obscure what's happening.
|
||||
// Worker Job.state -> a literal, legible label + severity kind, collapsed to three
|
||||
// honest phases: Searching -> Downloading -> Finishing. The record-label metaphor
|
||||
// stays in the chrome; these labels never obscure what's happening.
|
||||
const STATE_MAP: Record<string, { label: string; kind: Kind }> = {
|
||||
requested: { label: "Queued", kind: "idle" },
|
||||
matching: { label: "Searching", kind: "working" },
|
||||
matched: { label: "Matched", kind: "working" },
|
||||
matched: { label: "Searching", kind: "working" },
|
||||
downloading: { label: "Downloading", kind: "working" },
|
||||
tagging: { label: "Tagging", kind: "verify" },
|
||||
tagging: { label: "Finishing", kind: "verify" },
|
||||
imported: { label: "Pressed", kind: "done" },
|
||||
needs_attention: { label: "Needs attention", kind: "attention" },
|
||||
};
|
||||
@@ -29,16 +27,12 @@ export function timeAgo(value: string | Date | null | undefined, now: number): s
|
||||
return `${Math.floor(h / 24)}d`;
|
||||
}
|
||||
|
||||
export function describeJob(
|
||||
state: string,
|
||||
stage: string,
|
||||
): { label: string; kind: Kind; step: number; totalSteps: number } {
|
||||
const base =
|
||||
STATE_MAP[state] ??
|
||||
({ label: state ? state[0].toUpperCase() + state.slice(1) : "Unknown", kind: "idle" } as {
|
||||
label: string;
|
||||
kind: Kind;
|
||||
});
|
||||
const idx = STAGES.indexOf(stage);
|
||||
return { ...base, step: idx >= 0 ? idx + 1 : 0, totalSteps: STAGES.length };
|
||||
export function describeJob(state: string, stage: string): { label: string; kind: Kind } {
|
||||
void stage; // stage no longer drives the label; kept for call-site compatibility
|
||||
return (
|
||||
STATE_MAP[state] ?? {
|
||||
label: state ? state[0].toUpperCase() + state.slice(1) : "Unknown",
|
||||
kind: "idle",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user