ghostguild-org/server/utils/resend.js
Jennie Robinson Faber c3c8b6bcd4 Refactor email templates to use plain text format and update sender addresses
- Simplified the magic link email format to plain text for better compatibility.
- Updated the welcome email to use plain text and changed the sender address to match the domain.
- Enhanced event registration email format to plain text, removing HTML styling for a cleaner approach.
2026-03-05 18:40:37 +00:00

257 lines
7.1 KiB
JavaScript

import { Resend } from "resend";
const resend = new Resend(process.env.RESEND_API_KEY);
/**
* Send event registration confirmation email
*/
export async function sendEventRegistrationEmail(registration, eventData) {
const formatDate = (dateString) => {
const date = new Date(dateString);
return new Intl.DateTimeFormat("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
}).format(date);
};
const formatTime = (startDate, endDate) => {
const start = new Date(startDate);
const end = new Date(endDate);
const timeFormat = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "2-digit",
timeZoneName: "short",
});
return `${timeFormat.format(start)} - ${timeFormat.format(end)}`;
};
const baseUrl = process.env.BASE_URL || "https://ghostguild.org";
const eventUrl = `${baseUrl}/events/${eventData.slug || eventData._id}`;
let ticketSection = "";
if (
registration.ticketType &&
registration.ticketType !== "guest" &&
registration.amountPaid > 0
) {
ticketSection = `\nTicket: ${registration.ticketType === "member" ? "Member Ticket" : "Public Ticket"}
Paid: $${registration.amountPaid.toFixed(2)} CAD`;
if (registration.paymentId) {
ticketSection += `\nTransaction: ${registration.paymentId}`;
}
ticketSection += "\n";
} else if (
registration.ticketType === "member" &&
registration.amountPaid === 0
) {
ticketSection = "\nThis event is free for Ghost Guild members.\n";
}
try {
const { data, error } = await resend.emails.send({
from: "Ghost Guild <events@babyghosts.org>",
to: [registration.email],
subject: `You're registered for ${eventData.title}`,
text: `Hi ${registration.name},
You're registered for ${eventData.title}.
Date: ${formatDate(eventData.startDate)}
Time: ${formatTime(eventData.startDate, eventData.endDate)}
Location: ${eventData.location}
${eventData.description ? `\n${eventData.description}\n` : ""}${ticketSection}
View event: ${eventUrl}
To cancel, visit the event page and click "Cancel Registration."`,
});
if (error) {
console.error("Failed to send registration email:", error);
return { success: false, error };
}
return { success: true, data };
} catch (error) {
console.error("Error sending registration email:", error);
return { success: false, error };
}
}
/**
* Send event cancellation confirmation email
*/
export async function sendEventCancellationEmail(registration, eventData) {
const baseUrl = process.env.BASE_URL || "https://ghostguild.org";
try {
const { data, error } = await resend.emails.send({
from: "Ghost Guild <events@babyghosts.org>",
to: [registration.email],
subject: `Registration cancelled: ${eventData.title}`,
text: `Hi ${registration.name},
Your registration for ${eventData.title} has been cancelled.
You can register again if your plans change:
${baseUrl}/events`,
});
if (error) {
console.error("Failed to send cancellation email:", error);
return { success: false, error };
}
return { success: true, data };
} catch (error) {
console.error("Error sending cancellation email:", error);
return { success: false, error };
}
}
/**
* Send waitlist notification email when a spot opens up
*/
export async function sendWaitlistNotificationEmail(waitlistEntry, eventData) {
const formatDate = (dateString) => {
const date = new Date(dateString);
return new Intl.DateTimeFormat("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
}).format(date);
};
const formatTime = (startDate, endDate) => {
const start = new Date(startDate);
const end = new Date(endDate);
const timeFormat = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "2-digit",
timeZoneName: "short",
});
return `${timeFormat.format(start)} - ${timeFormat.format(end)}`;
};
const baseUrl = process.env.BASE_URL || "https://ghostguild.org";
const eventUrl = `${baseUrl}/events/${eventData.slug || eventData._id}`;
try {
const { data, error } = await resend.emails.send({
from: "Ghost Guild <events@babyghosts.org>",
to: [waitlistEntry.email],
subject: `A spot opened up for ${eventData.title}`,
text: `Hi ${waitlistEntry.name},
A spot opened up for ${eventData.title}.
Date: ${formatDate(eventData.startDate)}
Time: ${formatTime(eventData.startDate, eventData.endDate)}
Location: ${eventData.location}
Register now: ${eventUrl}
If you can no longer attend, ignore this email and the spot goes to the next person.`,
});
if (error) {
console.error("Failed to send waitlist notification email:", error);
return { success: false, error };
}
return { success: true, data };
} catch (error) {
console.error("Error sending waitlist notification email:", error);
return { success: false, error };
}
}
/**
* Send series pass confirmation email
*/
export async function sendSeriesPassConfirmation(options) {
const { to, name, series, ticket, events, paymentId } = options;
const formatDate = (dateString) => {
const date = new Date(dateString);
return new Intl.DateTimeFormat("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
}).format(date);
};
const formatTime = (startDate, endDate) => {
const start = new Date(startDate);
const end = new Date(endDate);
const timeFormat = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "2-digit",
timeZoneName: "short",
});
return `${timeFormat.format(start)} - ${timeFormat.format(end)}`;
};
const formatPrice = (price, currency = "CAD") => {
if (price === 0) return "Free";
return new Intl.NumberFormat("en-CA", {
style: "currency",
currency,
}).format(price);
};
const freeMemberLine =
ticket.isFree && ticket.type === "member"
? "\nThis series pass is free for Ghost Guild members.\n"
: "";
const eventList = events
.map(
(evt, index) =>
` ${index + 1}. ${evt.title}
${formatDate(evt.startDate)}
${formatTime(evt.startDate, evt.endDate)}
${evt.location}`,
)
.join("\n\n");
try {
const { data, error } = await resend.emails.send({
from: "Ghost Guild <events@babyghosts.org>",
to: [to],
subject: `Your series pass for ${series.title}`,
text: `Hi ${name},
Your series pass for ${series.title} is confirmed. You're registered for all ${events.length} events.
${freeMemberLine}
Pass details:
Series: ${series.title}
Type: ${ticket.type === "member" ? "Member Pass" : "Public Pass"}
Paid: ${formatPrice(ticket.price, ticket.currency)}${paymentId ? `\n Transaction: ${paymentId}` : ""}
Events: ${events.length}
Your events:
${eventList}`,
});
if (error) {
console.error("Failed to send series pass confirmation email:", error);
return { success: false, error };
}
return { success: true, data };
} catch (error) {
console.error("Error sending series pass confirmation email:", error);
return { success: false, error };
}
}