From 73f1ae6e2f75ffc678fd3e997a4f8c5ca119c629 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Mon, 20 Jul 2026 20:56:42 +0200 Subject: [PATCH] feat(web): order the In-queue tab next-up-first with a "Next up" marker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The queue tab showed newest-first (API returns createdAt desc) but the worker claims the oldest queued job next (claim_next: ORDER BY createdAt ASC) — so the item that ran next was at the BOTTOM. Sort the queue bucket ascending so it matches processing order, and badge the first claimable row (oldest, not individually paused) as "Next up". Co-Authored-By: Claude Opus 4.8 (1M context) --- web/src/app/_ui/job-row.tsx | 3 +++ web/src/app/design.css | 7 +++++++ web/src/app/queue.tsx | 9 +++++++++ 3 files changed, 19 insertions(+) diff --git a/web/src/app/_ui/job-row.tsx b/web/src/app/_ui/job-row.tsx index 4a8d5d6..63e7fb3 100644 --- a/web/src/app/_ui/job-row.tsx +++ b/web/src/app/_ui/job-row.tsx @@ -14,6 +14,7 @@ export function JobRow({ album, kind, label, + marker, meta, note, bar, @@ -22,6 +23,7 @@ export function JobRow({ album: string; kind: Kind; label: string; + marker?: string; meta?: ReactNode; note?: ReactNode; bar?: ReactNode; @@ -31,6 +33,7 @@ export function JobRow({

+ {marker ? {marker} : null} {album} · {artist}

{meta ?
{meta}
: null} diff --git a/web/src/app/design.css b/web/src/app/design.css index 0bc14a7..fcdd2b1 100644 --- a/web/src/app/design.css +++ b/web/src/app/design.css @@ -388,6 +388,13 @@ nav.contents .sep { flex: 1; } .tab .n { color: var(--rule-2); margin-left: 6px; } .tab.has-errors .n { color: var(--alert); } +/* "Next up" badge on the first queued row (the item the worker claims next) */ +.nextup { + display: inline-block; vertical-align: middle; margin-right: 9px; transform: translateY(-2px); + font-family: var(--mono); font-size: 0.56rem; letter-spacing: 0.13em; text-transform: uppercase; + color: var(--accent-ink); background: var(--accent); padding: 2px 7px; border-radius: 4px; +} + /* ── Kebab menu (⋮ popup for press actions) ───────────── */ .dept.has-actions { align-items: center; } .dept.has-actions .fill { transform: none; } diff --git a/web/src/app/queue.tsx b/web/src/app/queue.tsx index 3c060c0..b5066dd 100644 --- a/web/src/app/queue.tsx +++ b/web/src/app/queue.tsx @@ -189,6 +189,14 @@ export function Queue() { const j = jobOf(r); buckets[categoryOf(j.state, j.currentStage)].push(r); } + // The API returns rows newest-first, but the worker claims the OLDEST queued job next + // (claim_next: ORDER BY createdAt ASC). Order the queue tab to match, so the item that runs + // next sits at the top instead of the bottom. + const ts = (r: Row) => (r.createdAt ? new Date(r.createdAt).getTime() : 0); + buckets.queue.sort((a, b) => ts(a) - ts(b)); + // "Next up" = the first queued item the worker will actually claim (oldest, not individually + // paused) — mark it so the ordering reads clearly. + const nextUpId = buckets.queue.find((r) => !jobOf(r).paused)?.id ?? null; const queuedCount = buckets.queue.length; const downloading = buckets.active.length > 0; // Smart default: land on the tab that has something worth looking at. @@ -289,6 +297,7 @@ export function Queue() { album={r.album} kind={d.kind} label={d.label} + marker={r.id === nextUpId ? "Next up" : undefined} meta={meta} note={note} bar={bar}