Files
keysat/licensing-service/web
Grant fb062d5ca5 Tier upgrades Phase 5 — admin UI: tier_rank input + Change-tier modal
Closes the operator surface for tier upgrades. With this in,
operators have a complete UI for managing the upgrade ladder
without ever needing the curl-the-API path.

Policy editor (create + edit forms):
- New "Tier ladder rank (optional)" number input alongside the
  recurring section. Operators set "0" for free, "1" for
  standard, "2" for pro, etc. Empty input = "not in any ladder"
  (server stores NULL; that policy is excluded from buyer-facing
  upgrade flows but admin can still force-change to/from it).
- Edit-form behavior: empty input clears tier_rank to NULL.
  Filled input sets to that value. The PATCH always sends the
  field (using the nullable-patch shape Some(Option<i64>)) so
  the operator's intent — clear or set — actually lands.
- Range 0–1000 enforced server-side; clipped client-side too.

Licenses page:
- New "Change tier" button on every non-revoked license row,
  to the left of Suspend/Unsuspend/Revoke.
- Opens a modal that:
    * Loads all policies for the license's product
    * Shows them in a dropdown with metadata (rank · cadence ·
      trial flags) so the operator can see the ladder shape
    * Offers a "Apply as comp (skip_payment=true — no invoice,
      flips immediately)" checkbox + an audit-reason field
    * On submit, POSTs to the new admin endpoint:
      - skip_payment=true → "Applied" status, modal closes
      - skip_payment=false → renders the checkout URL the
        operator forwards to the buyer through whatever channel
        they use (the design-doc-spec'd "operator delivers the
        URL" flow)
- The modal deliberately doesn't show a quote preview before
  submit (the buyer-quote endpoint requires the buyer's signed
  license key, which the admin doesn't have). Server-side
  response carries the actual numbers when the operator commits.
  Future polish: a separate admin-mode quote endpoint could
  render the preview pre-submit.

Tests unchanged (77 still passing) — pure UI commit, no Rust
changes. The behavior the UI drives is fully covered by the
api.rs admin_change_tier_* tests added in c5d716a.
2026-05-08 20:15:23 -05:00
..