From 3f656baad1989ac22966ad36dd5caf215203a78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Sun, 16 Aug 2026 00:20:06 +0200 Subject: [PATCH] Make Duration sortable on the runs page It was left out because duration isn't a stored column, just started_at/ended_at math - now expressed as a SQL case expression so it can be sorted the same way as the other columns. Still-running rows sort by elapsed-so-far; never-started (queued) rows sort as NULL, which puts them out of the way at whichever end matches the current direction. Co-Authored-By: Claude Sonnet 5 --- app/src/app/(app)/runs/page.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/app/(app)/runs/page.tsx b/app/src/app/(app)/runs/page.tsx index 7d4b411..01d1e55 100644 --- a/app/src/app/(app)/runs/page.tsx +++ b/app/src/app/(app)/runs/page.tsx @@ -23,11 +23,18 @@ export const metadata: Metadata = { title: "Run History" }; const PAGE_SIZE = 25; +// Duration isn't a stored column - it's derived from started_at/ended_at (both unix seconds), so +// it needs a SQL expression rather than a plain column reference to be sortable. Still-running +// rows (ended_at IS NULL) count as elapsed-so-far; never-started (queued) rows sort as NULL, +// which SQLite puts first in ASC / last in DESC - keeping them out of the way either direction. +const durationExpr = sql`case when ${runs.startedAt} is null then null else coalesce(${runs.endedAt}, unixepoch()) - ${runs.startedAt} end`; + const SORT_COLUMNS = { script: runs.scriptName, status: runs.status, triggeredBy: runs.triggeredBy, started: runs.startedAt, + duration: durationExpr, } as const; type SortKey = keyof typeof SORT_COLUMNS; const DEFAULT_SORT: SortKey = "started"; @@ -172,7 +179,7 @@ export default async function RunsPage({ searchParams }: RunsPageProps) { {sortHeader("status", "Status")} {sortHeader("triggeredBy", "Triggered by")} {sortHeader("started", "Started")} - Duration + {sortHeader("duration", "Duration")}