state-hub/dashboard/src/workstreams.md

68 lines
1.5 KiB
Markdown
Raw Normal View History

---
title: Workstreams
---
# Workstreams
```js
const workstreams = await FileAttachment("data/workstreams.json").json();
const data = Array.isArray(workstreams) ? workstreams : [];
```
```js
const domainFilter = view(Inputs.select(
["(all)", ...new Set(data.map(w => w.domain ?? "unknown"))],
{ label: "Domain" }
));
const statusFilter = view(Inputs.select(
["(all)", "active", "blocked", "completed", "archived"],
{ label: "Status" }
));
const ownerFilter = view(Inputs.text({ label: "Owner contains" }));
```
```js
const filtered = data.filter(w =>
(domainFilter === "(all)" || w.domain === domainFilter) &&
(statusFilter === "(all)" || w.status === statusFilter) &&
(!ownerFilter || (w.owner ?? "").toLowerCase().includes(ownerFilter.toLowerCase()))
);
const STATUS_COLOR = {
active: "green",
blocked: "orange",
completed: "blue",
archived: "gray",
};
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,
}));
```
```js
import * as Plot from "npm:@observablehq/plot";
display(Plot.plot({
title: "Workstream Status Distribution",
marks: [
Plot.barX(
Object.entries(
filtered.reduce((acc, w) => { acc[w.status] = (acc[w.status] ?? 0) + 1; return acc; }, {})
).map(([status, count]) => ({ status, count })),
{ y: "status", x: "count", fill: "status", tip: true }
),
Plot.ruleX([0]),
],
marginLeft: 80,
width: 500,
}));
```