From 0551512fef8c9493648e8dc6d2d64c0e74ede913 Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Sat, 23 May 2026 15:26:24 -0400 Subject: [PATCH] feat(jobs): show absolute completion timestamp for done/failed jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Time column now anchors on the wall clock when a job is in a terminal state — "done 2:23 PM · 5m ago" / "failed May 22 · 14:24 · 1h ago" — so the operator can correlate with logs and other timestamps without hovering. Queued/running jobs keep the relative-only format since their timestamp is constantly moving. Widen the column to 180px to accommodate the longer label. Co-Authored-By: Claude Opus 4.7 (1M context) --- services/web-ui/public/screens-jobs.jsx | 23 +++++++++++++++++++++++ services/web-ui/public/styles-rest.css | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/services/web-ui/public/screens-jobs.jsx b/services/web-ui/public/screens-jobs.jsx index f1dd172..5d53ff8 100644 --- a/services/web-ui/public/screens-jobs.jsx +++ b/services/web-ui/public/screens-jobs.jsx @@ -21,6 +21,23 @@ function _fmtAbsolute(iso) { } catch { return iso; } } +// Compact clock for the inline jobs cell — "2:23 PM" if today, +// "May 22 · 2:23 PM" if a different day. Full datetime stays in the tooltip. +function _fmtCompact(iso) { + if (!iso) return ''; + try { + const d = new Date(iso); + const now = new Date(); + const sameDay = d.getFullYear() === now.getFullYear() + && d.getMonth() === now.getMonth() + && d.getDate() === now.getDate(); + const time = d.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' }); + if (sameDay) return time; + const date = d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); + return date + ' · ' + time; + } catch { return iso; } +} + function Jobs({ navigate }) { const [tab, setTab] = React.useState('all'); const [jobs, setJobs] = React.useState(window.ZAMPP_DATA.JOBS); @@ -194,6 +211,12 @@ function JobRow({ job, onRetry, onDelete }) { {(() => { const t = _jobTimeFor(job); if (!t) return '—'; + // Terminal states (done/failed) anchor on the absolute clock so the + // operator can correlate with logs; queued/running show relative + // since it's a moving target. + if (job.status === 'done' || job.status === 'failed') { + return t.label + ' ' + _fmtCompact(t.iso) + ' · ' + window.ZAMPP_API.fmtRelative(t.iso); + } return t.label + ' ' + window.ZAMPP_API.fmtRelative(t.iso); })()} diff --git a/services/web-ui/public/styles-rest.css b/services/web-ui/public/styles-rest.css index 37a3bc0..8c056cb 100644 --- a/services/web-ui/public/styles-rest.css +++ b/services/web-ui/public/styles-rest.css @@ -389,7 +389,7 @@ } .job-row { display: grid; - grid-template-columns: 20px 110px 1fr 90px 200px 130px 80px 90px; + grid-template-columns: 20px 110px 1fr 90px 200px 180px 80px 90px; align-items: center; gap: 12px; padding: 10px 16px; -- 2.45.2