/** * Shared HTML shell for transactional emails. Centralises the table- * based layout + the per-port branding override surface so templates * don't each inline a different copy of the boilerplate. * * Per-port branding (R2-H15): * - logoUrl - replaces the default Port Nimara logo image * - primaryColor - used for the page-title accent color * - emailHeaderHtml / emailFooterHtml - admin-authored HTML that * appears above / below the body content (e.g. legal footer, * custom marketing strip). When unset, the existing minimal * "Thank you, {{portName}} CRM" sign-off is rendered by callers. * * Senders resolve a `BrandingShell` via `resolveBrandingShell(portId)` * (or pass `null` for no override) and forward it to the template * function. Templates call `renderShell({ title, body, branding })`. */ import { absolutizeBrandingUrl } from '@/lib/branding/url'; // Neutral defaults - no tenant-specific imagery leaks across ports. // When branding hasn't been configured the email renders without a logo // and on a plain off-white background. Admins upload their own assets via // /admin/branding which then flow through via getPortBrandingConfig(). const DEFAULT_LOGO_URL: string | null = null; const DEFAULT_BACKGROUND_URL: string | null = null; const DEFAULT_PRIMARY_COLOR = '#1e293b'; export interface BrandingShell { logoUrl: string | null; /** Phase 5: blurred page-background image rendered behind the white * card. Defaults to the Port Nimara overhead image. Ports with * their own marina photography override via system_settings. */ backgroundUrl: string | null; primaryColor: string | null; emailHeaderHtml: string | null; emailFooterHtml: string | null; } interface ShellOpts { title: string; body: string; branding?: BrandingShell | null; } export function renderShell({ title, body, branding }: ShellOpts): string { // Branding URLs are stored path-only (so in-app rendering works across // any host). Mail clients have no app origin, so re-absolutize here. const logoUrl = absolutizeBrandingUrl(branding?.logoUrl ?? DEFAULT_LOGO_URL); const backgroundUrl = absolutizeBrandingUrl(branding?.backgroundUrl ?? DEFAULT_BACKGROUND_URL); const headerHtml = branding?.emailHeaderHtml ?? ''; const footerHtml = branding?.emailFooterHtml ?? ''; const wrapperStyle = backgroundUrl ? `background-image: url('${backgroundUrl}'); background-size: cover; background-position: center; background-color:#f2f2f2;` : 'background-color:#f2f2f2;'; const logoBlock = logoUrl ? `