'use client'; import { useEffect, useState, type ReactNode } from 'react'; import { useAuth, buildCentralAuthUrl } from '@startsimpli/auth'; import { LogOut, User } from 'lucide-react'; import { Sidebar, SidebarLayout, type SidebarSection } from './sidebar'; import { Button } from '../ui/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from '../ui/dropdown-menu'; /** * The one shared authenticated app shell — Sidebar SaaS chrome for EVERY app. * * Previously each app hand-rolled the same assembly in its own layout: the * central-auth gate (bounce to sign-in when there's no session), the loading * state, the account menu (email + Log out), and composing * + . That's the duplication — it now lives here once. * An app supplies only its slug + brand + nav sections: * * {children} * * `useAuth` / `buildCentralAuthUrl` come from the @startsimpli/auth peer dep, so * no app wires auth itself. The visual is the shared (dark, collapsible), * identical across apps. */ export interface AppShellProps { /** App slug for the central-auth return flow, e.g. 'market' | 'foundry'. */ app: string; /** Brand / product name shown at the top of the sidebar. */ appName: string; /** Sidebar navigation sections (label + href + optional icon). */ sections: SidebarSection[]; /** Extra items rendered above "Log out" in the account menu (optional). */ accountMenuExtra?: ReactNode; /** Loading label while the session bootstraps (default "Loading…"). */ loadingLabel?: string; children: ReactNode; } export function AppShell({ app, appName, sections, accountMenuExtra, loadingLabel = 'Loading…', children, }: AppShellProps) { const { user, isLoading, logout } = useAuth(); // Owned here so the content spacer shrinks with the rail — the Sidebar used to // keep this private, so "Collapse" narrowed the rail and left a dead 16rem gap. const [collapsed, setCollapsed] = useState(false); const signin = (returnTo: string) => buildCentralAuthUrl('signin', { app, returnTo }); // Gate: no session once bootstrap settles -> bounce to central auth, // preserving the current URL so the user returns here post-signin. useEffect(() => { if (isLoading || user) return; if (typeof window === 'undefined') return; window.location.href = signin(window.location.href); // eslint-disable-next-line react-hooks/exhaustive-deps }, [user, isLoading, app]); const handleLogout = async () => { try { await logout(); } finally { if (typeof window !== 'undefined') { window.location.href = signin(`${window.location.origin}/`); } } }; if (isLoading || !user) { return (
{loadingLabel}
); } const account = user.email || user.name || 'Account'; const userDropdown = ( {account} {accountMenuExtra} Log out ); return ( <> {children} ); }