From 6d951bd760f520c52c25c4921ddc59e7d89d7617 Mon Sep 17 00:00:00 2001 From: Dennis Thiessen Date: Mon, 15 Jun 2026 13:39:27 +0200 Subject: [PATCH] show last-run status/time/message for finished jobs in the admin panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Jobs panel only surfaced live progress; once a job finished you couldn't see when it ran, whether it succeeded, or its message (e.g. a regime/collector error). Add a "Last run · " line per job, colored by status, from the runtime_* fields the backend already returns. Co-Authored-By: Claude Opus 4.8 --- frontend/src/components/admin/JobControls.tsx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/frontend/src/components/admin/JobControls.tsx b/frontend/src/components/admin/JobControls.tsx index 959c4df..4faa2ee 100644 --- a/frontend/src/components/admin/JobControls.tsx +++ b/frontend/src/components/admin/JobControls.tsx @@ -13,6 +13,22 @@ function formatNextRun(iso: string | null): string { return `in ${hrs}h`; } +function formatAgo(iso: string | null | undefined): string { + if (!iso) return ''; + const mins = Math.floor((Date.now() - new Date(iso).getTime()) / 60_000); + if (mins < 1) return 'just now'; + if (mins < 60) return `${mins}m ago`; + const hrs = Math.floor(mins / 60); + if (hrs < 24) return `${hrs}h ago`; + return `${Math.floor(hrs / 24)}d ago`; +} + +function lastRunColor(status: string | null | undefined): string { + if (status === 'error') return 'text-red-300'; + if (status === 'rate_limited') return 'text-amber-300'; + return 'text-gray-500'; +} + export function JobControls() { const { data: jobs, isLoading } = useJobs(); const toggleJob = useToggleJob(); @@ -139,6 +155,13 @@ export function JobControls() { Not registered )} + {!job.running && job.runtime_finished_at && ( +
+ Last run {formatAgo(job.runtime_finished_at)} + {job.runtime_status ? ` · ${job.runtime_status}` : ''} + {job.runtime_message ? ` — ${job.runtime_message}` : ''} +
+ )} {job.running && (