// 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;
}