- 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.
257 lines
7.1 KiB
JavaScript
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 };
|
|
}
|
|
}
|