← All frames
Master-admin · Frame (d) · route /admin/portals

Master-admin states

Every state the master-admin console must render is contract. Loading, the fresh-install empty case, a load failure, the suspend confirmation and its root-portal refusal, the create-portal slug conflict, and the one most often got wrong: a non-platform-admin who reaches this route must be denied in place — never silently shown the fleet, never bounced to a login they'll sail back through.

(d1) Loading

All portals

Loading…

(d2) Fresh install — only the root portal

All portals

1 portal

No tenant portals yet

Only the seeded root portal exists. Create the first tenant portal to onboard a customer — they'll get their own users, catalog, and billing.

(d3) Couldn't load

All portals

(d4) Confirm suspend — a destructive, tenant-wide action
(d5) Root portal cannot be suspended — fail-closed
(d6) Slug already taken — inline on create
(d7) Not a platform admin — 403, never a sign-in redirect

All portals

Go to my portal console

Contract. Loading covers the in-flight GET /api/v1/admin/portals. (d2) is a real state — a fresh install has only the seeded root portal. (d3) is a non-2xx list with data-action="retry". (d4) confirms PATCH .../{id} { status: "suspended" }. (d5): the root portal is refused 409 ROOT_PORTAL_PROTECTED — client pre-disable + server guard. (d6): POST /api/v1/admin/portals returning 409 SLUG_TAKEN renders inline on the field, never a toast that loses the form. (d7) is the rule: a 403 is an authorization denial (platform-admin only, Permit ReBAC) shown in place — only a 401 re-authenticates.

Review · portal-admin-consoles approve or reject each flow ↓
master-admin-portalspendingApproveReject
portal-consolependingApproveReject
portal-billingpendingApproveReject