Files
certctl/web/src/components
shankar0123 cfe76ad381 auth-bundle-1 Phase 10 follow-up: approvals queue GUI + transparent E2E deferral
Self-audit caught the missing GUI surface for Phase 9's flow #6
(profile edit gated → second admin approves → edit lands). The
backend path is fully wired + tested in 69a508d; this commit adds
the operator-facing UI so an approver can act without curl.

# ApprovalsPage

Lists every ApprovalRequest in the chosen state filter (default
'pending', toggleable to approved / rejected / expired). Renders
both kinds:

  - cert_issuance — Rank-7 row with cert + job populated.
  - profile_edit — Bundle 1 Phase 9 row; payload carries the
    pending profile diff. Pill-rendered amber so an approver can
    distinguish at a glance.

Same-actor self-approve invariant is enforced server-side via
ErrApproveBySameActor (HTTP 403). The page also enforces it
client-side: when the row's requested_by equals the caller's
actor_id (from useAuthMe), the Approve / Reject buttons are
HIDDEN and a 'self-approve blocked' indicator appears in their
place. The operator literally cannot click the wrong button.

Approve + Reject prompt for an optional note via window.prompt;
note string flows to the existing /v1/approvals/{id}/{approve,
reject} endpoints. Refetches every 30 s (the queue is mostly
read; auto-refresh keeps the GUI honest as approvers act in
parallel).

# Wiring

* /auth/approvals route in main.tsx.
* Layout nav entry between API Keys and Auth Settings.
* api/client.ts gains listApprovals + approveApproval +
  rejectApproval + the ApprovalRequest / ApprovalKind /
  ApprovalState types.

# Tests

ApprovalsPage.test.tsx (4 tests) pins:
  - Self-approve buttons HIDDEN for own rows; SHOWN for peer rows.
  - profile_edit kind renders with the amber pill.
  - Approve POSTs the right URL with the note.
  - Empty state.

Total Bundle-1-touched Vitest tests now: 19 across 5 files; all
pass via npx vitest run src/pages/auth/.

# Transparent deferrals (called out for the record)

The prompt's 9-flow Playwright E2E suite remains DEFERRED. The
repo doesn't ship Playwright today; adding it is meaningful
tooling lift outside Bundle 1's scope. Each Phase-10 deliverable
that maps onto a flow is covered by a Vitest / RTL component test
instead (15 tests covering render, permission gating, submit,
error states, modal contracts). Full E2E coverage and the
≥75% src/pages/auth/ coverage metric are tracked as Phase 12
work; @vitest/coverage-v8 will land in the same commit that
wires the coverage gate.

# Verifications

* npx tsc --noEmit clean.
* npm run build green.
* 19 Vitest tests pass.
2026-05-09 21:12:06 +00:00
..