# 15 — User-facing Support Center UI **Agent:** 15/20 **Date:** 2026-08-05 **Scope:** Customer `/support` surfaces — list, create, thread, CSAT rating. **Contract:** [11-support-design.md](./11-support-design.md) §5 / §9.1 (`POST /api/support/tickets/{id}/csat`). --- ## Outcome Polished, accessible user Support Center that: 1. Lists the caller’s tickets with status chips + URL `?status=` persistence. 2. Creates tickets (subject, category, priority, body) when `support.ticket_create` allows. 3. Shows a threaded conversation with quiet polling (visibility-aware). 4. Prompts CSAT (1–5 + optional comment) when status is `resolved` or `closed` and no rating yet. 5. Respects plan feature keys via existing `PlanRouteGuard` + `FeatureGate` / `planCapabilities.can`. --- ## Surfaces | Route | Feature key | Notes | |-------|-------------|-------| | `/support` | `support.center` | Status filter chips, clickable rows, count | | `/support/new` | `support.ticket_create` | Priority + char counters; upgrade panel if denied | | `/support/[ticketId]` | `support.ticket_thread` | Thread + CSAT banner/form | Legacy plans: if the matrix turns `support.*` off (see `03-roles-matrix`), `PlanRouteGuard` shows the upgrade panel. When keys are absent (pre-cutover), `CORE_ALWAYS_ON` keeps basic support available. CSAT is **not** a separate feature key — it rides on `support.ticket_thread` once the ticket is resolved/closed (design §5). --- ## Client API | Helper | Path | |--------|------| | `listSupportTickets` / `getSupportTicket` / `createSupportTicket` / `replySupportTicket` | `apps/web/src/lib/support/api.ts` | | `submitSupportCsat` → `POST …/csat` `{ score, comment? }` | same | | `canRateTicket` / `canReplyToTicket` | same | | Types `SupportCsat`, `SubmitSupportCsatInput` | `apps/web/src/lib/support/types.ts` | | UI | `SupportTicketRating.svelte` | **Graceful backend lag:** if agent 13’s CSAT route is not mounted yet, `isSupportUnavailable` / 404–503 surfaces a clear message without breaking the thread. Duplicate ratings (`409`) show “already rated”. **ASSUMPTION:** Response may be a full ticket (with `csat`) or a CSAT object; UI merges `csat` onto the in-memory ticket either way. --- ## UX / a11y / perf - Status filters: `aria-pressed` chip group (matches admin queue pattern). - Rows: keyboard activatable (`Enter` / `Space`), `role="link"`. - CSAT: `role="radiogroup"` star buttons with explicit `aria-label`s; comment encouraged for scores ≤2. - Thread: `role="log"` + `aria-live="polite"`; capped scroll region; auto-scroll on new messages. - Polling: 20s interval skipped when `document.visibilityState === "hidden"`; refresh on focus. - No extra cards in hero chrome — reuse `PageShell` + existing tokens. --- ## Files touched - `apps/web/src/lib/support/types.ts` - `apps/web/src/lib/support/api.ts` - `apps/web/src/lib/components/SupportTicketRating.svelte` (new) - `apps/web/src/routes/support/+page.svelte` - `apps/web/src/routes/support/new/+page.svelte` - `apps/web/src/routes/support/[ticketId]/+page.svelte` - `apps/web/src/lib/plan-capabilities.ts` (upgrade titles for create/thread) --- ## Verification ```bash cd apps/web && npm run check ``` Manual: 1. `/support` — filter chips update URL; empty + list states. 2. `/support/new` — create with priority; gated plan shows upgrade. 3. Resolved ticket — CSAT form appears; after submit, “Your rating” card; reply still reopens when allowed. 4. Legacy matrix with `support.center=false` — route guard upgrade panel. **Depends on:** agent 13 mounting `POST /api/support/tickets/{id}/csat` and optional `csat` on GET. UI is ready ahead of that mount. --- ## Out of scope (other agents) - Schema / CSAT backend (12–13) - Staff assign / claim UI (14) - Admin CSAT aggregates