This commit is contained in:
@@ -11,6 +11,17 @@
|
||||
* SMTP_PASS — App Password
|
||||
* SMTP_FROM — e.g. "PieCed <noreply@pieced.ch>"
|
||||
* ADMIN_NOTIFICATION_EMAIL — e.g. admin@pieced.ch (optional)
|
||||
* SUPPORT_CONTACT_EMAIL — e.g. support@pieced.ch (optional)
|
||||
* Customer-facing address for "have
|
||||
* questions?" follow-ups in
|
||||
* transactional emails. The from
|
||||
* address itself (SMTP_USER) is
|
||||
* typically a noreply mailbox, so we
|
||||
* don't tell customers to "reply to
|
||||
* this email" — instead we point them
|
||||
* at this monitored address. If
|
||||
* unset, the contact-prompt line is
|
||||
* simply omitted from emails.
|
||||
*/
|
||||
|
||||
import nodemailer from "nodemailer";
|
||||
@@ -42,6 +53,12 @@ function getFrom(): string {
|
||||
);
|
||||
}
|
||||
|
||||
/** Returns the customer-facing support email address, or null if unset. */
|
||||
function getSupportContactEmail(): string | null {
|
||||
const v = process.env.SUPPORT_CONTACT_EMAIL?.trim();
|
||||
return v && v.length > 0 ? v : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape HTML entities to prevent injection in HTML emails.
|
||||
*/
|
||||
@@ -125,6 +142,21 @@ export async function sendRejectionEmail(
|
||||
</div>`
|
||||
: "";
|
||||
|
||||
const supportEmail = getSupportContactEmail();
|
||||
// The customer here is rejected pre-onboarding — they don't yet
|
||||
// have a portal account, so we can't send them to /support.
|
||||
// Instead point at the configured support address (if set).
|
||||
// If unset (e.g. early pilot before a support inbox exists), we
|
||||
// omit the follow-up line entirely rather than promise something
|
||||
// that goes nowhere — telling the customer to "reply to this
|
||||
// email" would be misleading because we send from a noreply box.
|
||||
const contactLineText = supportEmail
|
||||
? `If you have questions or would like to discuss this further, please contact us at ${supportEmail}.`
|
||||
: "";
|
||||
const contactLineHtml = supportEmail
|
||||
? `<p>If you have questions or would like to discuss this further, please contact us at <a href="mailto:${escapeHtml(supportEmail)}" style="color: #3b82f6;">${escapeHtml(supportEmail)}</a>.</p>`
|
||||
: "";
|
||||
|
||||
await getTransporter().sendMail({
|
||||
from: getFrom(),
|
||||
to,
|
||||
@@ -134,18 +166,20 @@ export async function sendRejectionEmail(
|
||||
"",
|
||||
`Thank you for your interest in PieCed IT. Unfortunately, we were unable to approve your onboarding request for ${companyName} at this time.`,
|
||||
notesBlock,
|
||||
"If you have questions or would like to discuss this further, please reply to this email.",
|
||||
contactLineText,
|
||||
"",
|
||||
"Best regards,",
|
||||
"PieCed IT",
|
||||
].join("\n"),
|
||||
]
|
||||
.filter((s) => s !== "")
|
||||
.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 onboarding request</h2>
|
||||
<p>Hello ${safeName},</p>
|
||||
<p>Thank you for your interest in PieCed IT. Unfortunately, we were unable to approve your onboarding request for <strong>${safeCompany}</strong> at this time.</p>
|
||||
${notesHtml}
|
||||
<p>If you have questions or would like to discuss this further, please reply to this email.</p>
|
||||
${contactLineHtml}
|
||||
<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>
|
||||
@@ -237,6 +271,15 @@ export async function sendResumeRejectionEmail(
|
||||
</div>`
|
||||
: "";
|
||||
|
||||
// The customer has portal access (their tenant exists, they
|
||||
// just had a resume request rejected), so direct them to the
|
||||
// support ticket system for follow-up. We never tell them to
|
||||
// "reply to this email" because the from address is a noreply
|
||||
// mailbox.
|
||||
const contactLineText =
|
||||
"If you have questions, open a support ticket at https://app.pieced.ch/support.";
|
||||
const contactLineHtml = `<p>If you have questions, <a href="https://app.pieced.ch/support" style="color: #3b82f6;">open a support ticket</a>.</p>`;
|
||||
|
||||
await getTransporter().sendMail({
|
||||
from: getFrom(),
|
||||
to,
|
||||
@@ -248,7 +291,7 @@ export async function sendResumeRejectionEmail(
|
||||
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.",
|
||||
contactLineText,
|
||||
"",
|
||||
"Best regards,",
|
||||
"PieCed IT",
|
||||
@@ -260,7 +303,7 @@ export async function sendResumeRejectionEmail(
|
||||
<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>
|
||||
${contactLineHtml}
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user