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:
| Field | Contents |
|---|---|
| Plan | planName, planId, planType (subscription | lifetime | free | null), planExpiresAt, isActive |
security | twoFactorEnabled, emailVerified, activeSessionCount, passkeyCount |
team | memberCount, pendingInviteCount, userRole - null unless config.tenancy.multiTenant is on |
- The endpoint returns
400whenconfig.tenancy.billingScopeis not"user"and no organization is active. The defaultbillingScopeis"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:
| Tab | Gate | Notes |
|---|---|---|
| Profile | always | /settings/profile - name, avatar, email |
| Billing | always | /settings/billing - sections render only when config.payment.enabled |
| Security | always | /settings/security - password, sessions, 2FA, passkeys |
| Developers | requires: ["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:
| Tab | URL | Gate |
|---|---|---|
| Users | /admin/users | none |
| Organizations | /admin/organizations | requires: ["multiTenant"] |
| Audit logs | /admin/audit-logs | none |
| Billing logs | /admin/billing-logs | none |
| API keys | /admin/api-keys | requires: ["apiKeys"] |
| Announcements | /admin/announcements | none |
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
apps/web-nuxt/app/pages/settings/ with definePageMeta({ layout: "dashboard", middleware: "auth" }).sectionTabsConfig.settings with a title i18n key, url, and any requires / roles / orgRoles gate.localePath() so non-default locales resolve.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.