// The portal shell — and the AUTH GATE. This layout wraps every page in the // silent `(portal)` group, and its `loader` runs server-side BEFORE the pages // render: it asks the api's `session.me` and throws RedirectError to /login for // anyone not signed in. The pages under here are `renderMode: 'ssr'` precisely so // this runs per request. import type { ReactNode } from 'react' import { RedirectError, useLoaderData, useLocation, type LoaderFn } from '@voltro/web' import { T, useLocale, useT } from '@voltro/i18n' import { LocaleSwitcher } from '@voltro/ui-shadcn' import { APP_NAME } from '../../config' import type { Identity } from '../../lib/api' const LOCALES = [ { code: 'en', label: 'English' }, { code: 'de', label: 'Deutsch' }, ] interface PortalData { readonly tenantId: string | null } export const loader: LoaderFn = async ({ query }) => { if (query) { const me = await query('session.me', {}) if (me.type !== 'user') { throw new RedirectError('/login?from=/') } return { tenantId: me.tenantId } } return { tenantId: null } } export default function PortalLayout({ children }: { readonly children: ReactNode }): ReactNode { useLoaderData() const pathname = useLocation() const locale = useLocale() const langLabel = useT('lang.label') const navItem = (href: string, label: ReactNode): ReactNode => ( {label} ) return (
{children}
) }