Policies: let team leaders configure policies in the UI (#6634)

Frontend follow-up to #6632 (team-scoped policies, editing gated to team
leaders on the backend). Brings the UI's edit gate in line.

## Problem
The policy config UI gated editing to `config.isAdmin`. On SaaS, org
users are never the single global admin, so **no one could open the
policy editor** — the same lockout #6632 fixed on the backend.

## Fix
`usePolicies` now allows a **team leader** to configure, falling back to
a global admin self-hosted:

```ts
canConfigure =
  config != null && (!config.enableLogin || isTeamLeader || config.isAdmin === true);
```

- SaaS → `isTeamLeader` (from `useSaaSTeam()`) — team leaders can
configure; members get the read-only surface.
- Self-hosted → `config.isAdmin` (the core `useSaaSTeam` stub returns
`false`, so admins aren't locked out).
- Login disabled (single-user) → always allowed.
- The `config != null` guard keeps the gate closed until app-config
resolves, so edit controls never flash for users who can't use them.

The two locked-policy banners now read "Contact a team leader to change
this policy" (updated in the `t()` defaults and the `en-GB`
translations).

## Verification
- Typecheck clean (proprietary + saas); eslint clean.
- Tests pass: `usePolicies`, `PoliciesSidebar`.
This commit is contained in:
Reece Browne
2026-06-11 23:51:26 +01:00
committed by GitHub
parent 962119e14f
commit e3e49c07ae
4 changed files with 12 additions and 14 deletions
@@ -279,7 +279,7 @@ export function PolicyDetailPanel({
icon={<LockIcon sx={{ fontSize: "1rem" }} />}
description={t(
"policies.detail.managedByOrg",
"Managed by your organization. Contact an admin to change this policy.",
"Managed by your organization. Contact a team leader to change this policy.",
)}
/>
)}
@@ -179,7 +179,7 @@ export function PolicySetupWizard({
)}
description={t(
"policies.wizard.lockedDescription",
"Contact an admin to change this policy.",
"Contact a team leader to change this policy.",
)}
/>
</div>