From e18a669aa0cd47d5ef3404dbb4896cfbaed534c6 Mon Sep 17 00:00:00 2001 From: stroblme Date: Fri, 28 Aug 2026 11:02:59 +0200 Subject: [PATCH] Write a run's elapsed time once, in the duration formatter's units The runs table drew when a run started and how long it took in two notations ('56m ago | 55.5 min'), and for a run still going those are the same reading twice. `dur` now steps past the minute into hours and days, the ago reading is derived from it, and a running run writes only the elapsed one. --- frontend/src/components/Runs/RunsScreen.tsx | 27 +++++++++++++++------ frontend/src/lib/utils.check.ts | 2 ++ frontend/src/lib/utils.ts | 5 ++++ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/Runs/RunsScreen.tsx b/frontend/src/components/Runs/RunsScreen.tsx index 5156c89..a2f2110 100644 --- a/frontend/src/components/Runs/RunsScreen.tsx +++ b/frontend/src/components/Runs/RunsScreen.tsx @@ -6,7 +6,6 @@ import { useRef } from "react" import type { fluksio__api__routes__runs__RunRow as RunRow } from "@/client" import { MAX_SERIES } from "@/components/Common/UplotChart" import { ValuePreview } from "@/components/Flow/ValuePreview" -import { ago } from "@/components/Health/queries" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" import { @@ -296,6 +295,12 @@ function ParamValue({ value }: { value: unknown }) { return {paramText(value)} } +/** How long ago a moment was, in `dur`'s units so a row's two times agree. */ +function since(ts: unknown): string { + if (!ts) return "—" + return `${dur(Date.now() - Date.parse(String(ts)))} ago` +} + function RunsTable({ runs, search, @@ -456,14 +461,20 @@ function RunsTable({ only the store's, which is then the whole answer. */} {shortCommit(run.origin_commit || run.commit || "") || "—"} - + {/* When it started, and how long it then took. Two readings of - one thing, so one column rather than two. */} - {ago(String(run.created_at ?? ""))} - | - - {run.duration_ms ? dur(run.duration_ms) : "—"} - + one thing, so one column rather than two — and while a run is + still going they are the same reading, so it is written once: + how long it has been running is also how long ago it began. */} + {run.status === "running" ? ( + `${dur(run.duration_ms)} ago` + ) : ( + <> + {since(run.created_at)} + | + {run.duration_ms ? dur(run.duration_ms) : "—"} + + )} ))} diff --git a/frontend/src/lib/utils.check.ts b/frontend/src/lib/utils.check.ts index a0c7d84..5e46fc5 100644 --- a/frontend/src/lib/utils.check.ts +++ b/frontend/src/lib/utils.check.ts @@ -38,6 +38,8 @@ assert.equal(dur(0), "0 ms") assert.equal(dur(203.63), "204 ms") assert.equal(dur(1240), "1.24 s") assert.equal(dur(90_000), "1.5 min") +assert.equal(dur(12_600_000), "3.5 h") +assert.equal(dur(259_200_000), "3 d") // The plain window down to 0.01 ms, so a fast run keeps one unit across polls. assert.equal(dur(0.4), "0.4 ms") diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index acaca2a..3969708 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -44,6 +44,8 @@ export function si(value: number, digits = 3): string { /** Floor, divisor and unit per step, largest first. */ const DUR_STEPS: [number, number, string][] = [ + [86_400_000, 86_400_000, "d"], + [3_600_000, 3_600_000, "h"], [60_000, 60_000, "min"], [1_000, 1_000, "s"], [0.01, 1, "ms"], @@ -63,6 +65,9 @@ const DUR_STEPS: [number, number, string][] = [ * window transposed onto units. That floor is deliberate: without it a tile * polled every ten seconds flips between "400 µs" and "1.2 ms", and a reading * that changes unit on every poll reads as broken rather than as fast. + * + * It keeps stepping past the minute into hours and days, so that an elapsed + * time is one notation wherever it is written — "3.5 h", never "210 min". */ export function dur(ms: number, digits = 3): string { if (!Number.isFinite(ms)) return "--"