rental confirmation looks less like spam

This commit is contained in:
jackiettran
2025-10-09 15:13:40 -04:00
parent 34c0ad2920
commit 513347e8b7
2 changed files with 78 additions and 23 deletions

View File

@@ -57,6 +57,45 @@ class EmailService {
}
}
/**
* Convert HTML to plain text for email fallback
* Strips HTML tags and formats content for plain text email clients
*/
htmlToPlainText(html) {
return html
// Remove style and script tags and their content
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
// Convert common HTML elements to text equivalents
.replace(/<br\s*\/?>/gi, '\n')
.replace(/<\/p>/gi, '\n\n')
.replace(/<\/div>/gi, '\n')
.replace(/<\/li>/gi, '\n')
.replace(/<\/h[1-6]>/gi, '\n\n')
.replace(/<li>/gi, '• ')
// Remove remaining HTML tags
.replace(/<[^>]+>/g, '')
// Decode HTML entities
.replace(/&nbsp;/g, ' ')
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
// Remove emojis and special characters that don't render well in plain text
.replace(/[\u{1F600}-\u{1F64F}]/gu, '') // Emoticons
.replace(/[\u{1F300}-\u{1F5FF}]/gu, '') // Misc Symbols and Pictographs
.replace(/[\u{1F680}-\u{1F6FF}]/gu, '') // Transport and Map
.replace(/[\u{2600}-\u{26FF}]/gu, '') // Misc symbols
.replace(/[\u{2700}-\u{27BF}]/gu, '') // Dingbats
.replace(/[\u{FE00}-\u{FE0F}]/gu, '') // Variation Selectors
.replace(/[\u{1F900}-\u{1F9FF}]/gu, '') // Supplemental Symbols and Pictographs
.replace(/[\u{1FA70}-\u{1FAFF}]/gu, '') // Symbols and Pictographs Extended-A
// Clean up excessive whitespace
.replace(/\n\s*\n\s*\n/g, '\n\n')
.trim();
}
async sendEmail(to, subject, htmlContent, textContent = null) {
if (!this.initialized) {
await this.initialize();
@@ -67,8 +106,18 @@ class EmailService {
return { success: true, messageId: "disabled" };
}
// Auto-generate plain text from HTML if not provided
if (!textContent) {
textContent = this.htmlToPlainText(htmlContent);
}
// Use friendly sender name format for better recognition
const fromName = process.env.SES_FROM_NAME || "RentAll";
const fromEmail = process.env.SES_FROM_EMAIL;
const source = `${fromName} <${fromEmail}>`;
const params = {
Source: process.env.SES_FROM_EMAIL,
Source: source,
Destination: {
ToAddresses: Array.isArray(to) ? to : [to],
},
@@ -82,17 +131,14 @@ class EmailService {
Data: htmlContent,
Charset: "UTF-8",
},
Text: {
Data: textContent,
Charset: "UTF-8",
},
},
},
};
if (textContent) {
params.Message.Body.Text = {
Data: textContent,
Charset: "UTF-8",
};
}
if (process.env.SES_REPLY_TO_EMAIL) {
params.ReplyToAddresses = [process.env.SES_REPLY_TO_EMAIL];
}
@@ -177,6 +223,7 @@ class EmailService {
rentalConfirmation: baseTemplate.replace(
"{{content}}",
`
<p>Hi {{recipientName}},</p>
<h2>{{title}}</h2>
<p>{{message}}</p>
<p><strong>Item:</strong> {{itemName}}</p>
@@ -220,11 +267,14 @@ class EmailService {
);
}
async sendRentalConfirmation(userEmail, notification, rental) {
async sendRentalConfirmation(userEmail, notification, rental, recipientName = null) {
const itemName = rental?.item?.name || "Unknown Item";
const variables = {
recipientName: recipientName || "there",
title: notification.title,
message: notification.message,
itemName: rental?.item?.name || "Unknown Item",
itemName: itemName,
startDate: rental?.startDateTime
? new Date(rental.startDateTime).toLocaleDateString()
: "Not specified",
@@ -235,9 +285,12 @@ class EmailService {
const htmlContent = this.renderTemplate("rentalConfirmation", variables);
// Use clear, transactional subject line with item name
const subject = `Rental Confirmation - ${itemName}`;
return await this.sendEmail(
userEmail,
`RentAll: ${notification.title}`,
subject,
htmlContent
);
}
@@ -421,12 +474,12 @@ class EmailService {
};
try {
// Get owner and renter emails
// Get owner and renter details
const owner = await User.findByPk(rental.ownerId, {
attributes: ["email"],
attributes: ["email", "firstName"],
});
const renter = await User.findByPk(rental.renterId, {
attributes: ["email"],
attributes: ["email", "firstName"],
});
// Create notification data for owner
@@ -455,7 +508,8 @@ class EmailService {
const ownerResult = await this.sendRentalConfirmation(
owner.email,
ownerNotification,
rental
rental,
owner.firstName
);
if (ownerResult.success) {
console.log(
@@ -482,7 +536,8 @@ class EmailService {
const renterResult = await this.sendRentalConfirmation(
renter.email,
renterNotification,
rental
rental,
renter.firstName
);
if (renterResult.success) {
console.log(