Skip to content

Billing: gate the pages on owner OR admin, not owner alone - #224

Merged
pavlo-flamingo merged 1 commit into
mainfrom
billing-gate-owner-and-admin
Aug 13, 2026
Merged

Billing: gate the pages on owner OR admin, not owner alone#224
pavlo-flamingo merged 1 commit into
mainfrom
billing-gate-owner-and-admin

Conversation

@pavlo-flamingo

Copy link
Copy Markdown
Contributor

Billing was owner-only. It is now open to owners and admins — the role part of #137, ported on its own so it can land without the Billing redesign.

Admins run the workspace day to day: they add the devices the plan is sized for and they are the ones who hit the limits. Owners keep the money; admins need to see and change what it buys. Everyone else stays out — to a member, billing is neither useful nor theirs.

The gate

src/app/hooks/use-billing-access-gate.ts answers "may this viewer manage billing?" for every billing surface, in three states rather than two: loading | allowed | denied. Same discipline as the feature flags, for the same reason — "not answered yet" is not "no", and notFound() throws, so a page closed on a guessed denied never reopens once /me lands. Roles arrive as plain strings from the gateway (OWNER, ADMIN, …) with no schema enum behind them, so the comparison is case-insensitive, matching employee-details-view.tsx.

Callers switched: the Billing & Usage page, the plan picker page, the Settings hub card (billings === 'on' && access === 'allowed'), and the subscription lock screen.

use-owner-gate.ts stays, narrowed to what ownership actually decides — self-deletion, where handing the workspace over IS the point.

The refusal screen

BillingOwnerOnlyScreenBillingRestrictedScreen: "owner-only" is no longer true. Copy now points at the owner and the admins, and the lock screen says "Only the workspace owner or an admin can restore it." The native wording still splits on isBillingHidden() and names no subscription or payment (App Store Guideline 3.1.1). A role refusal still explains itself instead of 404ing — the hub hides the card, so whoever lands here followed a bookmark or a shared link; a definitive billings === 'off' still 404s, since there the section genuinely does not exist for the tenant.

Not ported from #137

That PR also moves the role check past the lazy boundary into subscription-plan-lock-content.tsx, so that waiting on /me renders the paywall unpriced instead of the refusal. It depends on SubscriptionSettingsLoading, which arrives with the redesign; here loading stays grouped with the refusal, as the file already documents.

npm run type-check and Biome clean.

Admins run the workspace day to day — they add the devices the plan is
sized for and they hit the limits — so the section they cannot open is
the one telling them why. Owners keep the money; admins need to see and
change what it buys. Everyone else stays out: to a member, billing is
neither useful nor theirs.

`use-billing-access-gate.ts` answers that question for every billing
surface: the Billing & Usage page, the plan picker, the Settings hub
card and the subscription lock screen. Same three states as the feature
flags, for the same reason — "not answered yet" is not "no", and
`notFound()` throws, so a page closed on a guessed `denied` never
reopens once `/me` lands.

`use-owner-gate.ts` stays, narrowed to what ownership actually decides:
self-deletion, where the point IS handing the workspace over.

The refusal screen is renamed with its meaning — `BillingRestrictedScreen`,
"Billing is restricted" — since "owner-only" is no longer true, and its
copy now points at the owner AND the admins. The native wording still
splits on `isBillingHidden()`, naming no subscription or payment (App
Store Guideline 3.1.1).

Ported from #137, which carries this alongside the Billing redesign.
@pavlo-flamingo
pavlo-flamingo requested a review from a team as a code owner August 13, 2026 13:24
@pavlo-flamingo
pavlo-flamingo requested a review from a team August 13, 2026 13:24
@pavlo-flamingo
pavlo-flamingo enabled auto-merge (squash) August 13, 2026 13:25
@pavlo-flamingo
pavlo-flamingo merged commit f9d7b17 into main Aug 13, 2026
3 checks passed
@pavlo-flamingo
pavlo-flamingo deleted the billing-gate-owner-and-admin branch August 13, 2026 13:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants