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:
Jonathan
2026-07-13 23:29:33 +02:00
parent 9d5e1ff58a
commit 9561e6e196
5 changed files with 52 additions and 61 deletions
+3 -8
View File
@@ -5,21 +5,16 @@ describe("describeJob", () => {
it("maps pipeline states to literal labels + severity kind", () => {
expect(describeJob("requested", "intake")).toMatchObject({ label: "Queued", kind: "idle" });
expect(describeJob("matching", "match")).toMatchObject({ label: "Searching", kind: "working" });
expect(describeJob("matched", "match")).toMatchObject({ label: "Searching", kind: "working" });
expect(describeJob("downloading", "download")).toMatchObject({ label: "Downloading", kind: "working" });
expect(describeJob("tagging", "tag")).toMatchObject({ label: "Tagging", kind: "verify" });
expect(describeJob("tagging", "tag")).toMatchObject({ label: "Finishing", kind: "verify" });
expect(describeJob("imported", "import")).toMatchObject({ label: "Pressed", kind: "done" });
expect(describeJob("needs_attention", "download")).toMatchObject({ label: "Needs attention", kind: "attention" });
});
it("derives a stepped progress from the pipeline stage", () => {
expect(describeJob("downloading", "download")).toMatchObject({ step: 4, totalSteps: 6 });
expect(describeJob("matching", "intake")).toMatchObject({ step: 1, totalSteps: 6 });
});
it("falls back gracefully for unknown states/stages", () => {
it("falls back gracefully for unknown states", () => {
expect(describeJob("weird", "intake").kind).toBe("idle");
expect(describeJob("weird", "intake").label).toBe("Weird");
expect(describeJob("downloading", "???").step).toBe(0);
});
});