diff --git a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts index 13848ca866..2a69187593 100644 --- a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts @@ -14,7 +14,7 @@ import { } from "@calcom/prisma/enums"; import { bookingMetadataSchema } from "@calcom/prisma/zod-utils"; -import type { BookingInfo, timeUnitLowerCase } from "./smsReminderManager"; +import type { AttendeeInBookingInfo, BookingInfo, timeUnitLowerCase } from "./smsReminderManager"; import type { VariablesType } from "./templates/customTemplate"; import customTemplate from "./templates/customTemplate"; import emailReminderTemplate from "./templates/emailReminderTemplate"; @@ -42,10 +42,15 @@ async function getBatchId() { return batchIdResponse[1].batch_id as string; } +type ScheduleEmailReminderAction = Extract< + WorkflowActions, + "EMAIL_HOST" | "EMAIL_ATTENDEE" | "EMAIL_ADDRESS" +>; + export const scheduleEmailReminder = async ( evt: BookingInfo, triggerEvent: WorkflowTriggerEvents, - action: WorkflowActions, + action: ScheduleEmailReminderAction, timeSpan: { time: number | null; timeUnit: TimeUnit | null; @@ -79,20 +84,49 @@ export const scheduleEmailReminder = async ( const sandboxMode = process.env.NEXT_PUBLIC_IS_E2E ? true : false; + let attendeeEmailToBeUsedInMail: string | null = null; + let attendeeToBeUsedInMail: AttendeeInBookingInfo | null = null; let name = ""; let attendeeName = ""; let timeZone = ""; switch (action) { case WorkflowActions.EMAIL_HOST: + attendeeToBeUsedInMail = evt.attendees[0]; name = evt.organizer.name; - attendeeName = evt.attendees[0].name; + attendeeName = attendeeToBeUsedInMail.name; timeZone = evt.organizer.timeZone; break; case WorkflowActions.EMAIL_ATTENDEE: - name = evt.attendees[0].name; + //These type checks are required as sendTo is of type MailData["to"] which in turn is of string | {name?:string, email: string} | string | {name?:string, email: string}[0] + // and the email is being sent to the first attendee of event by default instead of the sendTo + // so check if first attendee can be extracted from sendTo -> attendeeEmailToBeUsedInMail + if (typeof sendTo === "string") { + attendeeEmailToBeUsedInMail = sendTo; + } else if (Array.isArray(sendTo)) { + // If it's an array, take the first entry (if it exists) and extract name and email (if object); otherwise, just put the email (if string) + const emailData = sendTo[0]; + if (typeof emailData === "object" && emailData !== null) { + const { name, email } = emailData; + attendeeEmailToBeUsedInMail = email; + } else if (typeof emailData === "string") { + attendeeEmailToBeUsedInMail = emailData; + } + } else if (typeof sendTo === "object" && sendTo !== null) { + const { name, email } = sendTo; + attendeeEmailToBeUsedInMail = email; + } + + // check if first attendee of sendTo is present in the attendees list, if not take the evt attendee + const attendeeEmailToBeUsedInMailFromEvt = evt.attendees.find( + (attendee) => attendee.email === attendeeEmailToBeUsedInMail + ); + attendeeToBeUsedInMail = attendeeEmailToBeUsedInMailFromEvt + ? attendeeEmailToBeUsedInMailFromEvt + : evt.attendees[0]; + name = attendeeToBeUsedInMail.name; attendeeName = evt.organizer.name; - timeZone = evt.attendees[0].timeZone; + timeZone = attendeeToBeUsedInMail.timeZone; break; } @@ -104,10 +138,10 @@ export const scheduleEmailReminder = async ( const variables: VariablesType = { eventName: evt.title || "", organizerName: evt.organizer.name, - attendeeName: evt.attendees[0].name, - attendeeFirstName: evt.attendees[0].firstName, - attendeeLastName: evt.attendees[0].lastName, - attendeeEmail: evt.attendees[0].email, + attendeeName: attendeeToBeUsedInMail.name, + attendeeFirstName: attendeeToBeUsedInMail.firstName, + attendeeLastName: attendeeToBeUsedInMail.lastName, + attendeeEmail: attendeeToBeUsedInMail.email, eventDate: dayjs(startTime).tz(timeZone), eventEndTime: dayjs(endTime).tz(timeZone), timeZone: timeZone, @@ -120,8 +154,8 @@ export const scheduleEmailReminder = async ( }; const locale = - action === WorkflowActions.EMAIL_ATTENDEE || action === WorkflowActions.SMS_ATTENDEE - ? evt.attendees[0].language?.locale + action === WorkflowActions.EMAIL_ATTENDEE + ? attendeeToBeUsedInMail.language?.locale : evt.organizer.language.locale; const emailSubjectTemplate = customTemplate(emailSubject, variables, locale, evt.organizer.timeFormat); diff --git a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts index 157e3cdc64..fbfb2c0356 100644 --- a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts @@ -22,16 +22,18 @@ export enum timeUnitLowerCase { } const log = logger.getChildLogger({ prefix: ["[smsReminderManager]"] }); +export type AttendeeInBookingInfo = { + name: string; + firstName?: string; + lastName?: string; + email: string; + timeZone: string; + language: { locale: string }; +}; + export type BookingInfo = { uid?: string | null; - attendees: { - name: string; - firstName?: string; - lastName?: string; - email: string; - timeZone: string; - language: { locale: string }; - }[]; + attendees: AttendeeInBookingInfo[]; organizer: { language: { locale: string }; name: string; @@ -52,11 +54,13 @@ export type BookingInfo = { metadata?: Prisma.JsonValue; }; +type ScheduleSMSReminderAction = Extract; + export const scheduleSMSReminder = async ( evt: BookingInfo, reminderPhone: string | null, triggerEvent: WorkflowTriggerEvents, - action: WorkflowActions, + action: ScheduleSMSReminderAction, timeSpan: { time: number | null; timeUnit: TimeUnit | null; @@ -93,30 +97,42 @@ export const scheduleSMSReminder = async ( } const isNumberVerified = await getIsNumberVerified(); + let attendeeToBeUsedInSMS: AttendeeInBookingInfo | null = null; + if (action === WorkflowActions.SMS_ATTENDEE) { + const attendeeWithReminderPhoneAsSMSReminderNumber = + reminderPhone && evt.attendees.find((attendee) => attendee.email === evt.responses?.email?.value); + attendeeToBeUsedInSMS = attendeeWithReminderPhoneAsSMSReminderNumber + ? attendeeWithReminderPhoneAsSMSReminderNumber + : evt.attendees[0]; + } else { + attendeeToBeUsedInSMS = evt.attendees[0]; + } + if (triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT) { scheduledDate = timeSpan.time && timeUnit ? dayjs(startTime).subtract(timeSpan.time, timeUnit) : null; } else if (triggerEvent === WorkflowTriggerEvents.AFTER_EVENT) { scheduledDate = timeSpan.time && timeUnit ? dayjs(endTime).add(timeSpan.time, timeUnit) : null; } - const name = action === WorkflowActions.SMS_ATTENDEE ? evt.attendees[0].name : ""; - const attendeeName = action === WorkflowActions.SMS_ATTENDEE ? evt.organizer.name : evt.attendees[0].name; + const name = action === WorkflowActions.SMS_ATTENDEE ? attendeeToBeUsedInSMS.name : ""; + const attendeeName = + action === WorkflowActions.SMS_ATTENDEE ? evt.organizer.name : attendeeToBeUsedInSMS.name; const timeZone = - action === WorkflowActions.SMS_ATTENDEE ? evt.attendees[0].timeZone : evt.organizer.timeZone; + action === WorkflowActions.SMS_ATTENDEE ? attendeeToBeUsedInSMS.timeZone : evt.organizer.timeZone; const locale = - action === WorkflowActions.EMAIL_ATTENDEE || action === WorkflowActions.SMS_ATTENDEE - ? evt.attendees[0].language?.locale + action === WorkflowActions.SMS_ATTENDEE + ? attendeeToBeUsedInSMS.language?.locale : evt.organizer.language.locale; if (message) { const variables: VariablesType = { eventName: evt.title, organizerName: evt.organizer.name, - attendeeName: evt.attendees[0].name, - attendeeFirstName: evt.attendees[0].firstName, - attendeeLastName: evt.attendees[0].lastName, - attendeeEmail: evt.attendees[0].email, + attendeeName: attendeeToBeUsedInSMS.name, + attendeeFirstName: attendeeToBeUsedInSMS.firstName, + attendeeLastName: attendeeToBeUsedInSMS.lastName, + attendeeEmail: attendeeToBeUsedInSMS.email, eventDate: dayjs(evt.startTime).tz(timeZone), eventEndTime: dayjs(evt.endTime).tz(timeZone), timeZone: timeZone,