Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
97 lines
5.0 KiB
Markdown
97 lines
5.0 KiB
Markdown
# 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`](./README.md) (when present) · prior matrix: [`../plan-permissions/`](../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
|
||
|
||
1. **Plan profiles** — Legacy, Free, Starter, Growth, Business, Enterprise/all-on. Apply writes a full override matrix via `PUT /api/admin/plans/{id}/features`.
|
||
2. **Legacy (A1)** — Allowlist nav (Dashboard, Products, Feeds, Export Feeds, Categories, Attributes, Standard Fields, Billing, Settings). `processing.monitor` and stores/marketing/integrations/support extras stay **OFF**. Prominent **Apply Legacy (A1)** when the selected plan is A1-like (`A1*`, name contains `legacy`, or `is_legacy`).
|
||
3. **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).
|
||
4. **Search / filters** — Text (key or label), section filter, state filter (all / enabled / disabled / differs from plan-name default).
|
||
5. **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 `{}`.
|
||
6. **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`](./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
|
||
|
||
1. Platform admin → `/admin/billing` → **Permissions**.
|
||
2. Select **Free** → profile chip should show Free ✓ (or Apply Free). Toggle one key → **Custom overrides** + Differs badge.
|
||
3. Select **A1** (or create an A1-named deal) → **Apply Legacy (A1)** → `processing.monitor` off; Products/Feeds on. Reload → persisted.
|
||
4. Section **Enable section** / **Disable section** on Catalog for the selected plan only.
|
||
5. Flip a **Global section** switch; confirm tenant capabilities respect it (agent 9/16).
|
||
6. **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
|
||
|
||
1. **ASSUMPTION:** Empty `plans.features` means “use defaults”; Apply Profile always writes an explicit full map.
|
||
2. **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.
|
||
3. Global “Off + features” updates **global feature masters**, not every plan’s overrides.
|