ProjectR Docs
Audit 2026-09

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

SeverityCountReading
P00nothing actively breaking or exploitable found
P139fix in Sprint 0–1 (false-success pattern, auth drift, audit-log gaps, error boundaries, perf)
P276Sprint 1–2 (consolidation, states, consistency, backend cost)
P351backlog (polish, a11y batch, long tail)

The nine themes

  1. 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.
  2. 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 getCampaignById queries are public with zero auth (one leaks shake-to-win reward probabilities).
  3. Audit-log gaps — three whole destructive admin families write zero auditLogs: Shopify webhook admin (incl. delete-all), the entire ratings/moderation surface, gamificationLoyalty deletes.
  4. Resilience — no error.tsx at the /dashboard segment: ~56 pages unwind to a white-page global-error; ratings has no loading/empty/error states; malformed deep links crash.
  5. 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.
  6. shadcn/UI upkeep — the component base is the right one (explicit fit verdict); the debt is upkeep: stale components.json, old-vintage button/card, 3 ui/ 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).
  7. 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.
  8. 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 .next artifact, 5 removable deps.
  9. Next.js 16 — error boundaries (above), Dockerfile expecting output:'standalone' that's never set, broken title-template chain, CSP shipping unsafe-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.

On this page