Dashboard Audit 2026-09
Full deep-dive audit of apps/dashboard + dashboard-facing Convex: 166 findings, 0 P0, sprint roadmap.
A read-only, full-scope audit completed 2026-09-03: architecture, implementation, cleanup, UI/UX, shadcn fit, bug hunting, Next.js 16, performance, Convex — plus page-by-page scorecards for all 76 routes, traces of all 16 user flows, and a live browser walkthrough. No code was changed during the audit.
Verdict
Zero P0s. No auth hole into sensitive data, no data-corruption path, tsc clean, and the backend's enforcement layer (requireDashboardAccess / requireAdminAccess + audit logs + tests) is a genuine strength. The real problem is honesty at the edges: the frontend discards the backend's careful denials, and the presentation layer's authorization story (gates, nav, comments) has drifted from what the backend actually enforces. Both are mechanical, low-risk fixes.
Tally: 166 findings
| Severity | Count | Reading |
|---|---|---|
| P0 | 0 | nothing actively breaking or exploitable found |
| P1 | 39 | fix in Sprint 0–1 (false-success pattern, auth drift, audit-log gaps, error boundaries, perf) |
| P2 | 76 | Sprint 1–2 (consolidation, states, consistency, backend cost) |
| P3 | 51 | backlog (polish, a11y batch, long tail) |
The nine themes
- False-success / false-empty (~35 sites) — the hook layer flattens every backend response into data, so a denial (
{success:false, message:"Admin access required"}) becomes a green success toast or a silent empty list. Ratings moderation, voucher revoke/extend/reissue, template edits, campaign create, product saves, notification queue — all lie today. One hook-contract change + a mechanical sweep fixes all of it. - Authorization presentation drift — ~37 pages have no client gate and several carry fictional "server-side permission check" comments; nav/gate/backend tiers disagree on orders and ratings; kbar advertises admin URLs to every role; two
getCampaignByIdqueries are public with zero auth (one leaks shake-to-win reward probabilities). - Audit-log gaps — three whole destructive admin families write zero
auditLogs: Shopify webhook admin (incl. delete-all), the entire ratings/moderation surface, gamificationLoyalty deletes. - Resilience — no
error.tsxat the/dashboardsegment: ~56 pages unwind to a white-pageglobal-error; ratings has no loading/empty/error states; malformed deep links crash. - Performance — 3–4 serial round trips before page data; the overview remounts all 11 subscriptions on every period toggle (live-confirmed); orders double-subscribes its own query; backend analytics collects every order + every profile twice per range while the aggregates built for it sit unread.
- shadcn/UI upkeep — the component base is the right one (explicit fit verdict); the debt is upkeep: stale
components.json, old-vintage button/card, 3ui/files importing app/Convex code, ~650 raw color utilities escaping the runtime theme system, and consolidation debt (5 delete dialogs, 3 table archetypes, 9 date pickers). - UX gaps — five destructive ops fire without confirmation; unsaved-changes guards in 3 of 12 forms; dead pagination on ratings and marketing campaigns; fabricated KPI trends on /loyalty.
- Cleanup — a repo-wide-verified safe-delete list: 10 unused ui components, dead
dal.ts("the REAL security boundary" with zero importers), 15 dead hook exports, a dead framer-motion + 1,203-line zustand-store chain, a committed.nextartifact, 5 removable deps. - Next.js 16 — error boundaries (above), Dockerfile expecting
output:'standalone'that's never set, broken title-template chain, CSP shippingunsafe-eval/unsafe-inline, Sentry PII + 100% tracing on an admin tool.
Live walkthrough confirmations
Driving the real app (dev deployment, super-admin session) confirmed: the overview period toggle blanks the whole page into skeletons; Orders renders Completed and Pending badges identically (no status color); /products/new is a stub page; the truncation notice "500 orders (1463 total before local filters)" is honest; page titles render without the site suffix. Nothing contradicted the static findings.
Where the detail lives
The per-lane evidence files were removed from the repo on 2026-09-05 after the fix program landed; git log -- apps/dashboard/docs/audit-2026-09 reaches them. They were one file per lane: lane-a-architecture … lane-j-flows, lane-k-live-walkthrough, plus FIX_TASKS.md (the handoff backlog). Main report: apps/dashboard/docs/dashboard-audit-2026-09.md (§7 synthesis). The next two pages of this section are the readable catalog and the fix backlog.