ZeroStarter

Dashboard

Protected dashboard with authentication, organization management, and sidebar navigation.

Overview

The dashboard is a protected area at /dashboard that requires authentication. It includes organization switching, user management, and a collapsible sidebar. The layout is defined in web/next/src/app/(protected)/layout.tsx.

Authentication Guard

The protected layout checks for a valid session server-side:

const session = await auth.api.getSession()
if (!session?.user) redirect("/")

Unauthenticated users are redirected to the home page. The auth.api.getSession() helper in web/next/src/lib/auth/index.ts makes a server-side call to the API using the request cookies.

Organization Persistence

When a user logs in, the dashboard restores their last active organization:

  1. On load, checks for last-active-org_{userId} cookie
  2. If found, calls POST /api/auth/organization/set-active to restore the org
  3. The org switcher component saves the cookie when the user switches organizations

This ensures users return to the same organization context across sessions.

Organization Switcher

Located at web/next/src/components/sidebar/dashboard/org-switcher.tsx:

  • Lists all organizations the user belongs to
  • Allows switching between organizations
  • Create Organization dialog with a name field (the slug is auto-generated from the name)
  • Saves last active org in a cookie for persistence

User Actions

Located at web/next/src/components/sidebar/dashboard/user-actions.tsx:

  • Links to documentation, with a version badge
  • Renders the shared SidebarUserMenu

The user avatar, name, and email, the optional feedback link (shown when NEXT_PUBLIC_USERJOT_URL is set), and the sign-out action all live in the shared SidebarUserMenu component at web/next/src/components/sidebar/user-menu.tsx. Sign-out redirects to the home page.

Adding Dashboard Pages

  1. Create a new page under web/next/src/app/(protected)/:
web/next/src/app/(protected)/
├── layout.tsx          ← Auth guard + sidebar
├── dashboard/
│   └── page.tsx        ← Main dashboard (currently empty)
└── settings/           ← illustrative example, not in the repo
    └── page.tsx        ← New page example
  1. The page automatically inherits the authenticated layout with sidebar.

  2. Access session data in your page:

import { auth } from "@/lib/auth"

export default async function SettingsPage() {
  const session = await auth.api.getSession()
  return <div>Welcome, {session?.user.name}</div>
}

The sidebar chrome is not built inline in the layout. It lives in the shared SidebarShell component at web/next/src/components/sidebar/shell.tsx, which is reused by both the dashboard and console layouts. The dashboard layout passes in header and footer; SidebarShell owns the provider, header brand, content, footer, and rail. (The optional badge prop is used by the console layout, not the dashboard.)

The shell uses the shadcn/ui Sidebar component with collapsible="icon" mode. It consists of:

  • Header: brand (site.name, optionally with the badge), the sidebar trigger, and the caller's header node (for the dashboard, the org switcher)
  • Content: the caller's nav node (empty for the dashboard; add navigation items here)
  • Footer: the caller's footer node (for the dashboard, the user actions)

A SidebarRail sits alongside the sidebar for drag-to-resize/toggle, and a floating trigger (SidebarFloatingTrigger) is rendered in the main content area.

The sidebar state (open/closed) is persisted in a sidebar_state cookie.