generated from coulomb/repo-seed
Dashboard workstreams: multi-select filters that survive data polls
- Replace single-select Domain/Status dropdowns with checkbox multi-selects - Use Inputs.form() with a custom template to lay the three filters out side by side in a card-style filter bar - Filter options are now static constants (DOMAINS, STATUSES) — no dependency on the polled data, so selections are never reset on refresh - Empty selection = no filter applied (show all); any checked item = include - Updated filtered computation and wsWithDeps to use filters.domain / filters.status array semantics Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -56,19 +56,32 @@ display(html`<div class="live-bar">
|
||||
```
|
||||
|
||||
```js
|
||||
const domainOpts = ["(all)", ...new Set(data.map(w => w.domain))].sort();
|
||||
const statusOpts = ["(all)", "active", "blocked", "completed", "archived"];
|
||||
// Static options — no dependency on `data`, so selections survive polls
|
||||
const DOMAINS = ["custodian", "railiance", "markitect", "coulomb_social", "personhood", "foerster_capabilities"];
|
||||
const STATUSES = ["active", "blocked", "completed", "archived"];
|
||||
|
||||
const domainFilter = view(Inputs.select(domainOpts, {label: "Domain"}));
|
||||
const statusFilter = view(Inputs.select(statusOpts, {label: "Status"}));
|
||||
const ownerFilter = view(Inputs.text({label: "Owner contains"}));
|
||||
const filters = view(Inputs.form(
|
||||
{
|
||||
domain: Inputs.checkbox(DOMAINS, {label: "Domain"}),
|
||||
status: Inputs.checkbox(STATUSES, {label: "Status"}),
|
||||
owner: Inputs.text({label: "Owner contains", placeholder: "filter by owner…"}),
|
||||
},
|
||||
{
|
||||
template: ({domain, status, owner}) => html`<div class="filter-bar">
|
||||
<div class="filter-group">${domain}</div>
|
||||
<div class="filter-group">${status}</div>
|
||||
<div class="filter-group filter-group-text">${owner}</div>
|
||||
</div>`,
|
||||
}
|
||||
));
|
||||
```
|
||||
|
||||
```js
|
||||
// Empty array = no filter applied (show all)
|
||||
const filtered = data.filter(w =>
|
||||
(domainFilter === "(all)" || w.domain === domainFilter) &&
|
||||
(statusFilter === "(all)" || w.status === statusFilter) &&
|
||||
(!ownerFilter || (w.owner ?? "").toLowerCase().includes(ownerFilter.toLowerCase()))
|
||||
(filters.domain.length === 0 || filters.domain.includes(w.domain)) &&
|
||||
(filters.status.length === 0 || filters.status.includes(w.status)) &&
|
||||
(!filters.owner || (w.owner ?? "").toLowerCase().includes(filters.owner.toLowerCase()))
|
||||
);
|
||||
|
||||
display(Inputs.table(filtered.map(w => ({
|
||||
@@ -104,11 +117,12 @@ display(Plot.plot({
|
||||
|
||||
```js
|
||||
// Build dep cards from the enriched open_workstreams in the summary
|
||||
const wsWithDeps = openWs.filter(w =>
|
||||
(domainFilter === "(all)" || (data.find(d => d.id === w.id)?.domain ?? "unknown") === domainFilter) &&
|
||||
(statusFilter === "(all)" || w.status === statusFilter) &&
|
||||
(w.depends_on.length > 0 || w.blocks.length > 0)
|
||||
);
|
||||
const wsWithDeps = openWs.filter(w => {
|
||||
const domain = data.find(d => d.id === w.id)?.domain ?? "unknown";
|
||||
return (filters.domain.length === 0 || filters.domain.includes(domain)) &&
|
||||
(filters.status.length === 0 || filters.status.includes(w.status)) &&
|
||||
(w.depends_on.length > 0 || w.blocks.length > 0);
|
||||
});
|
||||
|
||||
if (wsWithDeps.length === 0) {
|
||||
display(html`<p class="dim">No dependency edges recorded for the current filter. Use <code>create_dependency()</code> via the MCP server to link workstreams.</p>`);
|
||||
@@ -132,6 +146,9 @@ if (wsWithDeps.length === 0) {
|
||||
<style>
|
||||
.live-bar { font-size: 0.8rem; color: gray; text-align: right; margin-bottom: 0.5rem; }
|
||||
.dim { color: gray; font-style: italic; }
|
||||
.filter-bar { display: flex; flex-wrap: wrap; gap: 1.5rem; align-items: flex-start; padding: 0.75rem 1rem; background: var(--theme-background-alt); border-radius: 8px; margin-bottom: 1rem; }
|
||||
.filter-group { min-width: 140px; }
|
||||
.filter-group-text { align-self: flex-end; }
|
||||
.dep-grid { display: flex; flex-direction: column; gap: 0.75rem; }
|
||||
.dep-card { border: 1px solid #e0e0e0; border-radius: 6px; padding: 0.75rem 1rem; background: var(--theme-background-alt, #fafafa); }
|
||||
.dep-title { font-weight: 600; margin-bottom: 0.25rem; }
|
||||
|
||||
Reference in New Issue
Block a user