state-hub/dashboard/src/suggestions.md
tegwick d8a2bb5c6b
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
docs: work-record consolidation references and STATE-WP-0076 ready
Align dashboard reference pages with intake-as-discovery and suggestions as
legacy, frame work records as unit vs structure artefacts, and add the ready
STATE-WP-0076 plan for DoC/DoR quality policies. Include generated work-record
index and recent legacy-meter evidence captures.
2026-07-22 19:56:27 +02:00

3 KiB
Raw Blame History

title
Suggestions (legacy)
import {apiFetch, pollDelay, waitForVisible} from "./components/config.js";
const POLL = 30_000;
const sugState = (async function*() {
  let failures = 0;
  while (true) {
    let data = [], ok = false;
    try {
      const r = await apiFetch("/suggestions/?rank=wsjf&limit=100");
      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}));
  }
})();
const suggestions = sugState.data ?? [];
const _ok = sugState.ok ?? false;
const _ts = sugState.ts;

Suggestions (legacy)

import {injectTocTop} from "./components/toc-sidebar.js";
import {withDocHelp} from "./components/doc-overlay.js";

const _liveEl = html`<div class="live-indicator">
  <span style="color:${_ok ? 'var(--theme-foreground-focus)' : 'red'}">●</span>
  ${_ok ? `Live · ${_ts?.toLocaleTimeString()}` : html`<span style="color:red">API offline</span>`}
</div>`;
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/suggestions"); }

display(html`<p class="dim"><strong>Read-only legacy.</strong> New gated needs are <em>intake</em> work records
  (<code>GET /intakes/</code>, MCP <code>create_intake</code>) — see
  <a href="/docs/intakes">Intakes</a> and <a href="/docs/suggestions">Suggestions reference</a>.
  This page lists historical <code>GET /suggestions/</code> rows only; mutations return HTTP 410.</p>`);
display(html`<p class="dim">Former ranking: WSJF = (base_value + relevance_weight × relevance) / job_size.
  <a href="/wsjf-triage">Daily WSJF triage</a> is workplan-oriented advisory review.</p>`);
const stageBadge = (stage) => {
  const colors = {
    suggestion: "#6b7280",
    requirement: "#2563eb",
    promoted: "#059669",
    declined: "#9ca3af",
  };
  return html`<span style="font-size:0.75rem;padding:0.1rem 0.45rem;border-radius:4px;background:${colors[stage] ?? "#ccc'};color:#fff">${stage}</span>`;
};

const rows = suggestions.map((s) => html`<tr>
  <td>${stageBadge(s.stage)}</td>
  <td><strong>${s.title}</strong>${s.origin_ref ? html`<br/><code style="font-size:0.75rem">${s.origin_ref}</code>` : ""}</td>
  <td>${s.domain_slug || "—"}</td>
  <td style="text-align:right">${s.relevance}</td>
  <td style="text-align:right">${s.wsjf?.toFixed?.(1) ?? s.wsjf}</td>
  <td style="font-size:0.8rem">${s.last_requested_at ? new Date(s.last_requested_at).toLocaleString() : "—"}</td>
</tr>`);

display(html`<table class="dashboard-table" style="width:100%;font-size:0.88rem">
  <thead><tr>
    <th>Stage</th><th>Title</th><th>Domain</th><th>Relevance</th><th>WSJF</th><th>Last requested</th>
  </tr></thead>
  <tbody>${rows.length ? rows : html`<tr><td colspan="6" class="dim">No open suggestions yet.</td></tr>`}</tbody>
</table>`);