# UX research: Admin, roles/plans, support desk **Product:** Descrybe — product feed / AI catalog SaaS (Go API + SvelteKit admin). **Scope:** Clean ops panels, plan/role permission matrices, support queue with assignment + CSAT. **Date:** 2026-08-05 **Method:** Web research (admin tables, RBAC/entitlements, support SLA/CSAT) + codehelper skim of existing admin UI. --- ## Existing Descrybe surface (baseline) | Area | What exists today | Paths / symbols | | --- | --- | --- | | Admin chrome | Fixed left nav, dark shell (`.dark`), brand “Descrybe” + Admin pill | `AdminNav.svelte` | | Page frame | `PageShell` → title + description + actions; `max-w-7xl` | `PageShell.svelte` | | Tables | `TableShell` = bordered `bg-card` + scroll; row hover `muted/40` | `TableShell.svelte` | | Support queue | Status chip filters, search, Badge status map, click-through rows | `/admin/support`, `listAdminSupportTickets` | | Ticket model | status, priority, category, `assignee_admin_user_id`, message thread | `support/admin-api.ts` | | Plan features | Package select + section filter + search; checkbox matrix; package Badges | `PlanPermissionsPanel.svelte` | | Tokens | Majorelle primary, russian-violet ink, slate-friendly cards; semantic Badge variants | `layout.css` (`--primary` 247°, success/warning Badges) | **Implication:** Do not invent a second admin visual system. Extend `PageShell` + `Card` + `TableShell` + `Badge` + filter chips already used on Support. Prefer semantic tokens (`border-border`, `bg-primary/15`, `text-muted-foreground`) over one-off `slate-*` where the rest of admin already uses tokens (Support does; PlanPermissions still hardcodes some slate). --- ## A) Clean admin / ops panels ### Patterns to apply 1. **Job-first hierarchy (3 levels)** - L1: entity/queue purpose + critical alerts + primary actions (Refresh, Assign, Resolve). - L2: filters + operational table. - L3: audit / history / advanced settings on demand (drawer or sub-route). Source pattern: SaaS admin hierarchy guides (Taqwah, PyColors). 2. **Density as a tool, not a vibe** - Ops tables: comfortable default (~40–48px rows); optional compact for power users later. - Cap overview KPI cards at **3–5** (admin overview already leans summary + tool grid — keep that restraint). - Sticky table headers; freeze identity column (subject / company name) when horizontal scroll appears. 3. **Queue-shaped tables** Useful traits: strong status, workflow filters, clear row entry, stable column order. Weak traits: ambiguous state, decorative charts instead of work, actions buried in kebab menus for primary work. 4. **Filter UX** - Removable filter chips + result count (“12 of 240 · status open · priority high”). - Global search + a few workflow filters (status / assignee / plan tier / SLA risk) — not a filter drawer per column on day one. - Persist key filters in URL (`?status=open`) — Support already does this; extend to assignee/SLA. 5. **Layout zones for ticket / user detail** Entity header (state + plan + SLA) → primary thread/table → side sheet for assignment / meta → audit strip. Prefer split pane or side sheet over full-page form for assignment so the queue context stays visible. ### DO / DON'T — avoiding the “ugly admin” look (Descrybe-native) | DO | DON'T | | --- | --- | | Reuse `PageShell`, `Card`, `TableShell`, Lucide icons at `h-4`/`h-5` | New dashboard kits, glassmorphism, neon charts, multi-shadow cards | | Use majorelle **sparingly**: active nav, selected filter, primary CTA | Purple-to-indigo hero gradients, glow rings, “AI sparkle” chrome on every control | | Semantic status via `Badge` (`warning`/`success`/`secondary`) | Rainbow row backgrounds or emoji status columns | | One composition per page: title → filters → table | KPI strip + chart wall + three card grids above every queue | | Tabular nums + truncate with tooltip for IDs/emails | Wrapping every cell; monospace walls of UUID | | Destructive actions visually separate + confirm | Red “Delete” next to “Reply” without consequence copy | | Match admin dark shell tokens from `layout.css` | Flat gray Bootstrap admin or cream/serif “editorial” admin | Brand note: Descrybe’s primary *is* majorelle blue. “Not purple AI slop” means **don’t decorate** — use brand as selection/affordance, not as atmosphere. --- ## B) Plan / role permission matrices ### Model (separate two axes) Research consensus (Stripe-style entitlements + B2B RBAC UX): | Axis | Question | Descrybe mapping | | --- | --- | --- | | **Role** | What can this *person* do? | Platform admin vs company admin vs member; future scoped ops roles | | **Plan / entitlement** | What has this *account* bought? | Plans + `PlanPermissionsPanel` feature catalog + feature gates | | **Scope** | Where does it apply? | Company / catalog / feed — keep visible if multi-tenant ops act across companies | | **Lifecycle** | Trial, past_due, suspended, custom deal | Already surfaced in billing helpers (`billing-display`, package Badges) | **Never blur** “member can export” (role) with “Advanced export is on Enterprise” (plan). UI should explain *which* layer blocked an action. ### Matrix UX patterns 1. **Read matrix for audit; edit via focused builder** Full checkbox grids are good for platform ops (current Plan features panel). For end-customer role assignment, prefer named roles + plain-language summary, not a wall of toggles. 2. **Group by task domain** Sections already in `PLAN_FEATURE_SECTIONS` — keep section headers, global gate Badge (“Globally disabled”), and search. Add “affected companies / seats” count before bulk enable/disable. 3. **Honest gating states** Hide vs disable vs upgrade CTA vs “ask admin” — pick per feature. Support/billing should show *why* (plan vs role vs lifecycle). 4. **Role chips** Compact chips: plan name, `is_custom`, trial, platform-admin. Use `Badge variant="outline"` for ladder defaults and `secondary` for custom deals (already started in PlanPermissionsPanel). Avoid inventing 8 near-identical admin role names. 5. **Exceptions expire** Support overrides and custom deals need owner, reason, expiry, audit — otherwise Support becomes the policy engine. ### DO / DON'T — permissions UI | DO | DON'T | | --- | --- | | Label packages in customer language (Free / Pro / Enterprise / Custom deal) | Internal flags as the only label (`is_custom` alone) | | Show count enabled / total; section + search filters | Unfiltered 80-row checkbox dump with no scan path | | Confirm bulk “Disable all” with blast radius | Silent global section flips | | Log who changed plan features | Toggles with no success/error feedback (panel already alerts — keep it) | | Separate “AI roles” (provider keys) from user RBAC | Mixing `admin-ai-roles` (LLM slots) into the human permission matrix naming | --- ## C) Support desk: assignment, queues, SLA, CSAT ### Current hooks - Queue: status filters + search; columns subject/status/priority/company/requester/updated. - API update already allows `assignee_admin_user_id` / `clear_assignee`. - Priorities: `low | normal | high`. Categories: `billing | bug | account | other`. - Gap vs research: no SLA timers, no assignee column/filter, no CSAT fields, no “my queue” / unassigned views. ### Ticket queue patterns 1. **Ownership model** Default for B2B SaaS: route by **product area / category** + **customer plan tier**, then assign. Assignment modes to support: - Unassigned pool → claim - Round-robin / load-balance for general - Skill / category for billing vs bug - VIP / Enterprise bypass for high impact 2. **Queue views (filters, not separate apps)** - All open · Unassigned · Mine · SLA at risk · Waiting on customer (`pending`) Keep chip style from Support page (`border-primary bg-primary/15` when active). 3. **Row information hierarchy** Primary: subject + company. Secondary: status Badge, priority, assignee avatar/initials, last message age. Tertiary: category under subject (already). Add SLA countdown only when defined. 4. **SLA as timers + events** - Tier by priority (and optionally plan): first response + resolution targets. - States: on track → **at risk** (e.g. 80% elapsed) → breached. - Pause when waiting on customer. - UI: subtle text for on-track; `Badge variant="warning"` at risk; `destructive` only for breach — not red rows. 5. **Assignment UX** - Inline assignee control on detail header; optional bulk assign from queue. - Show workload (open count) in assignee picker. - Reassignment reason for misroutes (feeds quality metrics). ### CSAT patterns 1. **One rating question + optional comment** (1–5 or Good/Neutral/Bad). Extra questions kill response rate. 2. **Ask in-thread or embed in resolution email** — avoid “click this survey link” as primary path. 3. **Timing:** immediate on resolve for highest response; optional short delay (1–4h) if you need confirmation the fix stuck. Cap frequency (~1 survey / customer / 14–30 days). 4. **Closed loop:** rating ≤2 → auto follow-up task for lead/manager within 24h; do not only score the agent. 5. **Ops dashboard:** weekly CSAT trend, by category and by assignee — not a vanity gauge on every ticket list row (show star only when rated). ### DO / DON'T — support UX | DO | DON'T | | --- | --- | | Explicit assignee + unassigned queue | Tickets that only change status with no owner | | Concrete priority definitions (impact × urgency) | “High” used for every billing question | | Pair speed (FRT/SLA) with CSAT / reopen rate | Optimize SLA alone until quality collapses | | Internal notes distinct from customer replies | Agents accidentally sending notes to customers | | Macros that sound human; show company plan on ticket | Generic AI purple assist panel auto-replying without gate (`support` AI role stays off until product gate) | --- ## Concrete patterns checklist (build against this) | Pattern | Apply on | | --- | --- | | Density | Support + Users + Billing tables via shared table cell padding | | Hierarchy | PageShell title → chip filters → TableShell → detail | | Tables | Sticky header; identity first; Badge for state; truncate + title | | Filters | Chips + URL params + count; search left-icon Input | | Role / plan chips | Badge outline vs secondary; plan name humanized | | Ticket queues | Unassigned / Mine / At risk views; assignee column | | SLA | Timer + at-risk Badge; pause on `pending` | | Ratings | Post-resolve 1–5; optional comment; closed-loop on lows | --- ## Top 8 recommendations 1. **Standardize ops pages on one skeleton** — `PageShell` + filter chip bar + `TableShell` + empty/forbidden/unavailable states (copy Support; retire ad-hoc slate-only cards where tokens suffice). 2. **Ship assignee-centric queues next** — column + filter + “Unassigned / Mine”; wire existing `assignee_admin_user_id` before adding SLA or CSAT chrome. 3. **Keep plan entitlements and human roles as two panels** — extend `PlanPermissionsPanel` for packages; separate company/platform RBAC; never merge with `admin-ai-roles` LLM slots in the same matrix. 4. **Permission matrix = grouped sections + search + blast-radius confirms** — preserve section gates and package Badges; add “who is affected” on enable/disable all. 5. **Add SLA as a thin operational layer** — first-response + resolution by priority (optionally × plan tier); show at-risk/breach Badges; pause on `pending`. 6. **CSAT: one click in the resolution path** — 1–5 (or 3-point) in ticket UI / resolution email; optional comment on low scores only; frequency cap; manager follow-up automation for ≤2. 7. **Visual discipline = brand as signal, not decoration** — majorelle for selection/CTA only; semantic Badges for state; no gradient dashboards, glow, or decorative KPI walls. 8. **Measure ops health with a short metric set** — FRT, SLA at-risk/breach, misroute/reassign rate, reopen rate, CSAT weekly by category — surface on Support overview, not as chart spam on the queue. --- ## Sources (web) - [SaaS Admin Panel Design — hierarchy & density](https://taqwah.agency/blog/saas-admin-panel-design-guide) - [Admin tables & queues](https://pycolors.io/guides/saas-admin-panels) - [Data table density, chips, sticky headers](https://www.setproduct.com/blog/data-table-ui-design) - [Permission matrix as product surface (roles vs entitlements)](https://0r8it.com/blog/the-permission-matrix-is-a-product-surface) - [Multi-role B2B SaaS — role / plan / scope](https://dardesign.io/blog/multi-role-b2b-saas-ux-roles-permissions-flows) - [Support triage, routing, SLA events](https://thinkbot.agency/blog/support-ticket-automation-playbook-triage-routing-slas-knowledge-qa) - [Queue strategy & SLA milestones](https://www.supportbench.com/support-queue-strategy-triage-routing-ownership/) - [CSAT timing, one-question surveys, closed loop](https://supp.support/blog/how-to-set-up-csat-surveys) --- ## Open follow-ups (out of scope for this note) - Exact SLA targets (minutes/hours) per priority × plan — needs ops decision. - Whether CSAT is in-app only, email-embedded, or both. - Company-side roles matrix vs platform-admin-only for v1.