rental request email to owner
This commit is contained in:
@@ -41,6 +41,7 @@ class EmailService {
|
||||
"lateReturnCS.html",
|
||||
"damageReportCS.html",
|
||||
"lostItemCS.html",
|
||||
"rentalRequest.html",
|
||||
];
|
||||
|
||||
for (const templateFile of templateFiles) {
|
||||
@@ -65,38 +66,40 @@ class EmailService {
|
||||
* 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(/ /g, ' ')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/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();
|
||||
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(/ /g, " ")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/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) {
|
||||
@@ -271,6 +274,22 @@ class EmailService {
|
||||
<p><strong>Didn't change your password?</strong> If you did not make this change, please contact our support team immediately.</p>
|
||||
`
|
||||
),
|
||||
|
||||
rentalRequest: baseTemplate.replace(
|
||||
"{{content}}",
|
||||
`
|
||||
<p>Hi {{ownerName}},</p>
|
||||
<h2>New Rental Request for {{itemName}}</h2>
|
||||
<p>{{renterName}} would like to rent your item.</p>
|
||||
<p><strong>Rental Period:</strong> {{startDate}} to {{endDate}}</p>
|
||||
<p><strong>Total Amount:</strong> \${{totalAmount}}</p>
|
||||
<p><strong>Your Earnings:</strong> \${{payoutAmount}}</p>
|
||||
<p><strong>Delivery Method:</strong> {{deliveryMethod}}</p>
|
||||
<p><strong>Renter Notes:</strong> {{rentalNotes}}</p>
|
||||
<p><a href="{{approveUrl}}" class="button">Review & Respond</a></p>
|
||||
<p>Please respond to this request within 24 hours.</p>
|
||||
`
|
||||
),
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -307,7 +326,12 @@ class EmailService {
|
||||
);
|
||||
}
|
||||
|
||||
async sendRentalConfirmation(userEmail, notification, rental, recipientName = null) {
|
||||
async sendRentalConfirmation(
|
||||
userEmail,
|
||||
notification,
|
||||
rental,
|
||||
recipientName = null
|
||||
) {
|
||||
const itemName = rental?.item?.name || "Unknown Item";
|
||||
|
||||
const variables = {
|
||||
@@ -328,11 +352,7 @@ class EmailService {
|
||||
// Use clear, transactional subject line with item name
|
||||
const subject = `Rental Confirmation - ${itemName}`;
|
||||
|
||||
return await this.sendEmail(
|
||||
userEmail,
|
||||
subject,
|
||||
htmlContent
|
||||
);
|
||||
return await this.sendEmail(userEmail, subject, htmlContent);
|
||||
}
|
||||
|
||||
async sendVerificationEmail(user, verificationToken) {
|
||||
@@ -392,6 +412,62 @@ class EmailService {
|
||||
);
|
||||
}
|
||||
|
||||
async sendRentalRequestEmail(rental) {
|
||||
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000";
|
||||
const approveUrl = `${frontendUrl}/my-listings?rentalId=${rental.id}`;
|
||||
|
||||
// Fetch owner details
|
||||
const owner = await User.findByPk(rental.ownerId, {
|
||||
attributes: ["email", "firstName", "lastName"],
|
||||
});
|
||||
|
||||
// Fetch renter details
|
||||
const renter = await User.findByPk(rental.renterId, {
|
||||
attributes: ["firstName", "lastName"],
|
||||
});
|
||||
|
||||
if (!owner || !renter) {
|
||||
console.error(
|
||||
"Owner or renter not found for rental request notification"
|
||||
);
|
||||
return { success: false, error: "User not found" };
|
||||
}
|
||||
|
||||
const variables = {
|
||||
ownerName: owner.firstName,
|
||||
renterName: `${renter.firstName} ${renter.lastName}`.trim() || "A renter",
|
||||
itemName: rental.item?.name || "your item",
|
||||
startDate: rental.startDateTime
|
||||
? new Date(rental.startDateTime).toLocaleString("en-US", {
|
||||
dateStyle: "medium",
|
||||
timeStyle: "short",
|
||||
})
|
||||
: "Not specified",
|
||||
endDate: rental.endDateTime
|
||||
? new Date(rental.endDateTime).toLocaleString("en-US", {
|
||||
dateStyle: "medium",
|
||||
timeStyle: "short",
|
||||
})
|
||||
: "Not specified",
|
||||
totalAmount: rental.totalAmount
|
||||
? parseFloat(rental.totalAmount).toFixed(2)
|
||||
: "0.00",
|
||||
payoutAmount: rental.payoutAmount
|
||||
? parseFloat(rental.payoutAmount).toFixed(2)
|
||||
: "0.00",
|
||||
deliveryMethod: rental.deliveryMethod || "Not specified",
|
||||
approveUrl: approveUrl,
|
||||
};
|
||||
|
||||
const htmlContent = this.renderTemplate("rentalRequest", variables);
|
||||
|
||||
return await this.sendEmail(
|
||||
owner.email,
|
||||
`Rental Request for ${rental.item?.name || "Your Item"}`,
|
||||
htmlContent
|
||||
);
|
||||
}
|
||||
|
||||
async sendTemplateEmail(toEmail, subject, templateName, variables = {}) {
|
||||
const htmlContent = this.renderTemplate(templateName, variables);
|
||||
return await this.sendEmail(toEmail, subject, htmlContent);
|
||||
@@ -461,9 +537,9 @@ class EmailService {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate total fees
|
||||
const damageFee = damageAssessment.feeCalculation.amount;
|
||||
const lateFee = lateCalculation?.lateFee || 0;
|
||||
// Calculate total fees (ensure numeric values)
|
||||
const damageFee = parseFloat(damageAssessment.feeCalculation.amount) || 0;
|
||||
const lateFee = parseFloat(lateCalculation?.lateFee || 0);
|
||||
const totalFees = damageFee + lateFee;
|
||||
|
||||
// Determine fee type description
|
||||
|
||||
Reference in New Issue
Block a user