'use client'; import { useAuthBranding } from '@/components/shared/auth-branding-provider'; interface BrandedAuthShellProps { children: React.ReactNode; /** Per-port branding override. When omitted, the shell picks up * branding from the surrounding `` (mounted at * the route-group layout). When neither is present, falls back to * neutral defaults (no logo, plain background). */ branding?: { logoUrl?: string | null; backgroundUrl?: string | null; appName?: string | null; }; } /** * Branded shell shared by every auth/form surface - CRM login, portal login, * password set/reset/activate, forgot-password. Renders the background, * the port logo, and a centered white card that consumers populate with * their own form/content. * * Pages that know their portId at render time can pass `branding` as an * explicit prop; otherwise the surrounding `` is * the source of truth. */ export function BrandedAuthShell({ children, branding }: BrandedAuthShellProps) { const ctx = useAuthBranding(); const logoUrl = branding?.logoUrl ?? ctx?.logoUrl ?? null; const backgroundUrl = branding?.backgroundUrl ?? ctx?.backgroundUrl ?? null; // When no port name is known, treat the logo as decorative - "Sign in" // as alt text was being read on every auth page even when the page // itself isn't a sign-in surface (e.g. password reset, set-password). const appName = branding?.appName ?? ctx?.appName ?? null; const altText = appName ?? ''; // fixed inset-0 anchors the auth surface to the viewport directly - // iOS Safari ignores overflow-hidden on inner divs for body-level // scrolling, so a regular `h-dvh overflow-hidden` wrapper doesn't // stop the rubber-band bounce. Pinning to the viewport via position // fixed does. The fixed-position shell then uses flex to center the // card within the visible area. return (
{logoUrl ? (
{/* eslint-disable-next-line @next/next/no-img-element */} {altText}
) : null} {children}
); }