131 lines
4.4 KiB
JavaScript
131 lines
4.4 KiB
JavaScript
const fs = require("fs").promises;
|
|
const path = require("path");
|
|
const Handlebars = require("handlebars");
|
|
const config = require("../config");
|
|
|
|
class TemplateEngine {
|
|
constructor() {
|
|
this.templatesDir = path.join(__dirname, "..", "templates");
|
|
this.compiledTemplates = new Map();
|
|
}
|
|
|
|
// Convert HTML to plain text by removing tags and formatting
|
|
htmlToText(html) {
|
|
return html
|
|
.replace(/<style[^>]*>.*?<\/style>/gis, "") // Remove style blocks
|
|
.replace(/<script[^>]*>.*?<\/script>/gis, "") // Remove script blocks
|
|
.replace(/<br\s*\/?>/gi, "\n") // Convert <br> to newlines
|
|
.replace(/<\/p>/gi, "\n\n") // Convert </p> to double newlines
|
|
.replace(/<\/div>/gi, "\n") // Convert </div> to newlines
|
|
.replace(/<\/h[1-6]>/gi, "\n\n") // Convert headings to double newlines
|
|
.replace(/<li[^>]*>/gi, "• ") // Convert <li> to bullet points
|
|
.replace(/<\/li>/gi, "\n") // End list items with newlines
|
|
.replace(/<[^>]*>/g, "") // Remove all other HTML tags
|
|
.replace(/ /g, " ") // Convert to spaces
|
|
.replace(/&/g, "&") // Convert & to &
|
|
.replace(/</g, "<") // Convert < to <
|
|
.replace(/>/g, ">") // Convert > to >
|
|
.replace(/"/g, '"') // Convert " to "
|
|
.replace(/'/g, "'") // Convert ' to '
|
|
.replace(/\n\s*\n\s*\n/g, "\n\n") // Reduce multiple newlines to double
|
|
.replace(/^\s+|\s+$/gm, "") // Trim whitespace from lines
|
|
.trim();
|
|
}
|
|
|
|
async loadTemplate(templateName) {
|
|
const cacheKey = templateName;
|
|
|
|
if (this.compiledTemplates.has(cacheKey)) {
|
|
return this.compiledTemplates.get(cacheKey);
|
|
}
|
|
|
|
try {
|
|
const htmlPath = path.join(this.templatesDir, `${templateName}.html`);
|
|
const htmlContent = await fs.readFile(htmlPath, "utf-8");
|
|
|
|
// Automatically inject GIF if enabled
|
|
let finalHtmlContent = htmlContent;
|
|
if (config.gif.enabled && templateName === "outreach") {
|
|
finalHtmlContent = this.injectGifIntoHtml(htmlContent);
|
|
}
|
|
|
|
const htmlTemplate = Handlebars.compile(finalHtmlContent);
|
|
|
|
// Generate text version from HTML
|
|
const textTemplate = Handlebars.compile(
|
|
this.htmlToText(finalHtmlContent)
|
|
);
|
|
|
|
const compiledTemplate = {
|
|
html: htmlTemplate,
|
|
text: textTemplate,
|
|
};
|
|
|
|
this.compiledTemplates.set(cacheKey, compiledTemplate);
|
|
return compiledTemplate;
|
|
} catch (error) {
|
|
throw new Error(
|
|
`Failed to load template ${templateName}: ${error.message}`
|
|
);
|
|
}
|
|
}
|
|
|
|
// Inject GIF into HTML template automatically
|
|
injectGifIntoHtml(htmlContent) {
|
|
const gifHtml = `
|
|
{{#if gifUrl}}
|
|
<div style="text-align: center; margin: 20px 0;">
|
|
<img src="{{gifUrl}}" alt="{{gifAlt}}" style="max-width: 100%; height: auto; border-radius: 5px;" />
|
|
</div>
|
|
{{/if}}
|
|
`;
|
|
|
|
// Insert GIF after the header or at the beginning of content
|
|
if (htmlContent.includes('<div class="content">')) {
|
|
return htmlContent.replace(
|
|
'<div class="content">',
|
|
`<div class="content">${gifHtml}`
|
|
);
|
|
} else if (htmlContent.includes("<body>")) {
|
|
return htmlContent.replace("<body>", `<body>${gifHtml}`);
|
|
} else {
|
|
// Fallback: add at the beginning
|
|
return gifHtml + htmlContent;
|
|
}
|
|
}
|
|
|
|
async render(templateName, data) {
|
|
const template = await this.loadTemplate(templateName);
|
|
|
|
// Add default sender information from config
|
|
const defaultData = {
|
|
senderName: "John Smith",
|
|
senderTitle: "Business Development Manager",
|
|
senderCompany: "Legal Solutions Inc.",
|
|
fromEmail: config.email.user,
|
|
gifUrl: config.gif.enabled ? config.gif.url : null,
|
|
gifAlt: config.gif.enabled ? config.gif.alt : null,
|
|
...data,
|
|
};
|
|
|
|
return {
|
|
html: template.html(defaultData),
|
|
text: template.text(defaultData),
|
|
};
|
|
}
|
|
|
|
// Helper to format firm data for template
|
|
formatFirmData(firm) {
|
|
return {
|
|
firmName: firm.firmName || "your firm",
|
|
location: firm.location,
|
|
website: firm.website,
|
|
email: firm.contactEmail || firm.email,
|
|
greeting: firm.name || "Legal Professional",
|
|
// Additional fields can be mapped here
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = new TemplateEngine();
|