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 <noreply@anthropic.com>
This commit is contained in:
@@ -46,32 +46,6 @@ withDocHelp(_liveEl, "/docs/live-data");
|
|||||||
injectTocTop("live-indicator", _liveEl);
|
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`<p><strong>${filtered.length}</strong> events shown (append-only, no deletions).</p>`);
|
|
||||||
|
|
||||||
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)
|
## Event Volume (Last 30 Days)
|
||||||
|
|
||||||
```js
|
```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`<p><strong>${filtered.length}</strong> events shown (append-only, no deletions).</p>`);
|
||||||
|
|
||||||
|
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}));
|
||||||
|
```
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; }
|
.live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -67,7 +67,8 @@ import {MultiSelect} from "./components/multiselect.js";
|
|||||||
const DOMAINS = ["custodian", "railiance", "markitect", "coulomb_social", "personhood", "foerster_capabilities"];
|
const DOMAINS = ["custodian", "railiance", "markitect", "coulomb_social", "personhood", "foerster_capabilities"];
|
||||||
const STATUSES = ["active", "blocked", "completed", "archived"];
|
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"}),
|
domain: MultiSelect(DOMAINS, {label: "Domain", placeholder: "All domains"}),
|
||||||
status: MultiSelect(STATUSES, {label: "Status", placeholder: "All statuses"}),
|
status: MultiSelect(STATUSES, {label: "Status", placeholder: "All statuses"}),
|
||||||
@@ -79,7 +80,11 @@ const filters = view(Inputs.form(
|
|||||||
<div class="filter-owner">${owner}</div>
|
<div class="filter-owner">${owner}</div>
|
||||||
</div>`,
|
</div>`,
|
||||||
}
|
}
|
||||||
));
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
const filters = Generators.input(_filtersForm);
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@@ -89,15 +94,6 @@ const filtered = data.filter(w =>
|
|||||||
(filters.status.length === 0 || filters.status.includes(w.status)) &&
|
(filters.status.length === 0 || filters.status.includes(w.status)) &&
|
||||||
(!filters.owner || (w.owner ?? "").toLowerCase().includes(filters.owner.toLowerCase()))
|
(!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
|
## 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
|
## Dependencies
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|||||||
Reference in New Issue
Block a user