feat(dashboard): live-tick queued-age + terminal-age labels in rebuild queue
Stamp data-rqe-enqueued / data-rqe-finished / data-rqe-state on queued and terminal rqe-when spans. A 30s ticker updates them in place so 'queued 2m ago' and 'done 5m ago' labels advance as time passes. Complement to the keyed rebuild-queue row cache: rows now persist across snapshots, so without a ticker these static fmtAgo labels would become arbitrarily stale. The running-entry elapsed ticker (data-rqe-elapsed, 1s interval) already handled that state; this fills the gap for queued and terminal states.
This commit is contained in:
parent
029ae69953
commit
6f0b176778
1 changed files with 39 additions and 4 deletions
|
|
@ -2585,9 +2585,15 @@ window.marked = marked;
|
|||
// Source chip (manual / meta_update / auto_update / crash_recover).
|
||||
li.append(' ', el('span', { class: 'rqe-source rqe-source-' + entry.source }, entry.source));
|
||||
// Timing: queued Xs ago when pending, elapsed when running,
|
||||
// finished Xs ago for terminal.
|
||||
// finished Xs ago for terminal. Queued + terminal stamps use
|
||||
// data-rqe-enqueued / data-rqe-finished so the 30s ticker below
|
||||
// can keep them fresh — keyed rows persist across snapshots, so
|
||||
// without a ticker "queued 2m ago" would never advance.
|
||||
if (entry.state === 'queued') {
|
||||
li.append(' ', el('span', { class: 'rqe-when' }, '· queued ' + fmtAgo(entry.enqueued_at)));
|
||||
li.append(' ', el('span', {
|
||||
class: 'rqe-when',
|
||||
'data-rqe-enqueued': String(entry.enqueued_at),
|
||||
}, '· queued ' + fmtAgo(entry.enqueued_at)));
|
||||
} else if (entry.state === 'running' && entry.started_at) {
|
||||
const elapsed = Math.max(0, Math.floor(Date.now() / 1000 - entry.started_at));
|
||||
li.append(' ', el('span', {
|
||||
|
|
@ -2595,7 +2601,11 @@ window.marked = marked;
|
|||
'data-rqe-elapsed': String(entry.started_at),
|
||||
}, '· ' + fmtElapsed(elapsed)));
|
||||
} else if (entry.finished_at) {
|
||||
li.append(' ', el('span', { class: 'rqe-when' }, '· ' + entry.state + ' ' + fmtAgo(entry.finished_at)));
|
||||
li.append(' ', el('span', {
|
||||
class: 'rqe-when',
|
||||
'data-rqe-finished': String(entry.finished_at),
|
||||
'data-rqe-state': entry.state,
|
||||
}, '· ' + entry.state + ' ' + fmtAgo(entry.finished_at)));
|
||||
}
|
||||
// Reason (truncated; full text on hover).
|
||||
if (entry.reason) {
|
||||
|
|
@ -2671,14 +2681,39 @@ window.marked = marked;
|
|||
// `renderQueueEntry`; the ticker avoids a full re-render just for the
|
||||
// wall-clock update.
|
||||
setInterval(() => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
for (const span of document.querySelectorAll('.rqe-when[data-rqe-elapsed]')) {
|
||||
const started = parseInt(span.dataset.rqeElapsed, 10);
|
||||
if (!started) continue;
|
||||
const elapsed = Math.max(0, Math.floor(Date.now() / 1000 - started));
|
||||
const elapsed = Math.max(0, now - started);
|
||||
span.textContent = '· ' + fmtElapsed(elapsed);
|
||||
}
|
||||
for (const span of document.querySelectorAll('.build-logs-runtime[data-bl-elapsed]')) {
|
||||
const started = parseInt(span.dataset.blElapsed, 10);
|
||||
if (!started) continue;
|
||||
const elapsed = Math.max(0, now - started);
|
||||
span.textContent = fmtElapsed(elapsed);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// 30s ticker for queued-age and terminal-age labels. Keyed rows
|
||||
// persist across rebuild_queue_changed snapshots, so without this
|
||||
// "queued 1m ago" / "done 5m ago" labels would never advance.
|
||||
setInterval(() => {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
for (const span of document.querySelectorAll('.rqe-when[data-rqe-enqueued]')) {
|
||||
const enqueued = parseInt(span.dataset.rqeEnqueued, 10);
|
||||
if (!enqueued) continue;
|
||||
span.textContent = '· queued ' + fmtAgo(enqueued);
|
||||
}
|
||||
for (const span of document.querySelectorAll('.rqe-when[data-rqe-finished]')) {
|
||||
const finished = parseInt(span.dataset.rqeFinished, 10);
|
||||
if (!finished) continue;
|
||||
const state = span.dataset.rqeState || '';
|
||||
span.textContent = '· ' + state + ' ' + fmtAgo(finished);
|
||||
}
|
||||
}, 30_000);
|
||||
|
||||
// ─── reminders ──────────────────────────────────────────────────────────
|
||||
// Reminders aren't part of /api/state (separate sqlite table, separate
|
||||
// mutation cadence). refreshReminders() is called from refreshState() for
|
||||
|
|
|
|||
Loading…
Reference in a new issue