feat(web): order the In-queue tab next-up-first with a "Next up" marker

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) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-20 20:56:42 +02:00
parent c49cee9a20
commit 73f1ae6e2f
3 changed files with 19 additions and 0 deletions
+3
View File
@@ -14,6 +14,7 @@ export function JobRow({
album, album,
kind, kind,
label, label,
marker,
meta, meta,
note, note,
bar, bar,
@@ -22,6 +23,7 @@ export function JobRow({
album: string; album: string;
kind: Kind; kind: Kind;
label: string; label: string;
marker?: string;
meta?: ReactNode; meta?: ReactNode;
note?: ReactNode; note?: ReactNode;
bar?: ReactNode; bar?: ReactNode;
@@ -31,6 +33,7 @@ export function JobRow({
<div className="stripe" /> <div className="stripe" />
<div> <div>
<h3 className="title"> <h3 className="title">
{marker ? <span className="nextup">{marker}</span> : null}
{album} <span className="artist">· {artist}</span> {album} <span className="artist">· {artist}</span>
</h3> </h3>
{meta ? <div className="meta">{meta}</div> : null} {meta ? <div className="meta">{meta}</div> : null}
+7
View File
@@ -388,6 +388,13 @@ nav.contents .sep { flex: 1; }
.tab .n { color: var(--rule-2); margin-left: 6px; } .tab .n { color: var(--rule-2); margin-left: 6px; }
.tab.has-errors .n { color: var(--alert); } .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) ───────────── */ /* ── Kebab menu (⋮ popup for press actions) ───────────── */
.dept.has-actions { align-items: center; } .dept.has-actions { align-items: center; }
.dept.has-actions .fill { transform: none; } .dept.has-actions .fill { transform: none; }
+9
View File
@@ -189,6 +189,14 @@ export function Queue() {
const j = jobOf(r); const j = jobOf(r);
buckets[categoryOf(j.state, j.currentStage)].push(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 queuedCount = buckets.queue.length;
const downloading = buckets.active.length > 0; const downloading = buckets.active.length > 0;
// Smart default: land on the tab that has something worth looking at. // Smart default: land on the tab that has something worth looking at.
@@ -289,6 +297,7 @@ export function Queue() {
album={r.album} album={r.album}
kind={d.kind} kind={d.kind}
label={d.label} label={d.label}
marker={r.id === nextUpId ? "Next up" : undefined}
meta={meta} meta={meta}
note={note} note={note}
bar={bar} bar={bar}