--- title: Goals --- ```js import {API} from "./components/config.js"; const POLL = 20_000; ``` ```js const goalsState = (async function*() { while (true) { let domains = [], domainGoals = [], repoGoals = [], repos = [], ok = false; try { const [rd, rdg, rrg, rr] = await Promise.all([ fetch(`${API}/domains/?status=active`), fetch(`${API}/domain-goals/`), fetch(`${API}/repo-goals/`), fetch(`${API}/repos/`), ]); ok = rd.ok && rdg.ok && rrg.ok && rr.ok; if (ok) { [domains, domainGoals, repoGoals, repos] = await Promise.all([ rd.json(), rdg.json(), rrg.json(), rr.json(), ]); } } catch {} yield {domains, domainGoals, repoGoals, repos, ok, ts: new Date()}; await new Promise(res => setTimeout(res, POLL)); } })(); ``` ```js const domains = goalsState.domains ?? []; const domainGoals = goalsState.domainGoals ?? []; const repoGoals = goalsState.repoGoals ?? []; const repos = goalsState.repos ?? []; const _ok = goalsState.ok ?? false; const _ts = goalsState.ts; ``` ```js // ── Indexes ──────────────────────────────────────────────────────────────────── const repoById = Object.fromEntries(repos.map(r => [r.id, r])); const domainById = Object.fromEntries(domains.map(d => [d.id, d])); // Domain goals keyed by domain_id; active first, then superseded, then archived const goalsByDomain = {}; for (const g of domainGoals) { if (!goalsByDomain[g.domain_id]) goalsByDomain[g.domain_id] = []; goalsByDomain[g.domain_id].push(g); } const STATUS_ORDER = {active: 0, superseded: 1, archived: 2}; for (const id of Object.keys(goalsByDomain)) { goalsByDomain[id].sort((a, b) => (STATUS_ORDER[a.status] ?? 9) - (STATUS_ORDER[b.status] ?? 9) ); } // Repo goals keyed by domain_goal_id (primary) and repo_id (for unlinked) const repoGoalsByDomainGoal = {}; const unlinkedRepoGoals = []; // active repo goals with no domain_goal_id for (const rg of repoGoals) { if (rg.domain_goal_id) { if (!repoGoalsByDomainGoal[rg.domain_goal_id]) repoGoalsByDomainGoal[rg.domain_goal_id] = []; repoGoalsByDomainGoal[rg.domain_goal_id].push(rg); } else if (rg.status === "active") { unlinkedRepoGoals.push(rg); } } // Sort repo goals within each domain goal by priority asc for (const id of Object.keys(repoGoalsByDomainGoal)) { repoGoalsByDomainGoal[id].sort((a, b) => a.priority - b.priority); } // KPI const domainsWithActiveGoal = domains.filter(d => (goalsByDomain[d.id] ?? []).some(g => g.status === "active")); const domainsWithoutGoal = domains.filter(d => !(goalsByDomain[d.id] ?? []).some(g => g.status === "active")); const totalActiveRepoGoals = repoGoals.filter(g => g.status === "active").length; ``` # Goals ```js import {injectTocTop} from "./components/toc-sidebar.js"; import {withDocHelp} from "./components/doc-overlay.js"; // ── Live indicator ───────────────────────────────────────────────────────────── const _liveEl = html`
make api`}
API offline — run make api from state-hub/.
No active domains found.
`); } else { // Domains with active goal first, then those without const sorted = [ ...domainsWithActiveGoal.sort((a, b) => a.slug.localeCompare(b.slug)), ...domainsWithoutGoal.sort((a, b) => a.slug.localeCompare(b.slug)), ]; display(html`Active repo goals not yet associated with a domain goal.