diff --git a/packages/features/ee/workflows/api/scheduleEmailReminders.ts b/packages/features/ee/workflows/api/scheduleEmailReminders.ts index c4da9148d1..b1ef72f1f4 100644 --- a/packages/features/ee/workflows/api/scheduleEmailReminders.ts +++ b/packages/features/ee/workflows/api/scheduleEmailReminders.ts @@ -95,6 +95,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { ? reminder.booking?.attendees[0].timeZone : reminder.booking?.user?.timeZone; + const locale = + reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE || + reminder.workflowStep.action === WorkflowActions.SMS_ATTENDEE + ? reminder.booking?.attendees[0].locale + : reminder.booking?.user?.locale; + let emailContent = { emailSubject: reminder.workflowStep.emailSubject || "", emailBody: { @@ -131,13 +137,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { const emailSubject = await customTemplate( reminder.workflowStep.emailSubject || "", variables, - reminder.booking?.user?.locale || "" + locale || "" ); emailContent.emailSubject = emailSubject.text; emailContent.emailBody = await customTemplate( reminder.workflowStep.reminderBody || "", variables, - reminder.booking?.user?.locale || "" + locale || "" ); break; } diff --git a/packages/features/ee/workflows/api/scheduleSMSReminders.ts b/packages/features/ee/workflows/api/scheduleSMSReminders.ts index 0b31d01946..97b135dab4 100644 --- a/packages/features/ee/workflows/api/scheduleSMSReminders.ts +++ b/packages/features/ee/workflows/api/scheduleSMSReminders.ts @@ -76,6 +76,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { const senderID = getSenderId(sendTo, reminder.workflowStep.sender); + const locale = + reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE || + reminder.workflowStep.action === WorkflowActions.SMS_ATTENDEE + ? reminder.booking?.attendees[0].locale + : reminder.booking?.user?.locale; + let message: string | null = reminder.workflowStep.reminderBody; switch (reminder.workflowStep.template) { case WorkflowTemplates.REMINDER: @@ -104,7 +110,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { const customMessage = await customTemplate( reminder.workflowStep.reminderBody || "", variables, - reminder.booking?.user?.locale || "" + locale || "" ); message = customMessage.text; break; diff --git a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts index 9a79bde934..dad511401f 100644 --- a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts @@ -108,13 +108,14 @@ export const scheduleEmailReminder = async ( meetingUrl: bookingMetadataSchema.parse(evt.metadata || {})?.videoCallUrl, }; - const emailSubjectTemplate = await customTemplate( - emailSubject, - variables, - evt.organizer.language.locale - ); + const locale = + action === WorkflowActions.EMAIL_ATTENDEE || action === WorkflowActions.SMS_ATTENDEE + ? evt.attendees[0].language?.locale + : evt.organizer.language.locale; + + const emailSubjectTemplate = await customTemplate(emailSubject, variables, locale); emailContent.emailSubject = emailSubjectTemplate.text; - emailContent.emailBody = await customTemplate(emailBody, variables, evt.organizer.language.locale); + emailContent.emailBody = await customTemplate(emailBody, variables, locale); break; } diff --git a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts index 52911616a9..b79dd391c6 100644 --- a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts @@ -10,6 +10,7 @@ import dayjs from "@calcom/dayjs"; import prisma from "@calcom/prisma"; import { Prisma } from "@calcom/prisma/client"; import { bookingMetadataSchema } from "@calcom/prisma/zod-utils"; +import { Person } from "@calcom/types/Calendar"; import { getSenderId } from "../alphanumericSenderIdSupport"; import * as twilio from "./smsProviders/twilioProvider"; @@ -24,7 +25,7 @@ export enum timeUnitLowerCase { export type BookingInfo = { uid?: string | null; - attendees: { name: string; email: string; timeZone: string }[]; + attendees: { name: string; email: string; timeZone: string; language: { locale: string } }[]; organizer: { language: { locale: string }; name: string; @@ -87,6 +88,11 @@ export const scheduleSMSReminder = async ( const timeZone = action === WorkflowActions.SMS_ATTENDEE ? evt.attendees[0].timeZone : evt.organizer.timeZone; + const locale = + action === WorkflowActions.EMAIL_ATTENDEE || action === WorkflowActions.SMS_ATTENDEE + ? evt.attendees[0].language?.locale + : evt.organizer.language.locale; + switch (template) { case WorkflowTemplates.REMINDER: message = smsReminderTemplate(evt.startTime, evt.title, timeZone, attendeeName, name) || message; @@ -105,7 +111,7 @@ export const scheduleSMSReminder = async ( customInputs: evt.customInputs, meetingUrl: bookingMetadataSchema.parse(evt.metadata || {})?.videoCallUrl, }; - const customMessage = await customTemplate(message, variables, evt.organizer.language.locale); + const customMessage = await customTemplate(message, variables, locale); message = customMessage.text; break; } diff --git a/packages/features/ee/workflows/lib/reminders/templates/customTemplate.ts b/packages/features/ee/workflows/lib/reminders/templates/customTemplate.ts index 3ee20405ed..ec86a84ae7 100644 --- a/packages/features/ee/workflows/lib/reminders/templates/customTemplate.ts +++ b/packages/features/ee/workflows/lib/reminders/templates/customTemplate.ts @@ -17,7 +17,14 @@ export type VariablesType = { }; const customTemplate = async (text: string, variables: VariablesType, locale: string) => { - const timeWithTimeZone = `${variables.eventTime?.locale(locale).format("HH:mm")} (${variables.timeZone})`; + const translatedDate = new Intl.DateTimeFormat(locale, { + weekday: "long", + month: "long", + day: "numeric", + year: "numeric", + }).format(variables.eventDate?.toDate()); + + const timeWithTimeZone = `${variables.eventTime?.format("HH:mm")} (${variables.timeZone})`; let locationString = variables.location || ""; if (text.includes("{LOCATION}")) { @@ -30,7 +37,7 @@ const customTemplate = async (text: string, variables: VariablesType, locale: st .replaceAll("{ATTENDEE}", variables.attendeeName || "") .replaceAll("{ORGANIZER_NAME}", variables.organizerName || "") //old variable names .replaceAll("{ATTENDEE_NAME}", variables.attendeeName || "") //old variable names - .replaceAll("{EVENT_DATE}", variables.eventDate?.locale(locale).format("dddd, MMMM D, YYYY") || "") + .replaceAll("{EVENT_DATE}", translatedDate) .replaceAll("{EVENT_TIME}", timeWithTimeZone) .replaceAll("{LOCATION}", locationString) .replaceAll("{ADDITIONAL_NOTES}", variables.additionalNotes || "") diff --git a/packages/trpc/server/routers/viewer/workflows.tsx b/packages/trpc/server/routers/viewer/workflows.tsx index a3d07c11ef..98dc3a2351 100644 --- a/packages/trpc/server/routers/viewer/workflows.tsx +++ b/packages/trpc/server/routers/viewer/workflows.tsx @@ -433,7 +433,12 @@ export const workflowsRouter = router({ const bookingInfo = { uid: booking.uid, attendees: booking.attendees.map((attendee) => { - return { name: attendee.name, email: attendee.email, timeZone: attendee.timeZone }; + return { + name: attendee.name, + email: attendee.email, + timeZone: attendee.timeZone, + language: { locale: attendee.locale || "" }, + }; }), organizer: booking.user ? { @@ -625,7 +630,12 @@ export const workflowsRouter = router({ const bookingInfo = { uid: booking.uid, attendees: booking.attendees.map((attendee) => { - return { name: attendee.name, email: attendee.email, timeZone: attendee.timeZone }; + return { + name: attendee.name, + email: attendee.email, + timeZone: attendee.timeZone, + language: { locale: attendee.locale || "" }, + }; }), organizer: booking.user ? { @@ -746,7 +756,12 @@ export const workflowsRouter = router({ const bookingInfo = { uid: booking.uid, attendees: booking.attendees.map((attendee) => { - return { name: attendee.name, email: attendee.email, timeZone: attendee.timeZone }; + return { + name: attendee.name, + email: attendee.email, + timeZone: attendee.timeZone, + language: { locale: attendee.locale || "" }, + }; }), organizer: booking.user ? { @@ -903,72 +918,72 @@ export const workflowsRouter = router({ } if (isSMSAction(step.action) /*|| step.action === WorkflowActions.EMAIL_ADDRESS*/ /*) { - const hasTeamPlan = (await ctx.prisma.membership.count({ where: { userId: user.id } })) > 0; - if (!hasTeamPlan) { - throw new TRPCError({ code: "UNAUTHORIZED", message: "Team plan needed" }); - } - } +const hasTeamPlan = (await ctx.prisma.membership.count({ where: { userId: user.id } })) > 0; +if (!hasTeamPlan) { + throw new TRPCError({ code: "UNAUTHORIZED", message: "Team plan needed" }); +} +} - const booking = await ctx.prisma.booking.findFirst({ - orderBy: { - createdAt: "desc", - }, - where: { - userId: ctx.user.id, - }, - include: { - attendees: true, - user: true, - }, - }); +const booking = await ctx.prisma.booking.findFirst({ +orderBy: { + createdAt: "desc", +}, +where: { + userId: ctx.user.id, +}, +include: { + attendees: true, + user: true, +}, +}); - let evt: BookingInfo; - if (booking) { - evt = { - uid: booking?.uid, - attendees: - booking?.attendees.map((attendee) => { - return { name: attendee.name, email: attendee.email, timeZone: attendee.timeZone }; - }) || [], - organizer: { - language: { - locale: booking?.user?.locale || "", - }, - name: booking?.user?.name || "", - email: booking?.user?.email || "", - timeZone: booking?.user?.timeZone || "", - }, - startTime: booking?.startTime.toISOString() || "", - endTime: booking?.endTime.toISOString() || "", - title: booking?.title || "", - location: booking?.location || null, - additionalNotes: booking?.description || null, - customInputs: booking?.customInputs, - }; - } else { - //if no booking exists create an example booking - evt = { - attendees: [{ name: "John Doe", email: "john.doe@example.com", timeZone: "Europe/London" }], - organizer: { - language: { - locale: ctx.user.locale, - }, - name: ctx.user.name || "", - email: ctx.user.email, - timeZone: ctx.user.timeZone, - }, - startTime: dayjs().add(10, "hour").toISOString(), - endTime: dayjs().add(11, "hour").toISOString(), - title: "Example Booking", - location: "Office", - additionalNotes: "These are additional notes", - }; - } +let evt: BookingInfo; +if (booking) { +evt = { + uid: booking?.uid, + attendees: + booking?.attendees.map((attendee) => { + return { name: attendee.name, email: attendee.email, timeZone: attendee.timeZone }; + }) || [], + organizer: { + language: { + locale: booking?.user?.locale || "", + }, + name: booking?.user?.name || "", + email: booking?.user?.email || "", + timeZone: booking?.user?.timeZone || "", + }, + startTime: booking?.startTime.toISOString() || "", + endTime: booking?.endTime.toISOString() || "", + title: booking?.title || "", + location: booking?.location || null, + additionalNotes: booking?.description || null, + customInputs: booking?.customInputs, +}; +} else { +//if no booking exists create an example booking +evt = { + attendees: [{ name: "John Doe", email: "john.doe@example.com", timeZone: "Europe/London" }], + organizer: { + language: { + locale: ctx.user.locale, + }, + name: ctx.user.name || "", + email: ctx.user.email, + timeZone: ctx.user.timeZone, + }, + startTime: dayjs().add(10, "hour").toISOString(), + endTime: dayjs().add(11, "hour").toISOString(), + title: "Example Booking", + location: "Office", + additionalNotes: "These are additional notes", +}; +} - if ( - action === WorkflowActions.EMAIL_ATTENDEE || - action === WorkflowActions.EMAIL_HOST /*|| - action === WorkflowActions.EMAIL_ADDRESS*/ +if ( +action === WorkflowActions.EMAIL_ATTENDEE || +action === WorkflowActions.EMAIL_HOST /*|| +action === WorkflowActions.EMAIL_ADDRESS*/ /*) { scheduleEmailReminder( evt,