diff --git a/scripts/build-frames-site.mjs b/scripts/build-frames-site.mjs index 9ed51f6d..6e4b2bf5 100644 --- a/scripts/build-frames-site.mjs +++ b/scripts/build-frames-site.mjs @@ -31,6 +31,107 @@ const escapeHtml = (s) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c] ); +// Where approval decisions are recorded. A GitHub issue is the durable, auditable +// record; the design-approval workflow (or a maintainer) reads it and flips the +// per-flow `approved` in the manifest. Prefilled via plain query params — the only +// form of issue prefill that is 100% reliable (dropdown prefill is not). +const REPO_SLUG = 'izzywdev/FuzeFront'; +const PAGES_BASE = 'https://izzywdev.github.io/FuzeFront'; + +/** The per-flow list is the source of truth for approval: `build.flows`. (Some + * legacy manifests carried approvable entries under `frames`; fall back to that + * only if `build.flows` is absent, so the two never disagree silently.) */ +function flowsOf(manifest) { + const bf = manifest?.build?.flows; + if (Array.isArray(bf) && bf.length) return bf; + const legacy = Array.isArray(manifest?.frames) + ? manifest.frames.filter((f) => typeof f.approved === 'boolean') + : []; + return legacy; +} + +/** New-issue URL that records a decision for one flow. Approve and Reject differ + * only in the prefilled `decision:` line, so the reviewer's click IS the record. */ +function approvalHref(slug, flow, decision, stamp) { + const flowId = flow.id ?? flow.orchestrator ?? 'flow'; + const title = `design-approval: ${decision} ${slug} / ${flowId}`; + const body = [ + '', + '', + '```yaml', + `feature: ${slug}`, + `flow: ${flowId}`, + `route: ${flow.route ?? ''}`, + `decision: ${decision}`, + `stamp: ${stamp ?? ''}`, + '```', + '', + `Frames: ${PAGES_BASE}/${slug}/`, + '', + decision === 'reject' ? '**Reason for rejection (required):**' : '_Optional note:_', + '', + ].join('\n'); + const q = new URLSearchParams({ title, body, labels: 'design-approval' }); + return `https://github.com/${REPO_SLUG}/issues/new?${q.toString()}`; +} + +/** Fixed approval bar injected into every published frame of a feature, so the + * reviewer can approve/reject from wherever they are in the flow — not only the + * index. Derived entirely from the manifest's flow list. */ +function renderApprovalBar(slug, manifest) { + const flows = flowsOf(manifest); + const stamp = manifest?.stamp ? String(manifest.stamp).slice(0, 12) : ''; + if (!flows.length) { + return `