130 lines
5.1 KiB
TypeScript
130 lines
5.1 KiB
TypeScript
import { PageLayout, LayoutContainer, WidgetInstance } from './unifiedLayoutManager';
|
|
import { widgetRegistry } from './widgetRegistry';
|
|
import { template } from '@/lib/variables';
|
|
import { marked } from 'marked';
|
|
|
|
export const generateEmailHtml = async (layout: PageLayout, rootTemplateUrl: string): Promise<string> => {
|
|
try {
|
|
// 1. Fetch Root Template
|
|
const rootRes = await fetch(rootTemplateUrl);
|
|
if (!rootRes.ok) throw new Error(`Failed to load root template: ${rootTemplateUrl}`);
|
|
let rootHtml = await rootRes.text();
|
|
|
|
// 2. Resolve Root Template Variables (e.g. ${title})
|
|
// Map layout properties to variables. We map 'name' to 'title' for common usage.
|
|
const rootVars: Record<string, any> = {
|
|
...layout,
|
|
title: layout.name,
|
|
SOURCE: '${SOURCE}'
|
|
};
|
|
rootHtml = template(rootHtml, rootVars, false);
|
|
|
|
// 3. Generate Content Body
|
|
const contentHtml = await generateContainerHtml(layout.containers);
|
|
|
|
// 4. Inject Content
|
|
if (rootHtml.includes('${SOURCE}')) {
|
|
return rootHtml.replace('${SOURCE}', contentHtml);
|
|
} else {
|
|
return rootHtml.replace('</body>', `${contentHtml}</body>`);
|
|
}
|
|
} catch (error) {
|
|
console.error("Email generation failed:", error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const generateContainerHtml = async (containers: LayoutContainer[]): Promise<string> => {
|
|
let html = '';
|
|
|
|
// Sort containers
|
|
const sortedContainers = [...containers].sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
|
|
for (const container of sortedContainers) {
|
|
const gap = container.gap || 0;
|
|
|
|
// Container Table
|
|
html += `<table width="100%" border="0" cellpadding="0" cellspacing="0" style="margin-bottom: ${gap}px; min-width: 100%;">`;
|
|
html += `<tr><td align="center" valign="top">`;
|
|
html += `<table width="100%" border="0" cellpadding="0" cellspacing="0"><tr>`;
|
|
|
|
if (container.columns === 1) {
|
|
// Stacked vertical
|
|
html += `<td width="100%" valign="top">`;
|
|
const sortedWidgets = [...container.widgets].sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
|
|
for (const widget of sortedWidgets) {
|
|
html += await generateWidgetHtml(widget);
|
|
}
|
|
if (container.children?.length > 0) {
|
|
html += await generateContainerHtml(container.children);
|
|
}
|
|
html += `</td>`;
|
|
} else {
|
|
// Grid Layout
|
|
const colWidth = Math.floor(100 / container.columns);
|
|
const sortedWidgets = [...container.widgets].sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
|
|
for (let i = 0; i < sortedWidgets.length; i += container.columns) {
|
|
const rowWidgets = sortedWidgets.slice(i, i + container.columns);
|
|
if (i > 0) html += `</tr><tr>`;
|
|
|
|
for (const widget of rowWidgets) {
|
|
html += `<td width="${colWidth}%" valign="top">`;
|
|
html += await generateWidgetHtml(widget);
|
|
html += `</td>`;
|
|
}
|
|
|
|
// Filler cells
|
|
if (rowWidgets.length < container.columns) {
|
|
for (let j = rowWidgets.length; j < container.columns; j++) {
|
|
html += `<td width="${colWidth}%"> </td>`;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
html += `</tr></table>`;
|
|
html += `</td></tr>`;
|
|
html += `</table>`;
|
|
}
|
|
|
|
return html;
|
|
};
|
|
|
|
const generateWidgetHtml = async (widget: WidgetInstance): Promise<string> => {
|
|
const def = widgetRegistry.get(widget.widgetId);
|
|
if (!def) return `<!-- Missing Widget: ${widget.widgetId} -->`;
|
|
|
|
const templateUrl = def.metadata.defaultProps?.__templateUrl;
|
|
|
|
if (templateUrl) {
|
|
try {
|
|
const res = await fetch(templateUrl);
|
|
if (res.ok) {
|
|
const content = await res.text();
|
|
|
|
// Process props for markdown conversion
|
|
const processedProps = { ...(widget.props || {}) };
|
|
if (def.metadata.configSchema) {
|
|
for (const [key, schema] of Object.entries(def.metadata.configSchema)) {
|
|
if ((schema as any).type === 'markdown' && typeof processedProps[key] === 'string') {
|
|
try {
|
|
processedProps[key] = await marked.parse(processedProps[key]);
|
|
} catch (e) {
|
|
console.warn(`Failed to parse markdown for widget ${widget.widgetId} prop ${key}`, e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Perform Substitution using helper
|
|
return template(content, processedProps, false);
|
|
}
|
|
} catch (e) {
|
|
console.error(`Failed to fetch template for ${widget.widgetId}`, e);
|
|
}
|
|
}
|
|
return `<!-- Widget Content: ${widget.widgetId} -->`;
|
|
};
|