Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
5.0 KiB
08 — Admin Permissions UI (agent 8/20)
Owns: Admin → Billing → Permissions tab (PlanPermissionsPanel + admin-plan-permissions client).
Entry: /admin/billing → Permissions.
Docs home: README.md (when present) · prior matrix: ../plan-permissions/.
PROBLEM
Make plan feature editing clean and usable: named profiles (Legacy / Free / Starter…), bulk section toggles, search/filters, clear default vs custom, and one-click Legacy for A1-like plans — wired to real admin APIs (no stub mode).
Features delivered
- Plan profiles — Legacy, Free, Starter, Growth, Business, Enterprise/all-on. Apply writes a full override matrix via
PUT /api/admin/plans/{id}/features. - Legacy (A1) — Allowlist nav (Dashboard, Products, Feeds, Export Feeds, Categories, Attributes, Standard Fields, Billing, Settings).
processing.monitorand stores/marketing/integrations/support extras stay OFF. Prominent Apply Legacy (A1) when the selected plan is A1-like (A1*, name containslegacy, oris_legacy). - Bulk section toggles — Per-plan Enable/Disable section (all keys in that section). Global section masters stay in the right rail (optional “+ features” updates global feature-gate rows).
- Search / filters — Text (key or label), section filter, state filter (all / enabled / disabled / differs from plan-name default).
- Default vs custom — Badges: Legacy / Default ladder / Custom deal; Defaults vs Custom overrides; profile chip shows ✓ when the resolved matrix matches a named profile; Clear overrides sends
{}. - Real APIs — No 404 stub / in-session-only path. Mutations use feature endpoints; errors surface in alerts.
Source files
| File | Intent |
|---|---|
apps/web/src/lib/components/admin/PlanPermissionsPanel.svelte |
Permissions checklist UI |
apps/web/src/lib/admin-plan-permissions.ts |
Profiles, Legacy detect, API client |
apps/web/src/lib/plan-feature-catalog.ts |
Catalog / sections (shared) |
apps/web/src/routes/admin/billing/+page.svelte |
Hosts Permissions tab |
API contract used
| Method | Path | Use |
|---|---|---|
GET |
/api/admin/plans |
List packages |
GET|PUT |
/api/admin/plans/{id}/features |
Load / replace overrides |
POST |
/api/admin/plans/{id}/features/enable-all |
Full ON |
POST |
/api/admin/plans/{id}/features/disable-all |
Full OFF |
GET|PUT |
/api/admin/feature-gates |
Global masters |
PUT |
/api/admin/feature-gates/sections/{section} |
Global section switch |
Auth: session + platform admin + CSRF.
Legacy allowlist (summary)
Canonical ON set matches 03-roles-matrix.md / legacy_user — including AI titles/descriptions, API keys, and capability.ai_processing / api_access. Shell skips shell.support_notifications; dashboard skips dashboard.store_reconnect.
OFF (among others): processing.monitor, all stores.*, all marketing.*, integrations, support tickets, campaign/email/brand/SEO/BYOK capabilities.
Machine source: LEGACY_FEATURE_ALLOWLIST in admin-plan-permissions.ts. Backend seed (agent 5) should align; UI Apply Legacy persists even if seed lags.
Visual language
Uses admin shell tokens (border-border, bg-card, text-muted-foreground, bg-primary/15 active chips) — same vocabulary as AdminNav / agent 7 shell — not hard-coded slate cards.
Manual verify
- Platform admin →
/admin/billing→ Permissions. - Select Free → profile chip should show Free ✓ (or Apply Free). Toggle one key → Custom overrides + Differs badge.
- Select A1 (or create an A1-named deal) → Apply Legacy (A1) →
processing.monitoroff; Products/Feeds on. Reload → persisted. - Section Enable section / Disable section on Catalog for the selected plan only.
- Flip a Global section switch; confirm tenant capabilities respect it (agent 9/16).
- Clear overrides → Defaults badge; resolved view matches plan-name defaults (Legacy-like names still resolve Legacy on the client when empty).
Coordination
| Agent | Touchpoint |
|---|---|
| 3 / 4 | Roles matrix + contract — profiles follow Legacy allow-nav |
| 5 | Backend Legacy seed / is_legacy — UI already detects A1 + is_legacy |
| 7 | Admin shell tokens — Permissions uses the same |
| 9 | Plans table → Edit features deep-link (unchanged prop selectedPlanId) |
| 16 | User nav gating consumes capabilities after Legacy apply |
| 19 | Defaults alignment — keep LEGACY_FEATURE_ALLOWLIST in sync with seeds |
ASSUMPTIONS
- ASSUMPTION: Empty
plans.featuresmeans “use defaults”; Apply Profile always writes an explicit full map. - ASSUMPTION: Until agent 5 seeds Legacy on the API, A1 empty overrides may resolve all-ON server-side; Apply Legacy is the reliable admin path.
- Global “Off + features” updates global feature masters, not every plan’s overrides.