// HTML-first invoice templates — the single source of on-screen layout for // both the web hub and the desktop app, authored to also feed the shared // HTML -> PDF pipeline (OPL-2601) without modification. // // This entry point is safe to import from client bundles: it only references // the DB layer through erased `import type`, so no drizzle/sqlite code is // pulled in. import type { ComponentType } from "react"; import { MandalaInvoiceTemplate } from "./mandala"; import { MinimalInvoiceTemplate } from "./minimal"; import type { InvoiceHtmlTemplateName, InvoiceTemplateProps } from "./types"; export { MandalaInvoiceTemplate } from "./mandala"; export { MinimalInvoiceTemplate } from "./minimal"; export { configToCssVars, DEFAULT_HTML_TEMPLATE_CONFIG, formatInvoiceCurrency, formatInvoiceDate, formatLineQuantity, hasInvoiceFooter, InvoiceFrame, lineQuantityHeader, minutesToHours, } from "./shared"; export type { InvoiceHtmlTemplateName, InvoicePageSize, InvoiceTemplateConfig, InvoiceTemplateData, InvoiceTemplateLineItem, InvoiceTemplateProps, InvoiceTemplateWorkBreakdown, } from "./types"; /** Registry of the built-in HTML invoice templates, keyed by name. */ export const HTML_INVOICE_TEMPLATES: Record< InvoiceHtmlTemplateName, ComponentType > = { minimal: MinimalInvoiceTemplate, mandala: MandalaInvoiceTemplate, }; export interface InvoiceHtmlTemplateMeta { /** Render edge-to-edge: pdf pipeline sets page margins to 0 and the template owns its own padding. */ fullBleed: boolean; } export const HTML_INVOICE_TEMPLATE_META: Record< InvoiceHtmlTemplateName, InvoiceHtmlTemplateMeta > = { minimal: { fullBleed: false }, mandala: { fullBleed: true }, }; /** Resolve template metadata with the same minimal fallback as the registry. */ export function selectHtmlInvoiceTemplateMeta( name: string | null | undefined, ): InvoiceHtmlTemplateMeta { if (name && name in HTML_INVOICE_TEMPLATE_META) { return HTML_INVOICE_TEMPLATE_META[name as InvoiceHtmlTemplateName]; } return HTML_INVOICE_TEMPLATE_META.minimal; } /** * Resolve a template component by name. Unknown names fall back to `minimal`, * the safe, neutral default that every config renders cleanly. */ export function selectHtmlInvoiceTemplate( name: string | null | undefined, ): ComponentType { if (name && name in HTML_INVOICE_TEMPLATES) { return HTML_INVOICE_TEMPLATES[name as InvoiceHtmlTemplateName]; } return MinimalInvoiceTemplate; }