Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# 10 — Admin users & organizations UI (agent 10/20)
|
||||
|
||||
**Owns:** `/admin/users` — users directory, company list with plan view/assign, platform staff role assignment.
|
||||
**Does not own:** Billing plans table (agent 9), Permissions panel (agent 8), staff middleware/model (agent 6), admin shell chrome (agent 7 — nav label only).
|
||||
**Gate:** `requirePlatformAdmin` (full admin / developer). `support_staff` is excluded.
|
||||
|
||||
---
|
||||
|
||||
## PROBLEM
|
||||
|
||||
`/admin/users` was a thin client-filtered user list (no companies, no plan view, no staff role edits, no server pagination). Operators needed one place to manage orgs and staff after cutover.
|
||||
|
||||
---
|
||||
|
||||
## Delivered
|
||||
|
||||
1. **Users tab** — server-paginated (`limit`/`offset`, page size 25), search (`q`), staff-only filter, set-password invite + local dev tools preserved.
|
||||
2. **Staff roles** — dialog → `PATCH /api/admin/users/{id}/staff-role` with `admin` | `developer` | `support_staff` | clear. Uses agent 6 API; self-change blocked server-side.
|
||||
3. **Companies tab** — paginated list with active plan name, legacy/public/custom badge, credits remaining/total, “without plan” filter.
|
||||
4. **Assign plan** — dialog lists all admin plans (ladder + legacy + custom) → `POST /api/admin/plans/assign`.
|
||||
5. **API enrichments** — `GET /api/admin/users` returns `total`, `staff_role`, `resolved_role`, search/filters; `GET /api/admin/companies` returns `total`, `plan_id` / `plan_name` / `plan_is_*`, search.
|
||||
|
||||
---
|
||||
|
||||
## Source files
|
||||
|
||||
| File | Intent |
|
||||
|------|--------|
|
||||
| `apps/web/src/routes/admin/users/+page.svelte` | Orgs UI (users + companies tabs) |
|
||||
| `apps/web/src/lib/admin-orgs.ts` | Client helpers + types |
|
||||
| `apps/api/internal/httpapi/admin_orgs_handlers.go` | Paginated list handlers |
|
||||
| `apps/api/internal/httpapi/admin_staff_handlers.go` | Staff role PATCH (agent 6) |
|
||||
| `apps/web/src/lib/components/AdminNav.svelte` | Nav label “Users & orgs” |
|
||||
|
||||
---
|
||||
|
||||
## API contract
|
||||
|
||||
| Method | Path | Use |
|
||||
|--------|------|-----|
|
||||
| `GET` | `/api/admin/users?limit&offset&q&staff_only&active_only&inactive_only` | User directory |
|
||||
| `PATCH` | `/api/admin/users/{id}/staff-role` | Body `{ "staff_role": "admin"\|"developer"\|"support_staff"\|null }` |
|
||||
| `GET` | `/api/admin/companies?limit&offset&q&without_active_plan` | Companies + plan summary |
|
||||
| `GET` | `/api/admin/plans` | Plan picker |
|
||||
| `POST` | `/api/admin/plans/assign` | `{ company_id, plan_id }` |
|
||||
| `POST` | `/api/admin/emails/set-password` | Existing invite tooling |
|
||||
|
||||
All under `RequireSession` + `RequirePlatformAdmin` (full admin only).
|
||||
|
||||
---
|
||||
|
||||
## Security / performance
|
||||
|
||||
- AuthZ enforced server-side; UI gate is UX only.
|
||||
- Cannot change own staff role (agent 6 handler).
|
||||
- Lists use SQL `LIMIT`/`OFFSET` + `COUNT(*)` — no full-table client filter.
|
||||
- Company plan join is a single query (no N+1).
|
||||
|
||||
**ASSUMPTION:** `users.staff_role` migration (`029_staff_roles.sql`) applied. UI shows an info banner if PATCH returns 404/501.
|
||||
|
||||
---
|
||||
|
||||
## Manual verify
|
||||
|
||||
1. Platform admin → `/admin/users`.
|
||||
2. Search users; toggle Staff only; page Next/Previous.
|
||||
3. Assign staff role (admin / developer / support_staff); confirm badge.
|
||||
4. Companies tab → see plan badge; filter Without plan; Assign plan (legacy + public).
|
||||
5. As `support_staff`, confirm `/admin/users` is forbidden (full-admin-only nav).
|
||||
|
||||
---
|
||||
|
||||
## Coordination
|
||||
|
||||
- Agent 6: staff model + `PATCH …/staff-role` + `RequirePlatformAdmin` excluding support_staff.
|
||||
- Agent 9: billing assign UX remains; orgs page is the directory-focused assign path.
|
||||
- Agent 14: support_staff queue access after role grant from this UI.
|
||||
Reference in New Issue
Block a user