From d44f2cc223898ccb81c2cb3bc296512bfb1e31e7 Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 26 Feb 2026 16:49:33 +0100 Subject: [PATCH] dashboard: move charts to top of main content on workstreams and progress pages workstreams: decouple filter form from display (Generators.input pattern); Status Distribution chart now renders before the filter bar and table. Dependencies section follows after. progress: Event Volume chart moved above the filter controls and table; no reactive changes needed (chart uses raw data, not filtered). Co-Authored-By: Claude Sonnet 4.6 --- state-hub/dashboard/src/progress.md | 52 +++++++++++++------------- state-hub/dashboard/src/workstreams.md | 31 +++++++++------ 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/state-hub/dashboard/src/progress.md b/state-hub/dashboard/src/progress.md index dff1b5c..deba8d9 100644 --- a/state-hub/dashboard/src/progress.md +++ b/state-hub/dashboard/src/progress.md @@ -46,32 +46,6 @@ withDocHelp(_liveEl, "/docs/live-data"); injectTocTop("live-indicator", _liveEl); ``` -```js -const authorOpts = ["(all)", ...new Set(data.map(e => e.author ?? "unknown"))].sort(); -const typeOpts = ["(all)", ...new Set(data.map(e => e.event_type))].sort(); - -const authorFilter = view(Inputs.select(authorOpts, {label: "Author"})); -const typeFilter = view(Inputs.select(typeOpts, {label: "Event type"})); -const sinceFilter = view(Inputs.date({label: "Since"})); -``` - -```js -const filtered = data.filter(e => - (authorFilter === "(all)" || (e.author ?? "unknown") === authorFilter) && - (typeFilter === "(all)" || e.event_type === typeFilter) && - (!sinceFilter || new Date(e.created_at) >= sinceFilter) -); - -display(html`

${filtered.length} events shown (append-only, no deletions).

`); - -display(Inputs.table(filtered.map(e => ({ - Time: new Date(e.created_at).toLocaleString(), - Type: e.event_type, - Author: e.author ?? "—", - Summary: e.summary, -})), {rows: 50})); -``` - ## Event Volume (Last 30 Days) ```js @@ -105,6 +79,32 @@ display(byDay.length === 0 ); ``` +```js +const authorOpts = ["(all)", ...new Set(data.map(e => e.author ?? "unknown"))].sort(); +const typeOpts = ["(all)", ...new Set(data.map(e => e.event_type))].sort(); + +const authorFilter = view(Inputs.select(authorOpts, {label: "Author"})); +const typeFilter = view(Inputs.select(typeOpts, {label: "Event type"})); +const sinceFilter = view(Inputs.date({label: "Since"})); +``` + +```js +const filtered = data.filter(e => + (authorFilter === "(all)" || (e.author ?? "unknown") === authorFilter) && + (typeFilter === "(all)" || e.event_type === typeFilter) && + (!sinceFilter || new Date(e.created_at) >= sinceFilter) +); + +display(html`

${filtered.length} events shown (append-only, no deletions).

`); + +display(Inputs.table(filtered.map(e => ({ + Time: new Date(e.created_at).toLocaleString(), + Type: e.event_type, + Author: e.author ?? "—", + Summary: e.summary, +})), {rows: 50})); +``` + diff --git a/state-hub/dashboard/src/workstreams.md b/state-hub/dashboard/src/workstreams.md index 371a87a..e19e08b 100644 --- a/state-hub/dashboard/src/workstreams.md +++ b/state-hub/dashboard/src/workstreams.md @@ -67,7 +67,8 @@ import {MultiSelect} from "./components/multiselect.js"; const DOMAINS = ["custodian", "railiance", "markitect", "coulomb_social", "personhood", "foerster_capabilities"]; const STATUSES = ["active", "blocked", "completed", "archived"]; -const filters = view(Inputs.form( +// Create filter form without displaying — shown below the chart +const _filtersForm = Inputs.form( { domain: MultiSelect(DOMAINS, {label: "Domain", placeholder: "All domains"}), status: MultiSelect(STATUSES, {label: "Status", placeholder: "All statuses"}), @@ -79,7 +80,11 @@ const filters = view(Inputs.form(
${owner}
`, } -)); +); +``` + +```js +const filters = Generators.input(_filtersForm); ``` ```js @@ -89,15 +94,6 @@ const filtered = data.filter(w => (filters.status.length === 0 || filters.status.includes(w.status)) && (!filters.owner || (w.owner ?? "").toLowerCase().includes(filters.owner.toLowerCase())) ); - -display(Inputs.table(filtered.map(w => ({ - Title: w.title, - Domain: w.domain, - Status: w.status, - Owner: w.owner ?? "—", - Due: w.due_date ?? "—", - Updated: new Date(w.updated_at).toLocaleDateString(), -})), {rows: 20})); ``` ## Status Distribution @@ -119,6 +115,19 @@ display(Plot.plot({ })); ``` +```js +display(_filtersForm); + +display(Inputs.table(filtered.map(w => ({ + Title: w.title, + Domain: w.domain, + Status: w.status, + Owner: w.owner ?? "—", + Due: w.due_date ?? "—", + Updated: new Date(w.updated_at).toLocaleDateString(), +})), {rows: 20})); +``` + ## Dependencies ```js