GenerateSaaS

Dashboard and Settings

Navigate the authenticated dashboard surface and its flag-gated settings and admin tabs.

The dashboard is the authenticated surface at config.routes.dashboard (/dashboard), a Server Component that fetches GET /dashboard/status (packages/api/src/routes/internal/dashboard.ts) once on the server. Its tabbed sub-surfaces come from the shared sectionTabsConfig in @repo/config (packages/config/src/section-tabs.ts), where each tab declares optional requires (feature flags) and orgRoles gates so anything the viewer or active config can't access hides automatically.

Dashboard surface

The page renders from one server-side fetch - no client loading spinner. The metric row, security/team grid, usage chart, and quick-actions/recent-activity grid all hydrate from the single /dashboard/status payload.

Metric cardSource field
Current planplanName (falls back to free-plan label)
Plan statusplanType + isActive (free / lifetime / active / inactive)
Security scoresecurity flags, scored 0-2
Team members (multi-tenant) or Member since (single-tenant)team.memberCount or organizationCreatedAt

Dashboard status payload

GET /dashboard/status (auth-guarded) returns plan, security, and team stats (multi-tenant only).

// GET /dashboard/status - packages/api/src/routes/internal/dashboard.ts
{
  planName, planId, planType, planExpiresAt, isActive,
  organizationCreatedAt, // null when single-tenant / no active org
  security: { twoFactorEnabled, emailVerified, activeSessionCount, passkeyCount },
  team: config.tenancy.multiTenant
    ? { memberCount, pendingInviteCount, userRole }
    : null, // null when single-tenant
}

planType is "free" | "lifetime" | "subscription" | null (derived from the resolved billing plan). When config.tenancy.billingScope is not "user", the endpoint returns 400 No active organization if the session has no active org.

Settings tabs

sectionTabsConfig.settings drives the /settings sub-tabs. Profile, Billing, and Security are always present; Developers is flag-gated.

TabRouteGate
Profile/settings/profilealways on
Billing/settings/billingtab always present; sections gated by config.payment.enabled
Security/settings/securityalways on
Developers/settings/developersrequires: ["apiKeys"]

The Billing tab always renders, but its page sections only appear when config.payment.enabled is true (default true). With payments off, the tab shows nothing actionable.

When config.tenancy.multiTenant is true, an Organization group (sectionTabsConfig.organization) is added:

  • Overview: /settings/organization, match: "exact".
  • Activity: /settings/organization-activity, gated orgRoles: ["owner", "admin"].

Admin panel

The admin panel lives under the (dashboard)/admin route group. Access is enforced server-side in admin/layout.tsx: any session whose user.role !== "admin" is redirected to config.routes.loginRedirect (/dashboard). The sidebar link is separately hidden by roles: ["admin"] in the per-app config/sidebar.ts, so non-admins never see the entry.

Its tabs come from sectionTabsConfig.admin. The whole panel is role-gated by the layout, so individual tabs carry no roles gate - only requires feature flags hide a tab from admins:

TabRouteTab gate
Users/admin/usersnone (panel role-gated)
Organizations/admin/organizationsrequires: ["multiTenant"]
Audit logs/admin/audit-logsnone (panel role-gated)
Billing logs/admin/billing-logsnone (panel role-gated)
API keys/admin/api-keysrequires: ["apiKeys"]
Announcements/admin/announcementsnone (panel role-gated)

config.routes.admin is /admin/users (the default landing route), not /admin. /admin itself redirects to config.routes.admin, preserving any query string.

Frequently asked questions

Why does the Billing tab show but Developers does not? Billing is always present so users can view their plan, while Developers carries requires: ["apiKeys"]. When the apiKeys flag is off, the Developers tab is removed from settings.

How do I get the Organization tabs? Enable config.tenancy.multiTenant. That adds the Organization group (Overview + Activity) to settings and the Organizations tab to the admin panel.

Who can see the admin panel? Only users with user.role === "admin". admin/layout.tsx redirects everyone else to config.routes.loginRedirect, and the sidebar link is hidden via roles: ["admin"] in config/sidebar.ts - see authorization for how roles are assigned and checked.

On this page