Closes out the design pass with the missing piece: a real client-side
mock-API pipeline so the write-path CTAs the design shows (invite a
teammate, run a scan, kick off a workflow test, request reactivation)
actually do something visible without a backend.
* `public/mockServiceWorker.js` — generated by `pnpm exec msw init`.
* `src/mocks/handlers.ts` — POST handlers for `/api/team/invites`,
`/api/scans`, `/api/workflows/:id/test`, `/api/billing/reactivate`.
Each returns the design's mono status-code header
(`201 · invite.created`, `202 · scan.queued`, etc.) so the toast
surface reads identical to the handoff. A `x-bp-tenant-status` hint
header lets the same handler respond 402 (frozen) or 410 (archived)
without needing a real session.
* `src/mocks/browser.ts` — thin `setupWorker(...handlers)` wrapper,
imported lazily so prod bundles don't pull MSW.
* `src/components/portal/MockWorker.tsx` — client component that boots
the worker only when `window.__BP_MOCK_API__` is true (set by
`[slug]/layout` when `BP_DEV_FIXTURE` is on the server). Real Auth.js
builds skip the worker entirely.
* `src/components/portal/ToastHost.tsx` — global bottom-right toast
queue, mounted in `[slug]/layout`. Emits via a custom event so any
client component can call `toast({ msg, code })` without prop-drilling.
* `src/components/portal/InviteButton.tsx` — first live write affordance.
Modal with email + role-segmented buttons, POSTs to `/api/team/invites`
with the tenant-status hint header, surfaces 201/402/410 differently.
Wired into the Team page.
* `src/middleware.ts` — added `mockServiceWorker.js` to the matcher
exclusion list so the host-rewrite doesn't 404 the worker script.
Verified end-to-end via Playwright: SW registers at the root scope,
click Invite member → fill email → Send invitation → MSW intercepts →
toast "Invitation sent · 201 · invite.created" → modal closes.
This closes the last open M10.2 task. Branch is ready to review/merge.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Six existing customer-area shells under [slug]/* rebuilt against the
handoff design (sections §2/§4/§5/§6/§7/§8). Every screen reuses the
new Panel / Monogram / Sev primitives and the ledger-table token system
so the visual contract stays single-source-of-truth in globals.css.
* `[slug]/settings` (Organization, IT_ADMIN) — legal entity dl, primary
contact card, plan & seats meter, products subscribed kv-list
(ENTITLED green dot / TRIALING amber dot).
* `[slug]/settings/users` (Team, IT_ADMIN) — bracketed member ledger
with role chips, last-active mono dim, active/invited dot status.
Invite affordance present, modal wiring deferred.
* `[slug]/billing` (Billing, CXO + FINANCE + IT_ADMIN) — current plan
card with monthly net + 19% VAT, seats + evidence-storage meters,
payment method block that swaps to "Payment failed → Re-activate"
when tenant.status is frozen, full invoices ledger with paid/due dot.
* `[slug]/audit` (Audit log, LEGAL + IT_ADMIN) — filter bar (search +
event-type chip toggles + product select), ledger table with denied
red dot, footer count + retention note.
* `[slug]/settings/integrations` (SSO, IT_ADMIN) — read-only OIDC
summary pulling from KEYCLOAK_ISSUER / KEYCLOAK_CLIENT_ID, IdP-group→
role mapping table.
* `[slug]/products` (Products index, USER+) — 2x2 product grid with
live cards (entitled + trialing chips) and "Coming soon" dashed
placeholders, plus a cross-product findings table with filter chips.
Plus a new `NotAllowed` 403 surface in the same ledger language that
replaces the inline "NotAuthorized" message used by the old shells, so
forbidden routes still look like the rest of the portal.
Every page goes through `getPortalSession()` so `BP_DEV_FIXTURE` still
swaps between admin / user / trial / frozen / archived without
Keycloak. Every screen returns 200 against
`BP_DEV_FIXTURE=admin-acme pnpm dev`.
Still to come on this branch:
* Workflows editor (palette + canvas + inspector + drag-wiring)
* ⌘K command palette + toasts
* Product launch detail (per-product page)
* Login redesign (mock SSO picker + violet gradient panel)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Four real surfaces wired to tenant-registry (settings, settings/api-keys CRUD, audit pagination, products live entitlements), five forward-looking empty states with CTAs. 56 vitest tests + 10 Playwright canaries. lib/format.ts consolidates date helpers.
Refs: M10.1
10 route shells under /[slug]/, role-filtered Nav, backstage stub at /__backstage__, dashboard reads session.products to render tiles. src/lib/session.ts is the canonical role × surface matrix; canSee() is the only RBAC primitive in the portal (real enforcement remains at the API layer). 24 vitest tests; 100% src/lib coverage.
Refs: M5.2