# 09 — Admin billing plans UI (agent 9/20) **Owns:** Admin → Billing → **Plans** tab (table, filters, badges), create/edit plan dialogs, assign-plan UX, companies-tab assign shortcuts. **Does not own:** Permissions tab / `PlanPermissionsPanel` (agent 8). **Entry:** `/admin/billing` (gate: `requirePlatformAdmin`). --- ## Delivered 1. **Plans table** — search + visibility filters (All / Public / Legacy / Custom) with counts. 2. **Badges** — Public (ladder Free–Enterprise), Legacy (A1, Basic, Professional, Mini, Merkur…), Custom (other client deals). Public + `is_custom` also shows “Custom package flag” (Enterprise). 3. **Create / edit** — polished dialog: name, description, monthly/yearly credits, max products, term, custom flag; preview of visibility kind. Wired to `POST /api/admin/plans` (upsert with `id` for edit). 4. **Assign plan** — dialog with company + plan selects, optional trial + trial credits; row actions from Plans and Companies. `POST /api/admin/plans/assign`. 5. **Link to permissions** — Plans row → Permissions tab with that plan selected (`PlanPermissionsPanel`). 6. **Companies polish** — search, has/no active plan filter, Assign plan + Add credits actions; summary card for companies without a plan. --- ## Source files | File | Intent | |------|--------| | `apps/web/src/routes/admin/billing/+page.svelte` | Host page: stats, tabs, dialogs, companies table | | `apps/web/src/lib/components/admin/AdminPlansPanel.svelte` | Plans table + filters + row actions | | `apps/web/src/lib/admin-billing-plans.ts` | Visibility classify/filter, upsert/assign API helpers | --- ## API contract (existing) | Method | Path | Use | |--------|------|-----| | `GET` | `/api/admin/plans` | List all plans (ladder + deals) | | `POST` | `/api/admin/plans` | Upsert plan (`id` optional); body: name, description, monthly_credits, yearly_credits, max_products, is_custom, term | | `POST` | `/api/admin/plans/assign` | `{ company_id, plan_id, is_trial?, trial_credits? }` | | `GET` | `/api/admin/companies` | Companies + `has_active_plan` | | `POST` | `/api/admin/credits` | Adjust balance | | `POST` | `/api/admin/billing/run-cycles` | Due renewals | --- ## Visibility rules (UI) | Kind | Rule | |------|------| | **Public** | Name ∈ Free, Starter, Growth, Business, Enterprise (`isDefaultPublicPlanName`) | | **Legacy** | Known migrated names: A1 (+ `a1 …` prefix), Basic, Professional, Mini, Merkur*, Meur, or name contains `legacy` | | **Custom** | Everything else (client deals / `is_custom` non-ladder) | ASSUMPTION: Backend may later expose an explicit `is_legacy` / profile field; until then UI classifies by name (aligned with `IsPublicProductPlan` + migrated deal set). --- ## Manual verify 1. Platform admin → `/admin/billing` → Plans. 2. Filter Public / Legacy / Custom; search by name. 3. Create a custom plan; edit credits/description; confirm row badges. 4. Assign plan to a company (from Plans row or Companies → Assign plan); optional trial. 5. Click Permissions on a plan → Permissions tab opens with that plan selected. 6. Companies filter “No active plan”; Add credits still works. --- ## Coordination - Agent 8 owns Permissions panel styling/profiles — do not rewrite `PlanPermissionsPanel`. - Agent 5/19 may add explicit legacy profile on plans; when present, prefer API flag over name heuristics (additive). - Agent 10 may deepen org/company management; this page only adds assign/credits filters needed for billing ops.