From 502b814a6509ab51c14425c9d804e48a9a10d9f6 Mon Sep 17 00:00:00 2001 From: Logan Date: Thu, 20 Aug 2026 18:17:20 -0400 Subject: [PATCH] feat(jobs): let a panel answer for every kind, at either density Three properties the phone's band needs, added here so it is the same panel rather than a second job UI -- which is what keeps `applyJobControl` and its "you will discard hours of downloading" confirmation in the picture. `kinds="*"` is every kind, which is what the header indicator was for. Spelled as a star rather than taken as the meaning of an empty attribute, because empty is what a typo and a dropped binding both produce and "show everything" is the wrong thing to do by accident; empty still shows nothing. `density` is passed to `job-row`, whose `compact` variant its own source calls "the popover density" -- which is exactly what the band replaces. `full` stays the default, so the four settings call sites are untouched. `active-only` drops terminal rows. The band is in the layout, so a finished row there holds the content down after the work is done; Settings keeps them, because that is where "did the last scan work" is asked and a finished row there dismisses itself. --- frontend/src/components/jobs/job-panel.ts | 44 +++++++++++++-- frontend/test/components/job-panel.test.ts | 62 ++++++++++++++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/jobs/job-panel.ts b/frontend/src/components/jobs/job-panel.ts index 5bc5808..25470bc 100644 --- a/frontend/src/components/jobs/job-panel.ts +++ b/frontend/src/components/jobs/job-panel.ts @@ -51,6 +51,14 @@ export class JobPanel extends LitElement { * literal in a template, and one of them is inside an HTMX-adjacent * settings page where a property binding would be one more thing to * remember. + * + * **`*` means every kind**, which is the phone's band (#62) and + * nothing else: there, this panel is standing in for the header + * indicator, whose whole job was to be the one view of everything + * at once. It is spelled `*` rather than taken as the meaning of an + * empty attribute, because empty is what a typo and a missing + * binding both produce and "show everything" is the wrong thing to + * do by accident. Empty still shows nothing. */ @property({ type: String }) kinds = ''; @@ -59,6 +67,31 @@ export class JobPanel extends LitElement { @property({ type: String }) heading = ''; + /** + * Row density, passed to `job-row`. + * + * `full` adds elapsed time and per-job statistics and is what a + * settings section wants, so it stays the default and the four + * existing call sites are unchanged. `compact` is what `job-row` + * itself calls "the popover density", and it is what the phone's + * band uses (#62) — there this panel *is* the popover, on a screen + * 439 CSS px tall, and the full density spent 259 of them. + */ + @property({ type: String }) + density: 'compact' | 'full' = 'full'; + + /** + * Drop finished rows. + * + * For the phone's band (#62), which is *in the layout*: a finished + * row there is a banner that stays after the work is done and keeps + * the content pushed down. Settings keeps them, because that is + * where "did the last scan work" is asked, and a finished row there + * dismisses itself. + */ + @property({ type: Boolean, attribute: 'active-only' }) + activeOnly = false; + @state() private jobs: Job[] = []; @@ -162,9 +195,14 @@ export class JobPanel extends LitElement { } private get mine(): Job[] { - const wanted = this.wanted; + const ofKind = + this.kinds.trim() === '*' + ? this.jobs + : this.jobs.filter((job) => + this.wanted.has(job.kind as JobKind), + ); - return this.jobs.filter((job) => wanted.has(job.kind as JobKind)); + return this.activeOnly ? ofKind.filter((job) => !isTerminal(job)) : ofKind; } private openDetails(id: string) { @@ -196,7 +234,7 @@ export class JobPanel extends LitElement {