GenerateSaaS

Dashboard & settings

Build the authenticated app surface with gated settings tabs and an admin panel.

The dashboard is the authenticated surface behind sign-in - pages in apps/web-nuxt styled by @repo/styles, with data served by @repo/api. Its sub-tabs come from sectionTabsConfig (packages/config/src/section-tabs.ts) and are gated by feature flags, the user's roles, and their orgRoles.

The dashboard shell

Entry points live in config.routes - dashboard (/dashboard), settings (/settings/profile), admin (/admin/users), notifications (/notifications). Link via these keys, never hardcoded paths.

The dashboard hydrates from a single SSR call to GET /dashboard/status (packages/api/src/routes/internal/dashboard.ts):

// SSR-first: fetch the summary + session before render
const { data: session } = await authClient.useSession(ssrFetch);
const { data } = await useAsyncData("dashboard-status", async () => {
  const res = await api.dashboard.status.$get({});
  if (!res.ok) return null;
  return res.json();
});

The status payload (GET /dashboard/status) returns:

FieldContents
PlanplanName, planId, planType (subscription | lifetime | free | null), planExpiresAt, isActive
securitytwoFactorEnabled, emailVerified, activeSessionCount, passkeyCount
teammemberCount, pendingInviteCount, userRole - null unless config.tenancy.multiTenant is on
  • The endpoint returns 400 when config.tenancy.billingScope is not "user" and no organization is active. The default billingScope is "user".
  • Tab visibility is resolved client-side from sectionTabsConfig, not from the status payload.
  • Disabled features and ungranted tabs are never rendered - no flash, no dead links.

Settings tabs

Settings is a tabbed surface (sectionTabsConfig.settings). Each tab is shown only when its gate passes:

TabGateNotes
Profilealways/settings/profile - name, avatar, email
Billingalways/settings/billing - sections render only when config.payment.enabled
Securityalways/settings/security - password, sessions, 2FA, passkeys
Developersrequires: ["apiKeys"]/settings/developers - API key management; hidden when off

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

When config.tenancy.multiTenant is true (the default), an Organization group (sectionTabsConfig.organization) adds an Overview tab (/settings/organization) plus an Activity tab (/settings/organization-activity) gated by orgRoles: ["owner", "admin"]. With multiTenant off, the org pages redirect to config.routes.loginRedirect.

Admin panel

The admin role gate lives on the route and the sidebar category, not on the section tabs. Each admin page declares definePageMeta({ middleware: "auth", roles: ["admin"] }); the auth middleware redirects non-admins to config.routes.loginRedirect. The admin sidebar category in app/config/sidebar.ts also carries roles: ["admin"], so the whole group is hidden for everyone else.

The tabs themselves (sectionTabsConfig.admin) are only feature-flag-gated - they have no roles field, because the route guard already blocks non-admins:

TabURLGate
Users/admin/usersnone
Organizations/admin/organizationsrequires: ["multiTenant"]
Audit logs/admin/audit-logsnone
Billing logs/admin/billing-logsnone
API keys/admin/api-keysrequires: ["apiKeys"]
Announcements/admin/announcementsnone

Roles and feature flags are enforced server-side at the API layer - see Authorization. The page-level roles guard is client navigation; the API independently rejects non-admins.

Adding a settings tab

Create the page in apps/web-nuxt/app/pages/settings/ with definePageMeta({ layout: "dashboard", middleware: "auth" }).
Add an entry to sectionTabsConfig.settings with a title i18n key, url, and any requires / roles / orgRoles gate.
Link it with localePath() so non-default locales resolve.
Fetch its data SSR-first with await useAsyncData(...).

Frequently asked questions

Why is the Developers tab missing? Its entry carries requires: ["apiKeys"]. Enable the apiKeys feature to expose API key management under settings.

Why does Billing show but have no plans? The Billing tab is always present, but its sections gate on config.payment.enabled. Enable payments to populate plan, subscription, and credits sections.

How do I show the admin panel to a user? Grant that user the admin role. The admin sidebar category and each admin route carry roles: ["admin"], and the API rejects non-admins server-side.

Where do tab titles come from? They are i18n keys (e.g. sidebar.profile) rendered with $t(), so labels translate per locale.

On this page