--- title: Contributions --- ```js import {API, apiFetch, pollDelay, waitForVisible} from "./components/config.js"; const POLL = 30_000; ``` ```js // Live poll for contributions const contribState = (async function*() { let failures = 0; while (true) { let data = [], ok = false; try { const r = await apiFetch("/contributions/"); ok = r.ok; data = ok ? await r.json() : []; } catch {} failures = ok ? 0 : failures + 1; yield {data, ok, ts: new Date()}; await waitForVisible(pollDelay({ok, base: POLL, failures})); } })(); ``` ```js const contribs = contribState.data ?? []; const _ok = contribState.ok ?? false; const _ts = contribState.ts; ``` # Contributions ```js import {injectTocTop} from "./components/toc-sidebar.js"; import {withDocHelp} from "./components/doc-overlay.js"; const _liveEl = html`
${_ok ? `Live · ${_ts?.toLocaleTimeString()}` : html`API offline`}
`; withDocHelp(_liveEl, "/docs/live-data"); injectTocTop("live-indicator", _liveEl); const _h1 = document.querySelector("#observablehq-main h1"); if (_h1) { _h1.style.position = "relative"; withDocHelp(_h1, "/docs/contributions"); } ``` ```js // Filters const typeFilter = Inputs.select(["all", "br", "fr", "ep", "upr"], {label: "Type", value: "all"}); const statFilter = Inputs.select( ["all", "draft", "submitted", "acknowledged", "accepted", "rejected", "merged", "withdrawn"], {label: "Status", value: "all"} ); const repoFilter = Inputs.text({label: "Target repo", placeholder: "filter by repo…"}); display(html`
${typeFilter}${statFilter}${repoFilter}
`); ``` ```js const tf = typeFilter.value; const sf = statFilter.value; const rf = repoFilter.value?.trim().toLowerCase() ?? ""; const filtered = contribs.filter(c => (tf === "all" || c.type === tf) && (sf === "all" || c.status === sf) && (!rf || (c.target_repo ?? "").toLowerCase().includes(rf)) ); ``` ## Summary ```js const typeLabels = {br: "Bug Report", fr: "Feature Request", ep: "Extension Point", upr: "Upstream PR"}; const typeCounts = Object.fromEntries(["br","fr","ep","upr"].map(t => [ t, contribs.filter(c => c.type === t).length ])); const needsFollowUp = contribs.filter(c => ["submitted","acknowledged"].includes(c.status)).length; display(html`

Total

${contribs.length}

${["br","fr","ep","upr"].map(t => html`

${typeLabels[t]}

${typeCounts[t]}

`)}
${needsFollowUp > 0 ? html`
⚠ ${needsFollowUp} contribution(s) awaiting upstream response (submitted / acknowledged)
` : ""} `); ``` ## Status Kanban ```js const statusCols = [ {key: "draft", label: "Draft", color: "#aaa"}, {key: "submitted", label: "Submitted", color: "steelblue"}, {key: "acknowledged", label: "Acknowledged",color: "#f0a500"}, {key: "accepted", label: "Accepted", color: "#4caf50"}, {key: "merged", label: "Merged", color: "#2e7d32"}, {key: "rejected", label: "Rejected", color: "#e53935"}, {key: "withdrawn", label: "Withdrawn", color: "#bbb"}, ]; const colMap = {}; for (const c of filtered) { (colMap[c.status] = colMap[c.status] ?? []).push(c); } const activeCols = statusCols.filter(s => colMap[s.key]?.length); if (activeCols.length === 0) { display(html`

No contributions match the current filters.

`); } else { display(html`
${activeCols.map(s => html`
${s.label} ${colMap[s.key].length}
${colMap[s.key].map(c => html`
${c.type.toUpperCase()}
${c.title}
${c.target_org || c.target_repo ? html`
${[c.target_org, c.target_repo].filter(Boolean).join("/")}
` : ""} ${c.body_path ? html`
${c.body_path}
` : ""}
${new Date(c.created_at).toLocaleDateString()}
`)}
`)}
`); } ``` ## All Contributions ```js display(Inputs.table(filtered.map(c => ({ Type: c.type.toUpperCase(), Title: c.title, Status: c.status, Target: [c.target_org, c.target_repo].filter(Boolean).join("/") || "—", Created: new Date(c.created_at).toLocaleDateString(), })), {maxWidth: 900})); ```