68 lines
1.5 KiB
Markdown
68 lines
1.5 KiB
Markdown
|
|
---
|
||
|
|
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,
|
||
|
|
}));
|
||
|
|
```
|