The boilerplate includes two admin dashboards: a platform-wide super admin and an organization-level admin for B2B workspaces.

Platform Admin Dashboard

Located at /admin-dashboard, accessible only to users with is_admin=true in their profile.

Super Admin Dashboard

Platform admin dashboard with KPIs and overview

Organizations Management

Organizations management

Organization Detail

Organization detail view

Subscriptions Management

Subscriptions overview

AI Analytics

Platform-wide AI analytics

Platform Settings

Platform settings configuration

Auto-Admin Assignment

The admin check uses the shared maybeAutoPromoteAdmin() helper so layout and API admin verification follow the same auto-fix path:

  • Compares user email with admin_email in app_settings table
  • If email matches but is_admin=false, automatically updates to is_admin=true
  • Works in both layout (server-side) and API routes without duplicating the update/audit logic
  • Uses supabaseAdmin (service role) to bypass RLS
Implementation boundary

Admin dashboard pages are thin Server Components. Platform-wide reads for the overview, analytics, and settings pages live in core/admin/overview.ts, core/admin/analytics.ts, and core/admin/settings.ts. Destructive admin routes delegate to focused core action modules such as core/affiliates/admin-actions.ts, which keeps audit logging and cache revalidation reusable outside the HTTP route.

Dashboard Pages

Page Path Features
Overview /admin-dashboard Total users, orgs, active subscriptions, MRR/ARR, AI stats
Analytics /admin-dashboard/analytics AI requests (total/30d/7d/today), tokens, costs, charts, top users
Users /admin-dashboard/users User management, disable/enable, view details
Organizations /admin-dashboard/organizations Workspace management, member limits, subscription status
Subscriptions /admin-dashboard/subscriptions All subscriptions, plan breakdown, status filters
Referrals /admin-dashboard/referrals Referral monitoring, stats, manual reverse/reject (gated by REFERRAL_ENABLED)
Affiliates /admin-dashboard/affiliates Affiliate applications queue, conversions, manual reversal (gated by AFFILIATES_ENABLED)
Logs /admin-dashboard/logs Server-side error logs, cursor-paginated, category/level filters (gated by LOGS_ENABLED)
Licenses /admin-dashboard/licenses License management, revenue, expiration tracking, revoke/extend actions
Changelog /admin-dashboard/changelog Create, edit, publish/unpublish changelog entries. Multi-locale support. Toggle via appConfig.features.changelog
Roles /admin-dashboard/roles Dynamic role management, permissions, colors, icons
CMS /admin-dashboard/cms Pages, blocks, media library management
Jobs /admin-dashboard/jobs Background jobs, handlers, run history
Settings /admin-dashboard/settings Platform settings, cache management, environment status, database stats, log purging

KPI Performance

Dashboard queries are optimized for performance:

  • Server Component pages render pre-aggregated data from core/admin/* modules instead of inlining service-role query orchestration
  • Parallel database queries with Promise.all (9 queries in parallel)
  • MRR/ARR calculation from pricingConfig (code-based, not DB)
  • Efficient counting with count: 'exact', head: true
  • Explicit projections such as select('id') or select('amount') instead of select('*') for production queries
  • supabaseAdmin bypasses RLS for platform-wide stats

Admin Dashboard Design System

The admin dashboard follows a Data-Dense Dashboard design pattern optimized for information density and at-a-glance insights. The design uses shadcn/ui components with Tailwind CSS v4 theme tokens.

Do not hand-roll KPI cards or page titles

The first two rows below describe shared components, not classes to copy. StatCard and PageHeader (components/patterns/) exist precisely because these blocks were hand-rolled dozens of times and drifted. Copying the class strings recreates the drift — import the component instead.

Element Pattern Example Classes
Stat/KPI Cards Always <StatCard>. Its surface prop says what the card sits ON: canvas (default — admin and org page shells) lifts; panel recesses, required inside the private/org floating panel where a bg-card card on a bg-card panel is invisible. canvas: border bg-card hover:shadow-card
panel: border-0 bg-muted/60 hover:bg-muted
Card Icons Tinted chip, accent restricted to the chart ramp (chart-1chart-5). Not primary (a KPI row would read as one undifferentiated block) and not success/warning (that implies a status the metric does not have). Never a raw palette colourblue-500 does not follow the theme and survives no rebrand. h-8 w-8 rounded-lg bg-chart-1/10 text-chart-1
KPI Values Rendered by StatCard. Every numeral in a dashboard uses tabular-nums — proportional digits make a column jitter as values update, which is one of the clearest tells of unconsidered dashboard work. text-2xl font-semibold tabular-nums
Supporting stats Small muted line under the value (hint prop) mt-1 text-xs text-muted-foreground
Table Containers Clean card without colored accent bg-card border border-border
Table Headers Subtle muted background for scanning TableHeader className="bg-muted/50"
Table Rows Hover highlight for interactivity hover:bg-muted/50 transition-colors
Page Headers Always <PageHeader> (title + optional description + action row). It owns the page’s only <h1>; no gradients. text-3xl font-bold tracking-tight text-foreground
Sidebar Active Item One shared <SidebarNavLink> with a variant per surface — the three active states differ on purpose (identity, not drift), and sharing the component is what keeps the collapsed-rail aria-label from going missing on one of them. admin: bg-primary/10 text-primary border-l-2 border-l-primary
org: bg-primary text-primary-foreground
private: bg-card text-foreground shadow-raise
Dashboard shell Rail + app bar + scrolling content panel. The bar (h-16) owns the mobile drawer trigger, the surface identity, and then notifications / theme toggle / account dropdown. The panel is full-bleed on mobile and insets into a rounded, raised sheet once the rail appears. <main>: min-h-0 flex-1 overflow-y-auto bg-card lg:my-3 lg:mr-3 lg:rounded-xl lg:shadow-card
Separation By elevation and surface, not by borders. The page canvas is deliberately recessed below --card, so a panel is told apart from the page by depth. Rails draw no internal rules. The one deliberate exception is a data-table row rule, which carries scanability. --background vs --card = 1.195 (light) / 1.136 (dark)
Design Skills Workflow

When designing pages, Claude Code follows a 4-tier skill pipeline: (1) Foundation skills (/tailwind-v4-shadcn + /ui-ux-pro-max, plus /shadcn for component work) are always invoked, (2) ONE aesthetic skill sets the visual direction (e.g., /impeccable), (3) Refinement commands (/impeccable typeset, /impeccable animate, etc.) polish specific aspects, (4) Workflow commands (/impeccable critique, /impeccable polish) handle review and shipping. Since Impeccable v4 the refinement and workflow steps are subcommands of /impeccable, not standalone skills. All colors must use oklch() theme tokens — never raw hex. See .claude/rules/design-skills.md for the full orchestration guide.

Organization Admin Dashboard

Located at /org-dashboard, accessible to workspace owners and admins.

Organization Dashboard

Organization dashboard overview

Organization Members

Team members management

Member Detail

Member detail with usage stats

AI Analytics

AI usage analytics

Page Path Features
Admin /org-dashboard/admin Workspace admin overview / management
Members /org-dashboard/members Team list with pagination, invite form, pending invitations
Member Detail /org-dashboard/members/[id] Usage stats, permissions, AI logs, access logs with pagination
Roles /org-dashboard/members/roles Role overview, permission matrix
Billing /org-dashboard/billing Credits balance, credit history, Stripe portal
Settings /org-dashboard/settings Org name/slug, danger zone (deletion)
API Keys /org-dashboard/api-keys B2B API key management (create/revoke, scoped)
Analytics /org-dashboard/analytics Workspace AI usage analytics

Pagination

Tables include reusable pagination controls:

  • Rows per page selector (5, 10, 20, 50)
  • Page navigation (first, previous, next, last)
  • "Showing X to Y of Z entries" display
  • Page indicator (Page X of Y)
  • Independent pagination state per table

Shared Dashboard Context

The private and organization dashboards share request-level context helpers in core/accounts/dashboard-context.ts. They use React.cache() to deduplicate repeated auth/account reads between a layout and its child page during one request.

Helper Route family Context
getPrivateDashboardContext() /private-dashboard/* Current user, profile, memberships, pending invitation token, active account, billing access, credits, trial state, admin/org-management flags.
getOrgDashboardContext() /org-dashboard/* Current user, profile, manageable workspaces, current workspace, member count, active subscription, billing access, trial/Stripe state.

These helpers are not static caches. They read the current request session with getUser(), so they remain appropriate only for protected dynamic routes. Layouts and pages still own redirects: unauthenticated users go to login, users without org-manager membership go back to /private-dashboard, and B2B workspaces without access go to pricing.

Organization Dashboard Performance

Organization dashboard analytics and member usage stats are pre-aggregated in SQL instead of loading raw usage rows into the page component. Keep this boundary when adding new dashboard cards:

  • core/accounts/dashboard-usage.ts wraps the aggregate readers used by the private dashboard overview, org overview, org analytics, member list, and member detail pages.
  • get_org_dashboard_analytics returns 30-day KPIs, previous-period comparisons, daily usage, top members, model/agent breakdowns, active users, latency, and success rate for /org-dashboard/analytics.
  • get_org_member_usage_summary returns per-member request count, token total, cost, last AI activity, and chat-session count for member list/detail screens.
  • get_account_dashboard_usage_summary powers lightweight overview counters without downloading ai_requests or chat_sessions.
  • Supporting indexes cover ai_requests(account_id, user_id, created_at desc), chat_sessions(account_id, user_id, created_at desc), and user_access_logs(user_id, created_at desc).
Avoid usage-row waterfalls

Do not add dashboard code that fetches all ai_requests or chat_sessions for an account and then groups them in JavaScript. Use an aggregate RPC, a bounded latest-activity query, or a head: true count query depending on the UI.