Fixes localisation of {EVENT_DATE} in workflows (#6907)

* translate {EVENT_DATE} variable to correct language

* fix locale for cron schedule reminder emails/sms

* fix type error

* add missing locale to attendees

* fix type error

* code clean up

* revert last commit

* using Intl for date translations

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
This commit is contained in:
Carina Wollendorfer 2023-02-08 12:06:25 -05:00 committed by GitHub
parent a4b7f1af65
commit b3d8d7e944
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 120 additions and 79 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 || "")

View File

@ -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,