Show suspended since and new emails for suspend continue approve and rejection
All checks were successful
Build and Push / build (push) Successful in 1m26s
All checks were successful
Build and Push / build (push) Successful in 1m26s
This commit is contained in:
115
src/lib/email.ts
115
src/lib/email.ts
@@ -156,6 +156,121 @@ export async function sendRejectionEmail(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug 37a: separate email for resume request approval. The tenant
|
||||
* already exists; the message is "we're un-suspending it" rather than
|
||||
* "we're provisioning a new instance". Avoids confusing the customer
|
||||
* with onboarding language for a tenant they already had.
|
||||
*/
|
||||
export async function sendResumeApprovalEmail(
|
||||
to: string,
|
||||
contactName: string,
|
||||
companyName: string
|
||||
): Promise<void> {
|
||||
const safeName = escapeHtml(contactName);
|
||||
const safeCompany = escapeHtml(companyName);
|
||||
|
||||
try {
|
||||
await getTransporter().sendMail({
|
||||
from: getFrom(),
|
||||
to,
|
||||
subject: `Your PieCed AI assistant has been reactivated — ${companyName}`,
|
||||
text: [
|
||||
`Hello ${contactName},`,
|
||||
"",
|
||||
`Good news — your reactivation request for ${companyName} has been approved.`,
|
||||
"",
|
||||
"Your AI assistant is being brought back online and should be ready in a few minutes.",
|
||||
"You can check the status in your dashboard at https://app.pieced.ch",
|
||||
"",
|
||||
"Best regards,",
|
||||
"PieCed IT",
|
||||
].join("\n"),
|
||||
html: `
|
||||
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 560px; margin: 0 auto; color: #e0e0e0; background: #1a1a1a; padding: 32px; border-radius: 12px;">
|
||||
<h2 style="color: #ffffff; margin-top: 0;">Your AI assistant has been reactivated</h2>
|
||||
<p>Hello ${safeName},</p>
|
||||
<p>Good news — your reactivation request for <strong>${safeCompany}</strong> has been approved.</p>
|
||||
<p>Your AI assistant is being brought back online and should be ready in a few minutes.</p>
|
||||
<p>
|
||||
<a href="https://app.pieced.ch" style="display: inline-block; padding: 10px 24px; background: #3b82f6; color: #ffffff; text-decoration: none; border-radius: 8px; font-weight: 500;">
|
||||
Go to Dashboard
|
||||
</a>
|
||||
</p>
|
||||
<hr style="border: none; border-top: 1px solid #333; margin: 24px 0;" />
|
||||
<p style="color: #666; font-size: 12px;">PieCed IT — Hosted on-premises in Switzerland</p>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Failed to send resume approval email:", err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bug 37a: separate email for resume request rejection. Differs from
|
||||
* the onboarding rejection in two ways: it explicitly mentions the
|
||||
* tenant remains suspended, and it points the customer to the
|
||||
* 60-day retention window so they understand the deletion clock is
|
||||
* still ticking. The latter is important — a customer reading a
|
||||
* generic "request rejected" email might not realise their data is
|
||||
* still on a countdown.
|
||||
*/
|
||||
export async function sendResumeRejectionEmail(
|
||||
to: string,
|
||||
contactName: string,
|
||||
companyName: string,
|
||||
adminNotes?: string
|
||||
): Promise<void> {
|
||||
const safeName = escapeHtml(contactName);
|
||||
const safeCompany = escapeHtml(companyName);
|
||||
const safeNotes = adminNotes ? escapeHtml(adminNotes) : "";
|
||||
|
||||
try {
|
||||
const notesBlock = adminNotes
|
||||
? `\nNote from our team:\n${adminNotes}\n`
|
||||
: "";
|
||||
const notesHtml = safeNotes
|
||||
? `<div style="background: #2a2a2a; border-left: 3px solid #ef4444; padding: 12px 16px; border-radius: 6px; margin: 16px 0;">
|
||||
<p style="color: #ccc; font-size: 13px; margin: 0;"><strong>Note from our team:</strong></p>
|
||||
<p style="color: #aaa; font-size: 13px; margin: 8px 0 0 0;">${safeNotes}</p>
|
||||
</div>`
|
||||
: "";
|
||||
|
||||
await getTransporter().sendMail({
|
||||
from: getFrom(),
|
||||
to,
|
||||
subject: `Update on your reactivation request — ${companyName}`,
|
||||
text: [
|
||||
`Hello ${contactName},`,
|
||||
"",
|
||||
`Thank you for your reactivation request for ${companyName}. Unfortunately, we were unable to approve it at this time.`,
|
||||
notesBlock,
|
||||
"Your tenant remains suspended. As a reminder, your data is preserved for 60 days from the original cancellation date, after which it will be permanently deleted. You can submit a new reactivation request at any time before then.",
|
||||
"",
|
||||
"If you have questions, please reply to this email.",
|
||||
"",
|
||||
"Best regards,",
|
||||
"PieCed IT",
|
||||
].join("\n"),
|
||||
html: `
|
||||
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 560px; margin: 0 auto; color: #e0e0e0; background: #1a1a1a; padding: 32px; border-radius: 12px;">
|
||||
<h2 style="color: #ffffff; margin-top: 0;">Update on your reactivation request</h2>
|
||||
<p>Hello ${safeName},</p>
|
||||
<p>Thank you for your reactivation request for <strong>${safeCompany}</strong>. Unfortunately, we were unable to approve it at this time.</p>
|
||||
${notesHtml}
|
||||
<p>Your tenant remains suspended. As a reminder, your data is preserved for 60 days from the original cancellation date, after which it will be permanently deleted. You can submit a new reactivation request at any time before then.</p>
|
||||
<p>If you have questions, please reply to this email.</p>
|
||||
<hr style="border: none; border-top: 1px solid #333; margin: 24px 0;" />
|
||||
<p style="color: #666; font-size: 12px;">PieCed IT — Hosted on-premises in Switzerland</p>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Failed to send resume rejection email:", err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendAdminNotificationEmail(
|
||||
companyName: string,
|
||||
contactName: string,
|
||||
|
||||
Reference in New Issue
Block a user