146 lines
6.7 KiB
Markdown
146 lines
6.7 KiB
Markdown
# Admin roles, legacy plans & support desk
|
||||
|
|
|
|||
|
|
End-to-end work from the **20-agent** `admin-roles-support` swarm: legacy (A1) limited nav, platform staff roles, admin panel polish, support assign/claim + CSAT ratings.
|
|||
|
|
|
|||
|
|
**Runtime rule (tenant features):**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
effective(feature) = plan_allows(feature)
|
|||
|
|
AND global_section_enabled(section(feature))
|
|||
|
|
AND global_feature_enabled(feature)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Staff access is **orthogonal** to plan features: `users.staff_role` + `is_platform_admin` gate `/admin` and the support desk.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Doc map (agents 1–19)
|
|||
|
|
|
|||
|
|
| Doc | Owner | What it covers |
|
|||
|
|
|-----|-------|----------------|
|
|||
|
|
| [01-ux-research.md](./01-ux-research.md) | 1 | Admin / permissions / support UX patterns |
|
|||
|
|
| [02-current-inventory.md](./02-current-inventory.md) / [02-extension-points.json](./02-extension-points.json) | 2 | Pre-change inventory |
|
|||
|
|
| [03-roles-matrix.md](./03-roles-matrix.md) / [`.json`](./03-roles-matrix.json) | 3 | Legacy allow-list + role→feature matrix |
|
|||
|
|
| [04-contract.md](./04-contract.md) / [`.json`](./04-contract.json) | 4 | Unified plan + staff contract |
|
|||
|
|
| [05-legacy-seed.md](./05-legacy-seed.md) | 5 | Legacy profile seed / A1 detection |
|
|||
|
|
| [06-staff-roles.md](./06-staff-roles.md) | 6 | `admin` / `developer` / `support_staff` backend |
|
|||
|
|
| [07-admin-shell.md](./07-admin-shell.md) | 7 | Admin layout / nav chrome |
|
|||
|
|
| [08-permissions-ui.md](./08-permissions-ui.md) | 8 | Billing → Permissions panel |
|
|||
|
|
| [09-admin-billing-ui.md](./09-admin-billing-ui.md) | 9 | Plans list / assign polish |
|
|||
|
|
| [10-admin-orgs-ui.md](./10-admin-orgs-ui.md) | 10 | Users/companies + staff role grant |
|
|||
|
|
| [11-support-design.md](./11-support-design.md) / [11-support-api-contract.json](./11-support-api-contract.json) | 11 | Support desk design |
|
|||
|
|
| [12-support-backend.md](./12-support-backend.md) | 12 | Tickets / claim / assign API |
|
|||
|
|
| [13-support-ratings.md](./13-support-ratings.md) | 13 | CSAT 1–5 ratings |
|
|||
|
|
| [14-support-staff-ui.md](./14-support-staff-ui.md) | 14 | Staff inbox + assign UI |
|
|||
|
|
| [15-user-support-ui.md](./15-user-support-ui.md) | 15 | Customer support + rate UI |
|
|||
|
|
| [16-legacy-nav.md](./16-legacy-nav.md) | 16 | A1 image-nav gating |
|
|||
|
|
| [17-security.md](./17-security.md) | 17 | AuthZ / IDOR / CSRF pass |
|
|||
|
|
| [18-performance.md](./18-performance.md) | 18 | Indexes / list bounds |
|
|||
|
|
| [19-defaults-alignment.md](./19-defaults-alignment.md) | 19 | Ladder + Legacy + staff defaults |
|
|||
|
|
|
|||
|
|
Related: [`../plan-permissions/README.md`](../plan-permissions/README.md) (feature keys + admin Permissions API).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Schema (goose)
|
|||
|
|
|
|||
|
|
| Version | File | Purpose |
|
|||
|
|
|---------|------|---------|
|
|||
|
|
| 026 | `apps/api/sql/schema/026_plan_features.sql` | `plans.features` + global gates |
|
|||
|
|
| 027 | `027_capabilities_support_perf.sql` | Capabilities + support queue indexes |
|
|||
|
|
| 028 | `028_plan_is_legacy.sql` | `plans.is_legacy` + name backfill |
|
|||
|
|
| 029 | `029_staff_roles.sql` | `users.staff_role` |
|
|||
|
|
| 030 | `030_support_desk.sql` | CSAT table, claim queue cols, notification kinds |
|
|||
|
|
|
|||
|
|
Apply (Windows):
|
|||
|
|
|
|||
|
|
```powershell
|
|||
|
|
.\scripts\migrate.ps1
|
|||
|
|
# or from apps/api with DATABASE_URL set:
|
|||
|
|
go run github.com/pressly/goose/v3/cmd/goose@v3.24.3 -dir sql/schema postgres $env:DATABASE_URL up
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Strip UTF-8 BOM from new SQL if goose errors on `\ufeff-- +goose Up`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Code map
|
|||
|
|
|
|||
|
|
| Concern | Paths |
|
|||
|
|
|---------|--------|
|
|||
|
|
| Legacy detect/seed | `apps/api/internal/billing/legacy_plan*.go` |
|
|||
|
|
| Staff roles | `apps/api/internal/auth/staff*.go`, `httpapi/middleware.go`, `admin_staff_handlers.go` |
|
|||
|
|
| Support desk | `apps/api/internal/support/{desk,ratings,agents,tickets}.go`, `httpapi/support_*.go` |
|
|||
|
|
| Admin shell | `AdminNav.svelte`, `admin-nav-ui.svelte.ts`, root `+layout.svelte` |
|
|||
|
|
| Permissions UI | `PlanPermissionsPanel.svelte`, `admin-plan-permissions.ts` |
|
|||
|
|
| Orgs / staff grant | `routes/admin/users/+page.svelte`, `admin-orgs.ts` |
|
|||
|
|
| Legacy nav | `Nav.svelte`, `plan-capabilities.ts`, `PlanRouteGuard.svelte` |
|
|||
|
|
| Staff inbox | `routes/admin/support/**` |
|
|||
|
|
| User rate | `SupportTicketRating.svelte`, `routes/support/**` |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Manual verify checklist
|
|||
|
|
|
|||
|
|
### A1 / Legacy nav
|
|||
|
|
|
|||
|
|
- [ ] `goose` ≥ **30**; `seed-demo` (or `EnsureDefaultPlans`) so **Local Demo Co** is on plan **Legacy** (`is_legacy=true`)
|
|||
|
|
- [ ] Restart API after migrate/seed
|
|||
|
|
- [ ] Login as demo → Local Demo Co → sidebar shows only: Dashboard, Products, Feeds, Export Feeds, Categories, Attributes, Standard Fields, Usage & Billing, Settings
|
|||
|
|
- [ ] No Background Tasks / Processing, Stores, Campaigns, Brand, SEO (no empty **More** unless Platform admin)
|
|||
|
|
- [ ] `GET /api/billing/capabilities`: `processing.monitor` / `stores.hub` / `marketing.*` → false; nav parents → true
|
|||
|
|
- [ ] Deep link `/processing` → upgrade panel
|
|||
|
|
|
|||
|
|
### Admin panel polish
|
|||
|
|
|
|||
|
|
- [ ] `/admin` shell: sidebar groups Monitor / Manage, dark content, mobile drawer
|
|||
|
|
- [ ] `/admin/billing` → Permissions: profiles (incl. Legacy one-click), section toggles
|
|||
|
|
- [ ] `/admin/users`: companies + assign plan + staff role dialog
|
|||
|
|
|
|||
|
|
### Support assign + rate
|
|||
|
|
|
|||
|
|
- [ ] Customer: create ticket → staff reply → resolve → rate 1–5 (`SupportTicketRating`)
|
|||
|
|
- [ ] Staff: `/admin/support?scope=unassigned` → Claim → Mine; Unassign returns to queue
|
|||
|
|
- [ ] Full admin: ticket detail → Assign to staff → Apply
|
|||
|
|
- [ ] API: support routes under `RequireSupportDesk`; billing mutations still `RequirePlatformAdmin`
|
|||
|
|
|
|||
|
|
### Staff role access
|
|||
|
|
|
|||
|
|
- [ ] Grant `support_staff` from Users → that user sees Support (+ Overview redirects to Support), not Billing/Settings
|
|||
|
|
- [ ] `GET /api/admin/billing` (or plans) → **403** for support_staff
|
|||
|
|
- [ ] `admin` / `developer` / legacy `is_platform_admin` + NULL role → full console
|
|||
|
|
- [ ] Cannot PATCH own `staff_role`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Verify commands
|
|||
|
|
|
|||
|
|
```powershell
|
|||
|
|
.\scripts\migrate.ps1
|
|||
|
|
cd apps\api
|
|||
|
|
go test ./internal/billing/ ./internal/auth/ ./internal/support/ -count=1
|
|||
|
|
go test ./internal/httpapi/ -count=1 -run "Staff|Support|PlanFeature|PlanGate|AdminAuth|RequirePlatform|CSAT|Csat"
|
|||
|
|
cd ..\web
|
|||
|
|
npm run check
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Optional demo alignment:
|
|||
|
|
|
|||
|
|
```powershell
|
|||
|
|
cd apps\api
|
|||
|
|
go run ./cmd/seed-demo -postgres $env:DATABASE_URL
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Integration notes (agent 20)
|
|||
|
|
|
|||
|
|
- Merged duplicate `028_*` legacy SQL into `028_plan_is_legacy.sql`; renumbered staff → **029**, support desk → **030**.
|
|||
|
|
- `/admin` Overview: `support_staff` redirected to `/admin/support` (nav still shows Overview).
|
|||
|
|
- `seed-demo` / `EnsureLegacyDefaults` assigns **Legacy** to Local Demo Co / A1 cohort (not Enterprise all-ON).
|
|||
|
|
|
|||
|
|
### Remaining blockers / non-goals
|
|||
|
|
|
|||
|
|
1. Not every tenant mutation is `AssertFeature`'d (same as plan-permissions agent 10) — UI + critical writes first.
|
|||
|
|
2. Live browser QA of admin polish + CSAT requires a running `npm run dev` session and staff test user.
|
|||
|
|
3. OpenAPI public fragment may still omit some support/staff paths (session docs in plan-permissions / support contract JSON).
|