diff --git a/server/utils/resend.js b/server/utils/resend.js index ce79e94..528ab94 100644 --- a/server/utils/resend.js +++ b/server/utils/resend.js @@ -2,6 +2,29 @@ import { Resend } from "resend"; const resend = new Resend(useRuntimeConfig().resendApiKey); +const formatEventDate = (dateString, timeZone = "America/Toronto") => { + const date = new Date(dateString); + return new Intl.DateTimeFormat("en-US", { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + timeZone, + }).format(date); +}; + +const formatEventTime = (startDate, endDate, timeZone = "America/Toronto") => { + const start = new Date(startDate); + const end = new Date(endDate); + const timeFormat = new Intl.DateTimeFormat("en-US", { + hour: "numeric", + minute: "2-digit", + timeZoneName: "short", + timeZone, + }); + return `${timeFormat.format(start)} - ${timeFormat.format(end)}`; +}; + /** * Send event registration confirmation email * @param {Object} options - { requiresSignIn?: boolean } — when true, appends a @@ -9,29 +32,7 @@ const resend = new Resend(useRuntimeConfig().resendApiKey); * registered via the public form and did not receive an auto-login cookie. */ export async function sendEventRegistrationEmail(registration, eventData, 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 tz = eventData.displayTimezone || "America/Toronto"; const baseUrl = process.env.BASE_URL || "https://ghostguild.org"; const eventUrl = `${baseUrl}/events/${eventData.slug || eventData._id}`; const signInSection = options.requiresSignIn @@ -66,8 +67,8 @@ Paid: $${registration.amountPaid.toFixed(2)} CAD`; You're now registered for ${eventData.title}. -Date: ${formatDate(eventData.startDate)} -Time: ${formatTime(eventData.startDate, eventData.endDate)} +Date: ${formatEventDate(eventData.startDate, tz)} +Time: ${formatEventTime(eventData.startDate, eventData.endDate, tz)} Location: ${eventData.location} ${eventData.description ? `\n${eventData.description}\n` : ""}${ticketSection}${signInSection} View event: ${eventUrl} @@ -124,29 +125,7 @@ ${baseUrl}/events`, * 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 tz = eventData.displayTimezone || "America/Toronto"; const baseUrl = process.env.BASE_URL || "https://ghostguild.org"; const eventUrl = `${baseUrl}/events/${eventData.slug || eventData._id}`; @@ -159,8 +138,8 @@ export async function sendWaitlistNotificationEmail(waitlistEntry, eventData) { A spot opened up for ${eventData.title}. -Date: ${formatDate(eventData.startDate)} -Time: ${formatTime(eventData.startDate, eventData.endDate)} +Date: ${formatEventDate(eventData.startDate, tz)} +Time: ${formatEventTime(eventData.startDate, eventData.endDate, tz)} Location: ${eventData.location} Register now: ${eventUrl} @@ -186,29 +165,6 @@ If you can no longer attend, ignore this email and the spot goes to the next per 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", { @@ -223,13 +179,13 @@ export async function sendSeriesPassConfirmation(options) { : ""; const eventList = events - .map( - (evt, index) => - ` ${index + 1}. ${evt.title} - ${formatDate(evt.startDate)} - ${formatTime(evt.startDate, evt.endDate)} - ${evt.location}`, - ) + .map((evt, index) => { + const tz = evt.displayTimezone || "America/Toronto"; + return ` ${index + 1}. ${evt.title} + ${formatEventDate(evt.startDate, tz)} + ${formatEventTime(evt.startDate, evt.endDate, tz)} + ${evt.location}`; + }) .join("\n\n"); try {