Skip to content

Commit afea724

Browse files
kart1kaanikdhabal
andauthored
fix: improve cancellation email of reschedule RR booking (calcom#17768)
* fix: improve cancellation email of reschedule RR booking * fix failing test * fix: uses same lucky user when rescheduling RR booking also has a fixed host * chore * chore * better message in the cancelled email * fix * fix * add booker rescheduled in reassigned email to old host * test * add reassigned subtite * fix: use same lucky user when reschedule RR booking has a fixed host * fix: test * fix: subtitle --------- Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
1 parent a491518 commit afea724

10 files changed

Lines changed: 107 additions & 44 deletions

File tree

apps/web/test/utils/bookingScenario/expects.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,10 +711,12 @@ export function expectSuccessfulRoundRobinReschedulingEmails({
711711
emails,
712712
newOrganizer,
713713
prevOrganizer,
714+
bookerReschedule,
714715
}: {
715716
emails: Fixtures["emails"];
716717
newOrganizer: { email: string; name: string };
717718
prevOrganizer: { email: string; name: string };
719+
bookerReschedule?: boolean;
718720
}) {
719721
if (newOrganizer !== prevOrganizer) {
720722
vi.waitFor(() => {
@@ -738,6 +740,19 @@ export function expectSuccessfulRoundRobinReschedulingEmails({
738740
`${prevOrganizer.email}`
739741
);
740742
});
743+
744+
// if booking is rescheduled by booker, old organizer should recieve reassigned emails
745+
if (bookerReschedule) {
746+
vi.waitFor(() => {
747+
expect(emails).toHaveEmail(
748+
{
749+
heading: "event_request_reassigned",
750+
to: `${prevOrganizer.email}`,
751+
},
752+
`${prevOrganizer.email}`
753+
);
754+
});
755+
}
741756
} else {
742757
vi.waitFor(() => {
743758
// organizer should receive rescheduled emails

packages/emails/email-manager.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -237,25 +237,44 @@ export const sendRoundRobinCancelledEmailsAndSMS = async (
237237
calEvent: CalendarEvent,
238238
members: Person[],
239239
eventTypeMetadata?: EventTypeMetadata,
240-
reassignedTo?: { name: string | null; email: string }
240+
reassignedTo?: { name: string | null; email: string; reason?: string }
241241
) => {
242242
if (eventTypeDisableHostEmail(eventTypeMetadata)) return;
243243
const calendarEvent = formatCalEvent(calEvent);
244244
const emailsAndSMSToSend: Promise<unknown>[] = [];
245245
const successfullyReScheduledSMS = new EventCancelledSMS(calEvent);
246246
for (const teamMember of members) {
247-
if (!reassignedTo) {
248-
emailsAndSMSToSend.push(
249-
sendEmail(() => new OrganizerCancelledEmail({ calEvent: calendarEvent, teamMember }))
250-
);
251-
} else {
252-
emailsAndSMSToSend.push(
253-
sendEmail(
254-
() =>
255-
new OrganizerReassignedEmail({ calEvent: calendarEvent, teamMember, reassigned: reassignedTo })
256-
)
257-
);
247+
emailsAndSMSToSend.push(
248+
sendEmail(
249+
() => new OrganizerCancelledEmail({ calEvent: calendarEvent, teamMember, reassigned: reassignedTo })
250+
)
251+
);
252+
253+
if (teamMember.phoneNumber) {
254+
emailsAndSMSToSend.push(successfullyReScheduledSMS.sendSMSToAttendee(teamMember));
258255
}
256+
}
257+
258+
await Promise.all(emailsAndSMSToSend);
259+
};
260+
261+
export const sendRoundRobinReassignedEmailsAndSMS = async (args: {
262+
calEvent: CalendarEvent;
263+
members: Person[];
264+
reassignedTo: { name: string | null; email: string };
265+
eventTypeMetadata?: EventTypeMetadata;
266+
}) => {
267+
const { calEvent, members, reassignedTo, eventTypeMetadata } = args;
268+
if (eventTypeDisableHostEmail(eventTypeMetadata)) return;
269+
const calendarEvent = formatCalEvent(calEvent);
270+
const emailsAndSMSToSend: Promise<unknown>[] = [];
271+
const successfullyReScheduledSMS = new EventCancelledSMS(calEvent);
272+
for (const teamMember of members) {
273+
emailsAndSMSToSend.push(
274+
sendEmail(
275+
() => new OrganizerReassignedEmail({ calEvent: calendarEvent, teamMember, reassigned: reassignedTo })
276+
)
277+
);
259278

260279
if (teamMember.phoneNumber) {
261280
emailsAndSMSToSend.push(successfullyReScheduledSMS.sendSMSToAttendee(teamMember));

packages/emails/src/templates/BaseScheduledEmail.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ export const BaseScheduledEmail = (
107107
}
108108
withSpacer
109109
/>
110+
{props.reassigned?.reason && (
111+
<Info label={t("reason")} description={props.reassigned.reason} withSpacer />
112+
)}
110113
</>
111114
)}
112115
{props.reassigned && props.reassigned.byUser && (
Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import { OrganizerScheduledEmail } from "./OrganizerScheduledEmail";
22

3-
export const OrganizerCancelledEmail = (props: React.ComponentProps<typeof OrganizerScheduledEmail>) => (
4-
<OrganizerScheduledEmail
5-
title="event_request_cancelled"
6-
headerType="xCircle"
7-
subject="event_cancelled_subject"
8-
callToAction={null}
9-
{...props}
10-
/>
11-
);
3+
export const OrganizerCancelledEmail = (props: React.ComponentProps<typeof OrganizerScheduledEmail>) => {
4+
const t = props.teamMember?.language.translate || props.calEvent.organizer.language.translate;
5+
const title = props.reassigned ? "event_request_reassigned" : "event_request_cancelled";
6+
const subtitle = props.reassigned ? t("event_reassigned_subtitle") : "";
7+
const subject = props.reassigned ? "event_reassigned_subject" : "event_cancelled_subject";
8+
return (
9+
<OrganizerScheduledEmail
10+
title={title}
11+
subtitle={subtitle}
12+
headerType="xCircle"
13+
subject={subject}
14+
callToAction={null}
15+
reassigned={props.reassigned}
16+
{...props}
17+
/>
18+
);
19+
};

packages/emails/templates/organizer-cancelled-email.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import type { CalendarEvent, Person } from "@calcom/types/Calendar";
44
import { renderEmail } from "../";
55
import generateIcsFile, { GenerateIcsRole } from "../lib/generateIcsFile";
66
import OrganizerScheduledEmail from "./organizer-scheduled-email";
7+
import type { Reassigned } from "./organizer-scheduled-email";
78

89
export default class OrganizerCancelledEmail extends OrganizerScheduledEmail {
910
protected async getNodeMailerPayload(): Promise<Record<string, unknown>> {
1011
const toAddresses = [this.teamMember?.email || this.calEvent.organizer.email];
12+
const subject = this.reassigned ? "event_reassigned_subject" : "event_cancelled_subject";
1113

1214
return {
1315
icalEvent: generateIcsFile({
@@ -17,19 +19,20 @@ export default class OrganizerCancelledEmail extends OrganizerScheduledEmail {
1719
}),
1820
from: `${EMAIL_FROM_NAME} <${this.getMailerOptions().from}>`,
1921
to: toAddresses.join(","),
20-
subject: `${this.t("event_cancelled_subject", {
22+
subject: `${this.t(subject, {
2123
title: this.calEvent.title,
2224
date: this.getFormattedDate(),
2325
})}`,
24-
html: await this.getHtml(this.calEvent, this.calEvent.organizer),
26+
html: await this.getHtml(this.calEvent, this.calEvent.organizer, this.reassigned),
2527
text: this.getTextBody("event_request_cancelled"),
2628
};
2729
}
2830

29-
async getHtml(calEvent: CalendarEvent, organizer: Person) {
31+
async getHtml(calEvent: CalendarEvent, organizer: Person, reassigned: Reassigned | undefined) {
3032
return await renderEmail("OrganizerCancelledEmail", {
3133
calEvent,
3234
attendee: organizer,
35+
reassigned,
3336
});
3437
}
3538
}

packages/features/bookings/lib/handleNewBooking.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ import { CreationSource } from "@calcom/prisma/enums";
8080
import {
8181
eventTypeAppMetadataOptionalSchema,
8282
eventTypeMetaDataSchemaWithTypedApps,
83+
userMetadata as userMetadataSchema,
8384
} from "@calcom/prisma/zod-utils";
84-
import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils";
8585
import { getAllWorkflowsFromEventType } from "@calcom/trpc/server/routers/viewer/workflows/util";
8686
import type {
8787
AdditionalInformation,
@@ -1929,7 +1929,21 @@ async function handler(
19291929
members: newBookedMembers,
19301930
eventTypeMetadata: eventType.metadata,
19311931
});
1932-
sendRoundRobinCancelledEmailsAndSMS(cancelledRRHostEvt, cancelledMembers, eventType.metadata);
1932+
const reassignedTo = users.find(
1933+
(user) => !user.isFixed && newBookedMembers.some((member) => member.email === user.email)
1934+
);
1935+
sendRoundRobinCancelledEmailsAndSMS(
1936+
cancelledRRHostEvt,
1937+
cancelledMembers,
1938+
eventType.metadata,
1939+
!!reassignedTo
1940+
? {
1941+
name: reassignedTo.name,
1942+
email: reassignedTo.email,
1943+
...(reqBody.rescheduledBy === bookerEmail && { reason: "Booker Rescheduled" }),
1944+
}
1945+
: undefined
1946+
);
19331947
}
19341948
} else {
19351949
if (!isDryRun) {

packages/features/bookings/lib/handleNewBooking/test/reschedule.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2006,7 +2006,7 @@ describe("handleNewBooking", () => {
20062006
});
20072007
describe("Team event-type", () => {
20082008
test(
2009-
"should send correct schedule/cancellation emails to hosts when round robin is rescheduled to different host",
2009+
"should send correct schedule/cancellation/reassigned emails to hosts when round robin is rescheduled to different host",
20102010
async ({ emails }) => {
20112011
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;
20122012
const booker = getBooker({
@@ -2158,6 +2158,7 @@ describe("handleNewBooking", () => {
21582158
prevOrganizer: roundRobinHost1,
21592159
newOrganizer: roundRobinHost2,
21602160
emails,
2161+
bookerReschedule: true,
21612162
});
21622163
},
21632164
timeout

packages/features/ee/round-robin/roundRobinManualReassignment.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,9 +463,9 @@ describe("roundRobinManualReassignment test", () => {
463463
const roundRobinManualReassignment = (await import("./roundRobinManualReassignment")).default;
464464
await mockEventManagerReschedule();
465465

466-
const sendRoundRobinCancelledEmailsAndSMSSpy = vi.spyOn(
466+
const sendRoundRobinReassignedEmailsAndSMSSpy = vi.spyOn(
467467
await import("@calcom/emails"),
468-
"sendRoundRobinCancelledEmailsAndSMS"
468+
"sendRoundRobinReassignedEmailsAndSMS"
469469
);
470470

471471
const testDestinationCalendar = createTestDestinationCalendar();
@@ -521,7 +521,7 @@ describe("roundRobinManualReassignment test", () => {
521521
reassignedById: 1,
522522
});
523523

524-
expect(sendRoundRobinCancelledEmailsAndSMSSpy).toHaveBeenCalledTimes(1);
524+
expect(sendRoundRobinReassignedEmailsAndSMSSpy).toHaveBeenCalledTimes(1);
525525
});
526526
});
527527

packages/features/ee/round-robin/roundRobinManualReassignment.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { cloneDeep } from "lodash";
33

44
import dayjs from "@calcom/dayjs";
55
import {
6-
sendRoundRobinCancelledEmailsAndSMS,
6+
sendRoundRobinReassignedEmailsAndSMS,
77
sendRoundRobinScheduledEmailsAndSMS,
88
sendRoundRobinUpdatedEmailsAndSMS,
99
} from "@calcom/emails";
@@ -373,9 +373,9 @@ export const roundRobinManualReassignment = async ({
373373
};
374374

375375
if (previousRRHost && emailsEnabled) {
376-
await sendRoundRobinCancelledEmailsAndSMS(
377-
cancelledEvt,
378-
[
376+
await sendRoundRobinReassignedEmailsAndSMS({
377+
calEvent: cancelledEvt,
378+
members: [
379379
{
380380
...previousRRHost,
381381
name: previousRRHost.name || "",
@@ -384,9 +384,9 @@ export const roundRobinManualReassignment = async ({
384384
language: { translate: previousRRHostT, locale: previousRRHost.locale || "en" },
385385
},
386386
],
387-
eventType?.metadata as EventTypeMetadata,
388-
{ name: newUser.name, email: newUser.email }
389-
);
387+
reassignedTo: { name: newUser.name, email: newUser.email },
388+
eventTypeMetadata: eventType?.metadata as EventTypeMetadata,
389+
});
390390
}
391391

392392
if (hasOrganizerChanged) {

packages/features/ee/round-robin/roundRobinReassignment.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { cloneDeep } from "lodash";
44
import { OrganizerDefaultConferencingAppType, getLocationValueForDB } from "@calcom/app-store/locations";
55
import dayjs from "@calcom/dayjs";
66
import {
7-
sendRoundRobinCancelledEmailsAndSMS,
7+
sendRoundRobinReassignedEmailsAndSMS,
88
sendRoundRobinScheduledEmailsAndSMS,
99
sendRoundRobinUpdatedEmailsAndSMS,
1010
} from "@calcom/emails";
@@ -420,9 +420,9 @@ export const roundRobinReassignment = async ({
420420
}
421421

422422
if (emailsEnabled) {
423-
await sendRoundRobinCancelledEmailsAndSMS(
424-
cancelledRRHostEvt,
425-
[
423+
await sendRoundRobinReassignedEmailsAndSMS({
424+
calEvent: cancelledRRHostEvt,
425+
members: [
426426
{
427427
...previousRRHost,
428428
name: previousRRHost.name || "",
@@ -431,9 +431,9 @@ export const roundRobinReassignment = async ({
431431
language: { translate: previousRRHostT, locale: previousRRHost.locale || "en" },
432432
},
433433
],
434-
eventType?.metadata as EventTypeMetadata,
435-
{ name: reassignedRRHost.name, email: reassignedRRHost.email }
436-
);
434+
reassignedTo: { name: reassignedRRHost.name, email: reassignedRRHost.email },
435+
eventTypeMetadata: eventType?.metadata as EventTypeMetadata,
436+
});
437437
}
438438
}
439439

0 commit comments

Comments
 (0)