port-nimara-client-portal/server/api/eoi/send-reminders.ts

313 lines
10 KiB
TypeScript

import { requireAuth } from '~/server/utils/auth';
import { getDocumensoDocument, checkDocumentSignatureStatus, formatRecipientName } from '~/server/utils/documeso';
import { getInterestById } from '~/server/utils/nocodb';
import { sendEmail } from '~/server/utils/email';
interface ReminderEmail {
to: string;
subject: string;
html: string;
}
export default defineEventHandler(async (event) => {
// Check authentication (x-tag header OR Keycloak session)
await requireAuth(event);
try {
const body = await readBody(event);
const { interestId, documentId } = body;
if (!interestId || !documentId) {
throw createError({
statusCode: 400,
statusMessage: 'Interest ID and Document ID are required',
});
}
// Get interest details
const interest = await getInterestById(interestId);
if (!interest) {
throw createError({
statusCode: 404,
statusMessage: 'Interest not found',
});
}
// Check if reminders are enabled for this interest
// For now, we'll assume they're always enabled unless explicitly disabled
const remindersEnabled = (interest as any)['reminder_enabled'] !== false;
if (!remindersEnabled) {
return {
success: false,
message: 'Reminders are disabled for this interest'
};
}
// Get document and check signature status
const document = await getDocumensoDocument(parseInt(documentId));
const status = await checkDocumentSignatureStatus(parseInt(documentId));
const emailsToSend: ReminderEmail[] = [];
const currentHour = new Date().getHours();
// Determine if we should send reminders based on time
const shouldSendMorningReminder = currentHour === 9;
const shouldSendAfternoonReminder = currentHour === 16;
if (!shouldSendMorningReminder && !shouldSendAfternoonReminder) {
return {
success: false,
message: 'Reminders are only sent at 9am and 4pm'
};
}
// If client hasn't signed, send reminder to sales (4pm only)
if (!status.clientSigned && shouldSendAfternoonReminder) {
const salesEmail = generateSalesReminderEmail(interest, document);
emailsToSend.push(salesEmail);
}
// If client has signed but others haven't, send reminders to them
if (status.clientSigned && !status.allSigned) {
for (const recipient of status.unsignedRecipients) {
if (recipient.signingOrder > 1) { // Skip client
const reminderEmail = generateRecipientReminderEmail(
recipient,
interest['Full Name'] || 'Client',
recipient.signingUrl
);
emailsToSend.push(reminderEmail);
}
}
}
// Send all emails
const results = [];
for (const email of emailsToSend) {
try {
await sendReminderEmail(email);
results.push({
to: email.to,
success: true
});
} catch (error) {
console.error(`Failed to send reminder to ${email.to}:`, error);
results.push({
to: email.to,
success: false,
error: error instanceof Error ? error.message : String(error)
});
}
}
// Update last reminder sent timestamp
await updateInterest(interestId, {
'last_reminder_sent': new Date().toISOString()
} as any);
return {
success: true,
remindersSent: results.length,
results
};
} catch (error: any) {
console.error('Failed to send reminders:', error);
throw createError({
statusCode: error.statusCode || 500,
statusMessage: error.statusMessage || 'Failed to send reminders',
});
}
});
function generateRecipientReminderEmail(
recipient: any,
clientName: string,
signUrl: string
): ReminderEmail {
const recipientFirst = formatRecipientName(recipient);
const clientFormatted = clientName;
const html = `<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge" /><!--<![endif]-->
<title>Port Nimara EOI Signature Request</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;">
<!--[if gte mso 9]>
<v:background xmlns:v="urn:schemas-microsoft-com:vml" fill="true">
<v:fill type="frame" src="https://s3.portnimara.com/images/Overhead_1_blur.png" color="#f2f2f2" />
</v:background>
<![endif]-->
<table role="presentation" width="100%" border="0" cellspacing="0" cellpadding="0"
style="background-image:url('https://s3.portnimara.com/images/Overhead_1_blur.png');
background-size:cover;
background-position:center;
background-color:#f2f2f2;">
<tr>
<td align="center" style="padding:30px;">
<table role="presentation" width="600" border="0" cellspacing="0" cellpadding="0"
style="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;">
<!-- logo -->
<center>
<img
src="https://s3.portnimara.com/images/Port%20Nimara%20New%20Logo-Circular%20Frame_250px.png"
alt="Port Nimara Logo"
width="100"
style="margin-bottom:20px;"
/>
</center>
<!-- greeting & body -->
<p style="margin-bottom:10px; font-size:16px;">
Dear <strong>${recipientFirst}</strong>,
</p>
<p style="margin-bottom:20px; font-size:16px;">
There is an EOI from <strong>${clientFormatted}</strong> waiting to be signed.
Please click the button below to review and sign the document.
If you need any assistance, please reach out to the sales team.
</p>
<!-- CTA button -->
<p style="text-align:center; margin:30px 0;">
<a href="${signUrl}"
style="display:inline-block;
background-color:#007bff;
color:#ffffff;
text-decoration:none;
padding:10px 20px;
border-radius:5px;
font-weight:bold;">
Sign Your EOI
</a>
</p>
<!-- closing -->
<p style="font-size:16px;">
Thank you,<br/>
- The Port Nimara CRM
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>`;
// For testing, send to matt@portnimara.com
return {
to: 'matt@portnimara.com', // TODO: Change to recipient.email after testing
subject: `EOI Signature Reminder - ${clientName}`,
html
};
}
function generateSalesReminderEmail(interest: any, document: any): ReminderEmail {
const clientName = interest['Full Name'] || 'Client';
const html = `<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge" /><!--<![endif]-->
<title>Port Nimara EOI Signature Reminder</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('https://s3.portnimara.com/images/Overhead_1_blur.png');
background-size:cover;
background-position:center;
background-color:#f2f2f2;">
<tr>
<td align="center" style="padding:30px;">
<table role="presentation" width="600" border="0" cellspacing="0" cellpadding="0"
style="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;">
<!-- logo -->
<center>
<img
src="https://s3.portnimara.com/images/Port%20Nimara%20New%20Logo-Circular%20Frame_250px.png"
alt="Port Nimara Logo"
width="100"
style="margin-bottom:20px;"
/>
</center>
<!-- greeting & body -->
<p style="margin-bottom:10px; font-size:16px;">
Dear Sales Team,
</p>
<p style="margin-bottom:20px; font-size:16px;">
The EOI for <strong>${clientName}</strong> has not been signed by the client yet.
Please follow up with them to ensure the document is signed.
Document: ${document.title}
</p>
<!-- closing -->
<p style="font-size:16px;">
Thank you,<br/>
- The Port Nimara CRM
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>`;
return {
to: 'sales@portnimara.com',
subject: `Action Required: EOI Not Signed - ${clientName}`,
html
};
}
async function sendReminderEmail(email: ReminderEmail) {
// Use noreply@portnimara.com credentials with correct mail server
const credentials = {
host: 'mail.portnimara.com',
port: 465,
secure: true,
auth: {
user: 'noreply@portnimara.com',
pass: 'sJw6GW5G5bCI1EtBIq3J2hVm8xCOMw1kQs1puS6g0yABqkrwj'
}
};
// Send email using the existing email utility
await sendEmail({
from: 'Port Nimara CRM <noreply@portnimara.com>',
to: email.to,
subject: email.subject,
html: email.html
}, credentials);
}