Files
descrybe/docs/admin-roles-support/15-user-support-ui.md
T

97 lines
3.7 KiB
Markdown
Raw Normal View History

# 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 callers 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 (15 + 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 13s CSAT route is not mounted yet, `isSupportUnavailable` / 404503 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 (1213)
- Staff assign / claim UI (14)
- Admin CSAT aggregates