Fixes bug that new line is not working in workflow reminder emails (#3452)

* add html format to email template

* remove workflow reminder email

* fix that text was used instead of html

* fixes that email subject was used instead of body

* remove \n\n from text template

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
Carina Wollendorfer 2022-07-21 14:56:20 -04:00 committed by GitHub
parent d3fcb8bf7d
commit c93e238e15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 60 additions and 94 deletions

View File

@ -9,7 +9,6 @@ import client from "@sendgrid/client";
import sgMail from "@sendgrid/mail";
import dayjs from "@calcom/dayjs";
import { sendWorkflowReminderEmail } from "@calcom/emails";
import prisma from "@calcom/prisma";
import { BookingInfo, timeUnitLowerCase } from "@ee/lib/workflows/reminders/smsReminderManager";
import emailReminderTemplate from "@ee/lib/workflows/reminders/templates/emailReminderTemplate";
@ -60,11 +59,17 @@ export const scheduleEmailReminder = async (
const attendeeName = action === WorkflowActions.EMAIL_HOST ? evt.attendees[0].name : evt.organizer.name;
const timeZone = action === WorkflowActions.EMAIL_HOST ? evt.organizer.timeZone : evt.attendees[0].timeZone;
let emailContent = {
emailSubject,
emailBody: {
text: emailBody,
html: `<body style="white-space: pre-wrap;">${emailBody}</body>`,
},
};
switch (template) {
case WorkflowTemplates.REMINDER:
const emailTemplate = emailReminderTemplate(startTime, evt.title, timeZone, attendeeName, name);
emailSubject = emailTemplate.subject;
emailBody = emailTemplate.body;
emailContent = emailReminderTemplate(startTime, evt.title, timeZone, attendeeName, name);
break;
}
@ -73,7 +78,14 @@ export const scheduleEmailReminder = async (
triggerEvent === WorkflowTriggerEvents.EVENT_CANCELLED
) {
try {
await sendWorkflowReminderEmail(evt, sendTo, emailSubject, emailBody);
await sgMail.send({
to: sendTo,
from: senderEmail,
subject: emailContent.emailSubject,
text: emailContent.emailBody.text,
html: emailContent.emailBody.html,
batchId: batchIdResponse[1].batch_id,
});
} catch (error) {
console.log("Error sending Email");
}
@ -88,13 +100,9 @@ export const scheduleEmailReminder = async (
await sgMail.send({
to: sendTo,
from: senderEmail,
subject: emailSubject,
content: [
{
type: "text/html",
value: emailBody,
},
],
subject: emailContent.emailSubject,
text: emailContent.emailBody.text,
html: emailContent.emailBody.html,
batchId: batchIdResponse[1].batch_id,
sendAt: scheduledDate.unix(),
});

View File

@ -7,19 +7,25 @@ const emailReminderTemplate = (
attendee: string,
name: string
) => {
const templateSubject = `Reminder: ${eventName} at ${dayjs(startTime)
const emailSubject = `Reminder: ${eventName} on ${dayjs(startTime)
.tz(timeZone)
.format("YYYY MMM D h:mmA")}`;
.format("YYYY MMM D")} at ${dayjs(startTime).tz(timeZone).format("h:mmA")} ${timeZone}.`;
const templateBody = `Hi ${name},\n\nThis is a reminder that your meeting (${eventName}) with ${attendee} is on ${dayjs(
const templateBodyText = `Hi ${name}, this is a reminder that your meeting (${eventName}) with ${attendee} is on ${dayjs(
startTime
)
.tz(timeZone)
.format("YYYY MMM D")} at ${dayjs(startTime).tz(timeZone).format("h:mmA")} ${timeZone}.`;
const emailContent = { subject: templateSubject, body: templateBody };
const templateBodyHtml = `<body>Hi ${name},<br><br>This is a reminder that your meeting (${eventName}) with ${attendee} is on ${dayjs(
startTime
)
.tz(timeZone)
.format("YYYY MMM D")} at ${dayjs(startTime).tz(timeZone).format("h:mmA")} ${timeZone}.<body>`;
return emailContent;
const emailBody = { text: templateBodyText, html: templateBodyHtml };
return { emailSubject, emailBody };
};
export default emailReminderTemplate;

View File

@ -72,11 +72,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
? reminder.booking?.user?.email
: reminder.booking?.attendees[0].email;
let emailTemplate = {
subject: reminder.workflowStep.emailSubject || "",
body: reminder.workflowStep.reminderBody || "",
};
const name =
reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE
? reminder.booking?.attendees[0].name
@ -92,9 +87,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
? reminder.booking?.attendees[0].timeZone
: reminder.booking?.user?.timeZone;
let emailContent = {
emailSubject: reminder.workflowStep.emailSubject || "",
emailBody: {
text: reminder.workflowStep.reminderBody || "",
html: `<body style="white-space: pre-wrap;">${reminder.workflowStep.reminderBody || ""}</body>`,
},
};
switch (reminder.workflowStep.template) {
case WorkflowTemplates.REMINDER:
emailTemplate = emailReminderTemplate(
emailContent = emailReminderTemplate(
reminder.booking?.startTime.toISOString() || "",
reminder.booking?.eventType?.title || "",
timeZone || "",
@ -103,17 +106,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
);
break;
}
if (emailTemplate.subject.length > 0 && emailTemplate.body.length > 0 && sendTo) {
if (emailContent.emailSubject.length > 0 && emailContent.emailBody.text.length > 0 && sendTo) {
await sgMail.send({
to: sendTo,
from: senderEmail,
subject: emailTemplate.subject,
content: [
{
type: "text/html",
value: emailTemplate.body,
},
],
subject: emailContent.emailSubject,
text: emailContent.emailBody.text,
html: emailContent.emailBody.html,
batchId: batchIdResponse[1].batch_id,
sendAt: dayjs(reminder.scheduledDate).unix(),
});

View File

@ -10,7 +10,6 @@ import AttendeeRescheduledEmail from "./templates/attendee-rescheduled-email";
import AttendeeScheduledEmail from "./templates/attendee-scheduled-email";
import BrokenIntegrationEmail from "./templates/broken-integration-email";
import FeedbackEmail, { Feedback } from "./templates/feedback-email";
import WorkflowReminderEmail from "./templates/workflow-reminder-email";
import ForgotPasswordEmail, { PasswordReset } from "./templates/forgot-password-email";
import OrganizerCancelledEmail from "./templates/organizer-cancelled-email";
import OrganizerLocationChangeEmail from "./templates/organizer-location-change-email";
@ -21,7 +20,6 @@ import OrganizerRequestRescheduleEmail from "./templates/organizer-request-resch
import OrganizerRescheduledEmail from "./templates/organizer-rescheduled-email";
import OrganizerScheduledEmail from "./templates/organizer-scheduled-email";
import TeamInviteEmail, { TeamInvite } from "./templates/team-invite-email";
import { BookingInfo } from "@calcom/web/ee/lib/workflows/reminders/smsReminderManager";
export const sendScheduledEmails = async (calEvent: CalendarEvent) => {
const emailsToSend: Promise<unknown>[] = [];
@ -329,14 +327,3 @@ export const sendBrokenIntegrationEmail = async (evt: CalendarEvent, type: "vide
}
});
};
export const sendWorkflowReminderEmail = async (evt: BookingInfo, sendTo: string, emailSubject: string, emailBody: string) => {
await new Promise((resolve, reject) => {
try {
const workflowReminderEmail = new WorkflowReminderEmail(evt, sendTo, emailSubject, emailBody);
resolve(workflowReminderEmail.sendEmail());
} catch (e) {
reject(console.error("WorkflowReminderEmail.sendEmail failed", e));
}
});
}

View File

@ -1,34 +0,0 @@
import BaseEmail from "./_base-email";
import { BookingInfo } from "@calcom/web/ee/lib/workflows/reminders/smsReminderManager";
export default class WorkflowReminderEmail extends BaseEmail {
sendTo: string;
body: string;
emailSubject: string;
evt: BookingInfo;
constructor(evt: BookingInfo, sendTo: string, emailSubject: string, body: string) {
super();
this.sendTo = sendTo;
this.body = body;
this.evt = evt;
this.emailSubject = emailSubject;
}
protected getNodeMailerPayload(): Record<string, unknown> {
let from ="";
let replyTo ="";
if(this.evt.organizer) {
from = this.evt.organizer.name || "";
replyTo = this.evt.organizer.email;
}
return {
to: `<${this.sendTo}>`,
from: `${from} <${this.getMailerOptions().from}>`,
replyTo: replyTo,
subject: this.emailSubject,
text: this.body,
};
}
}

View File

@ -19,14 +19,14 @@ generator zod {
}
enum SchedulingType {
ROUND_ROBIN @map("roundRobin")
COLLECTIVE @map("collective")
ROUND_ROBIN @map("roundRobin")
COLLECTIVE @map("collective")
}
enum PeriodType {
UNLIMITED @map("unlimited")
ROLLING @map("rolling")
RANGE @map("range")
UNLIMITED @map("unlimited")
ROLLING @map("rolling")
RANGE @map("range")
}
model EventType {
@ -253,10 +253,10 @@ model Attendee {
}
enum BookingStatus {
CANCELLED @map("cancelled")
ACCEPTED @map("accepted")
REJECTED @map("rejected")
PENDING @map("pending")
CANCELLED @map("cancelled")
ACCEPTED @map("accepted")
REJECTED @map("rejected")
PENDING @map("pending")
}
model DailyEventReference {
@ -335,10 +335,10 @@ model SelectedCalendar {
}
enum EventTypeCustomInputType {
TEXT @map("text")
TEXTLONG @map("textLong")
NUMBER @map("number")
BOOL @map("bool")
TEXT @map("text")
TEXTLONG @map("textLong")
NUMBER @map("number")
BOOL @map("bool")
}
model EventTypeCustomInput {
@ -575,9 +575,9 @@ model WorkflowsOnEventTypes {
}
enum TimeUnit {
DAY @map("day")
HOUR @map("hour")
MINUTE @map("minute")
DAY @map("day")
HOUR @map("hour")
MINUTE @map("minute")
}
model WorkflowReminder {