Files
descrybe/docs/admin-roles-support/15-user-support-ui.md
T
greeneclipse 8580c996c3 Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
2026-08-09 22:47:43 +02:00

3.7 KiB
Raw Blame 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 §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
submitSupportCsatPOST …/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-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.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

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