import type { Metadata } from 'next'; import { getPortalSession } from '@/lib/portal/auth'; import { getPortalDashboard } from '@/lib/services/portal.service'; import { isPortalDisabledGlobally } from '@/lib/services/portal-auth.service'; import { PortalHeader } from '@/components/portal/portal-header'; import { PortalNav } from '@/components/portal/portal-nav'; export const metadata: Metadata = { title: { default: 'Client Portal', template: '%s | Client Portal', }, }; export default async function PortalLayout({ children }: { children: React.ReactNode }) { // Route-level kill switch. When every port has client_portal_enabled=false, // surface a clean "Portal not available" notice instead of letting the // login form render (it would just reject every submit with a confusing // ConflictError). Single-port deployments effectively get a global toggle // out of the admin System Settings UI. if (await isPortalDisabledGlobally()) { return (

Client portal unavailable

The client portal isn't currently enabled for this site. If you were expecting to sign in here, please contact your account manager.

); } // This layout wraps all portal routes including login/verify // We can't easily check pathname in a server layout, so we attempt // to get the session and pass it down - login/verify pages handle their own // redirect logic independently. const session = await getPortalSession().catch(() => null); // For authenticated routes we need client info for the header. // If session is absent, children (login/verify pages) handle their own redirect. let clientName = ''; let portName = 'Client Portal'; let portLogoUrl: string | null = null; if (session) { const dashboard = await getPortalDashboard(session.clientId, session.portId).catch(() => null); if (dashboard) { clientName = dashboard.client.fullName; portName = dashboard.port.name; portLogoUrl = dashboard.port.logoUrl; } } return (
{session && ( <> )}
{children}
); }