81 lines
3.2 KiB
TypeScript
81 lines
3.2 KiB
TypeScript
|
|
/**
|
||
|
|
* 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 })`.
|
||
|
|
*/
|
||
|
|
|
||
|
|
const DEFAULT_LOGO_URL =
|
||
|
|
'https://s3.portnimara.com/images/Port%20Nimara%20New%20Logo-Circular%20Frame_250px.png';
|
||
|
|
const DEFAULT_BACKGROUND_URL = 'https://s3.portnimara.com/images/Overhead_1_blur.png';
|
||
|
|
const DEFAULT_PRIMARY_COLOR = '#0F4C81';
|
||
|
|
|
||
|
|
export interface BrandingShell {
|
||
|
|
logoUrl: 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 {
|
||
|
|
const logoUrl = branding?.logoUrl ?? DEFAULT_LOGO_URL;
|
||
|
|
const headerHtml = branding?.emailHeaderHtml ?? '';
|
||
|
|
const footerHtml = branding?.emailFooterHtml ?? '';
|
||
|
|
|
||
|
|
return `<!DOCTYPE html>
|
||
|
|
<html>
|
||
|
|
<head>
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||
|
|
<title>${title}</title>
|
||
|
|
<style type="text/css">
|
||
|
|
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
|
||
|
|
img { border: 0; display: block; }
|
||
|
|
p { margin: 0; padding: 0; }
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body style="margin:0; padding:0; background-color:#f2f2f2;">
|
||
|
|
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0" style="background-image: url('${DEFAULT_BACKGROUND_URL}'); background-size: cover; background-position: center; background-color:#f2f2f2;">
|
||
|
|
<tr>
|
||
|
|
<td align="center" style="padding:30px 16px;">
|
||
|
|
<table role="presentation" width="600" border="0" cellspacing="0" cellpadding="0" style="width:100%; max-width:600px; background-color:#ffffff; border-radius:8px; overflow:hidden; box-shadow:0 2px 4px rgba(0,0,0,0.1);">
|
||
|
|
<tr>
|
||
|
|
<td style="padding:20px; font-family: Arial, sans-serif; color:#333333; word-break:break-word;">
|
||
|
|
<center>
|
||
|
|
<img src="${logoUrl}" alt="Port logo" width="100" style="margin-bottom:20px;" />
|
||
|
|
</center>
|
||
|
|
${headerHtml ? `<div>${headerHtml}</div>` : ''}
|
||
|
|
${body}
|
||
|
|
${footerHtml ? `<div style="margin-top:24px;">${footerHtml}</div>` : ''}
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
</table>
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
</table>
|
||
|
|
</body>
|
||
|
|
</html>`;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Surface the brand primary color to template bodies. */
|
||
|
|
export function brandingPrimaryColor(branding?: BrandingShell | null): string {
|
||
|
|
return branding?.primaryColor ?? DEFAULT_PRIMARY_COLOR;
|
||
|
|
}
|