Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
3.7 KiB
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 §5 / §9.1 (POST /api/support/tickets/{id}/csat).
Outcome
Polished, accessible user Support Center that:
- Lists the caller’s tickets with status chips + URL
?status=persistence. - Creates tickets (subject, category, priority, body) when
support.ticket_createallows. - Shows a threaded conversation with quiet polling (visibility-aware).
- Prompts CSAT (1–5 + optional comment) when status is
resolvedorclosedand no rating yet. - 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-pressedchip group (matches admin queue pattern). - Rows: keyboard activatable (
Enter/Space),role="link". - CSAT:
role="radiogroup"star buttons with explicitaria-labels; 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.tsapps/web/src/lib/support/api.tsapps/web/src/lib/components/SupportTicketRating.svelte(new)apps/web/src/routes/support/+page.svelteapps/web/src/routes/support/new/+page.svelteapps/web/src/routes/support/[ticketId]/+page.svelteapps/web/src/lib/plan-capabilities.ts(upgrade titles for create/thread)
Verification
cd apps/web && npm run check
Manual:
/support— filter chips update URL; empty + list states./support/new— create with priority; gated plan shows upgrade.- Resolved ticket — CSAT form appears; after submit, “Your rating” card; reply still reopens when allowed.
- 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