diff --git a/src/modules/email/email.module.ts b/src/modules/email/email.module.ts index cf7728e..e971ed3 100644 --- a/src/modules/email/email.module.ts +++ b/src/modules/email/email.module.ts @@ -10,7 +10,6 @@ import { MailboxResolverService } from "./services/mailbox-resolver.service"; import { WebSocketAuthService } from "./services/websocket-auth.service"; import { jwtConfig } from "../../configs/jwt.config"; import { MailServerModule } from "../mail-server/mail-server.module"; -import { TemplateProcessorService } from "../templates/services/template-processor.service"; import { TemplatesModule } from "../templates/templates.module"; import { User } from "../users/entities/user.entity"; import { UsersModule } from "../users/users.module"; @@ -18,7 +17,7 @@ import { UsersModule } from "../users/users.module"; @Module({ imports: [MailServerModule, UsersModule, TemplatesModule, JwtModule.registerAsync(jwtConfig()), MikroOrmModule.forFeature([User])], controllers: [EmailController], - providers: [EmailService, MailboxResolverService, EmailGateway, EmailNotificationService, WebSocketAuthService, TemplateProcessorService], - exports: [EmailService, MailboxResolverService, EmailGateway, EmailNotificationService, WebSocketAuthService, TemplateProcessorService], + providers: [EmailService, MailboxResolverService, EmailGateway, EmailNotificationService, WebSocketAuthService], + exports: [EmailService, MailboxResolverService, EmailGateway, EmailNotificationService, WebSocketAuthService], }) export class EmailModule {} diff --git a/src/modules/email/services/email.service.ts b/src/modules/email/services/email.service.ts index 70fb0da..d63b3ac 100644 --- a/src/modules/email/services/email.service.ts +++ b/src/modules/email/services/email.service.ts @@ -37,15 +37,20 @@ export class EmailService { this.logger.log(`Processing email through business template for business: ${user.business.id}`); // Process email content through business template - const processedTemplate = await this.templateProcessorService.processEmailWithTemplate(user.business.id, { + const processedTemplate = await this.templateProcessorService.processEmailContent(user.business.id, { text: sendEmailDto.text, html: sendEmailDto.html, subject: sendEmailDto.subject, }); // Update the DTO with processed content - sendEmailDto.html = processedTemplate.html; - sendEmailDto.text = processedTemplate.text; + if (processedTemplate.isHtml) { + sendEmailDto.html = processedTemplate.content; + sendEmailDto.text = undefined; // Clear text when using HTML template + } else { + sendEmailDto.text = processedTemplate.content; + sendEmailDto.html = undefined; // Clear HTML when no template + } if (processedTemplate.hasTemplate) { this.logger.log(`Email content processed through template for business: ${user.business.id}`); @@ -410,14 +415,19 @@ export class EmailService { const user = await this.userRepository.findOne({ wildduckUserId: userEmailId, deletedAt: null }, { populate: ["business"] }); if (user && user.business && (updateDraftDto.text || updateDraftDto.html)) { - const processedTemplate = await this.templateProcessorService.processEmailWithTemplate(user.business.id, { + const processedTemplate = await this.templateProcessorService.processEmailContent(user.business.id, { text: updateDraftDto.text, html: updateDraftDto.html, subject: updateDraftDto.subject, }); - draftData.html = processedTemplate.html; - draftData.text = processedTemplate.text; + if (processedTemplate.isHtml) { + draftData.html = processedTemplate.content; + draftData.text = undefined; + } else { + draftData.text = processedTemplate.content; + draftData.html = undefined; + } if (processedTemplate.hasTemplate) { this.logger.log(`Draft content processed through template for business: ${user.business.id}`); diff --git a/src/modules/templates/interfaces/structure.interface.ts b/src/modules/templates/interfaces/structure.interface.ts index 36c1486..3cae048 100644 --- a/src/modules/templates/interfaces/structure.interface.ts +++ b/src/modules/templates/interfaces/structure.interface.ts @@ -102,3 +102,15 @@ export type ImageType = { alignment?: HorizontalAlignment; verticalAlignment?: VerticalAlignment; }; + +export interface EmailContent { + text?: string; + html?: string; + subject?: string; +} + +export interface ProcessedEmailResult { + content: string; + isHtml: boolean; + hasTemplate: boolean; +} diff --git a/src/modules/templates/services/template-processor.service.ts b/src/modules/templates/services/template-processor.service.ts index 20e4a8a..8a9c93e 100644 --- a/src/modules/templates/services/template-processor.service.ts +++ b/src/modules/templates/services/template-processor.service.ts @@ -1,13 +1,7 @@ import { Injectable, Logger } from "@nestjs/common"; import { TemplatesService } from "./templates.service"; -// import { PersonalityDataType, SectionItemType, TextType } from "../interfaces/structure.interface"; - -interface ProcessedTemplateResult { - html: string; - text: string; - hasTemplate: boolean; -} +import { EmailContent, ProcessedEmailResult } from "../interfaces/structure.interface"; @Injectable() export class TemplateProcessorService { @@ -15,101 +9,66 @@ export class TemplateProcessorService { constructor(private readonly templatesService: TemplatesService) {} - /** - * Process email content through business template - */ - async processEmailWithTemplate( - businessId: string, - emailContent: { text?: string; html?: string; subject?: string }, - // userId?: string, - ): Promise { + //**************************************************** */ + async processEmailContent(businessId: string, emailContent: EmailContent): Promise { try { - this.logger.log(`Processing email with template for business: ${businessId}`); + // Get selected template for the business + const selectedTemplate = await this.getSelectedTemplate(businessId); - // Get business templates - const templates = await this.templatesService.getTemplatesByBusinessId(businessId); - - if (!templates || templates.length === 0) { - this.logger.log(`No templates found for business: ${businessId}, using original content`); + if (!selectedTemplate) { + // No template - return original text content return { - html: emailContent.html || this.convertTextToHtml(emailContent.text || ""), - text: emailContent.text || this.stripHtmlTags(emailContent.html || ""), + content: emailContent.text || emailContent.html || "", + isHtml: false, hasTemplate: false, }; } - // Use the first template - const template = templates[0]; - - this.logger.log(`Using template: ${template.name} for email processing`); - - // Process the template with email content - const processedContent = await this.injectContentIntoTemplate(emailContent, template.rawHtml); + // Process with template + const processedContent = this.injectContentIntoTemplate(selectedTemplate.rawHtml, emailContent); return { - html: processedContent.html, - text: processedContent.text, + content: processedContent, + isHtml: true, hasTemplate: true, }; } catch (error) { - this.logger.error(`Failed to process email with template for business ${businessId}:`, error); + this.logger.error(`Failed to process email for business ${businessId}:`, error); - // Fallback to original content if template processing fails + // Fallback to original content return { - html: emailContent.html || this.convertTextToHtml(emailContent.text || ""), - text: emailContent.text || this.stripHtmlTags(emailContent.html || ""), + content: emailContent.text || emailContent.html || "", + isHtml: false, hasTemplate: false, }; } } - /** - * Inject email content into template structure - */ - private async injectContentIntoTemplate( - // templateStructure: Record, - emailContent: { text?: string; html?: string; subject?: string }, - rawHtml?: string, - ): Promise<{ html: string; text: string }> { - try { - // If template has raw HTML, use it as base and inject content - if (rawHtml) { - const html = this.injectContentIntoRawHtml(rawHtml, emailContent); - return { - html, - text: this.stripHtmlTags(html), - }; - } - - // Otherwise, build simple HTML with email content - const html = this.buildSimpleTemplate(emailContent); - return { - html, - text: this.stripHtmlTags(html), - }; - } catch (error) { - this.logger.error("Failed to inject content into template:", error); - throw error; - } + //**************************************************** */ + private async getSelectedTemplate(businessId: string) { + const templates = await this.templatesService.getTemplatesByBusinessId(businessId); + return templates.find((template) => template.selected) || null; } - /** - * Inject email content into raw HTML template - */ - private injectContentIntoRawHtml(rawHtml: string, emailContent: { text?: string; html?: string; subject?: string }): string { - let processedHtml = rawHtml; + //**************************************************** */ + private injectContentIntoTemplate(templateHtml: string | undefined, emailContent: EmailContent): string { + if (!templateHtml) { + return emailContent.text || emailContent.html || ""; + } - // Replace placeholders with actual content - const contentToInject = emailContent.html || this.convertTextToHtml(emailContent.text || ""); + let processedHtml = templateHtml; - // Common placeholder patterns - const placeholders = [/\{\{content\}\}/gi, /\{\{message\}\}/gi, /\{\{body\}\}/gi, /\{content\}/gi, /\{message\}/gi, /\{body\}/gi]; + // Content to inject (prefer text, fallback to html) + const contentToInject = emailContent.text || this.stripHtmlTags(emailContent.html || ""); - placeholders.forEach((placeholder) => { + // Replace content placeholders + const contentPlaceholders = [/\{\{content\}\}/gi, /\{\{message\}\}/gi, /\{\{body\}\}/gi, /\{content\}/gi, /\{message\}/gi, /\{body\}/gi]; + + contentPlaceholders.forEach((placeholder) => { processedHtml = processedHtml.replace(placeholder, contentToInject); }); - // Replace subject placeholders + // Replace subject placeholders if subject exists if (emailContent.subject) { const subjectPlaceholders = [/\{\{subject\}\}/gi, /\{\{title\}\}/gi, /\{subject\}/gi, /\{title\}/gi]; @@ -121,49 +80,7 @@ export class TemplateProcessorService { return processedHtml; } - /** - * Build simple HTML template - */ - private buildSimpleTemplate(emailContent: { text?: string; html?: string; subject?: string }): string { - const content = emailContent.html || this.convertTextToHtml(emailContent.text || ""); - - return ` - - - - - - ${emailContent.subject || "Email"} - - - - - - - `; - } - - /** - * Convert plain text to HTML - */ - private convertTextToHtml(text: string): string { - return text - .replace(/\n\n/g, "

") - .replace(/\n/g, "
") - .replace(/^(.*)$/, "

$1

"); - } - - /** - * Strip HTML tags from content - */ + //**************************************************** */ private stripHtmlTags(html: string): string { return html .replace(/<[^>]*>/g, "") @@ -172,6 +89,7 @@ export class TemplateProcessorService { .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, '"') + .replace(/'/g, "'") .trim(); } } diff --git a/src/modules/templates/templates.controller.ts b/src/modules/templates/templates.controller.ts index 4baaac5..9e3b0cf 100644 --- a/src/modules/templates/templates.controller.ts +++ b/src/modules/templates/templates.controller.ts @@ -57,7 +57,7 @@ export class TemplatesController { return this.templatesService.deleteTemplate(params.id, businessId); } - @Post(":id/set-selected") + @Patch(":id/set-selected") @ApiOperation({ summary: "Set template as selected (unselects all other templates for the business)" }) @ApiResponse({ status: 200, description: "Template set as selected successfully" }) @ApiResponse({ status: 404, description: "Template not found" })