fix: new reassignment email for RR host reassignment (#17279)
* add new reassignment email * improve reassign email * fixes * Minor copy fixup --------- Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
co-authored by
CarinaWolli
Alex van Andel
parent
4021801698
commit
d6dbbd1472
@@ -39,10 +39,14 @@
|
||||
"email_not_sent": "Error occurred while sending email",
|
||||
"event_declined_subject": "Declined: {{title}} at {{date}}",
|
||||
"event_cancelled_subject": "Canceled: {{title}} at {{date}}",
|
||||
"event_reassigned_subject": "Reassigned: {{title}} at {{date}}",
|
||||
"event_request_declined": "Your event request has been declined",
|
||||
"event_request_declined_recurring": "Your recurring event request has been declined",
|
||||
"event_request_cancelled": "Your scheduled event was canceled",
|
||||
"event_request_reassigned": "Your scheduled event was reassigned",
|
||||
"event_reassigned_subtitle": "You will no longer have the event on your calendar and your round robin likelihood will not be negatively impacted",
|
||||
"organizer": "Organizer",
|
||||
"reassigned_to": "Reassigned to",
|
||||
"need_to_reschedule_or_cancel": "Need to reschedule or cancel?",
|
||||
"you_can_view_booking_details_with_this_url": "You can view the booking details from this url {{url}} and add the event to your calendar",
|
||||
"no_options_available": "No options available",
|
||||
@@ -2654,10 +2658,10 @@
|
||||
"no_columns_found": "No columns found",
|
||||
"salesforce_create_record_as": "On booking, add events on and new attendees as:",
|
||||
"salesforce_lead": "Lead",
|
||||
"salesforce_contact_under_account": "Contact under an account",
|
||||
"salesforce_contact_under_account": "Contact under an account",
|
||||
"salesforce_skip_entry_creation": "Skip creating {{entry}} record if they do not exist in Salesforce",
|
||||
"salesforce_if_account_does_not_exist": "If the contact does not exist under an account, create new lead from attendee",
|
||||
"salesforce_create_new_contact_under_account": "Create a new contact under an account based on email domain of attendee and existing contacts",
|
||||
"salesforce_create_new_contact_under_account": "Create a new contact under an account based on email domain of attendee and existing contacts",
|
||||
"mass_assign_attributes": "Mass assign attributes",
|
||||
"reroute_preview_custom_message": "It results in showing custom message. Try changing the response to route to an event",
|
||||
"reroute_preview_external_redirect": "It results in redirecting to {{externalUrl}}. Try changing the response to route to an event",
|
||||
|
||||
@@ -65,6 +65,7 @@ import OrganizerDailyVideoDownloadRecordingEmail from "./templates/organizer-dai
|
||||
import OrganizerDailyVideoDownloadTranscriptEmail from "./templates/organizer-daily-video-download-transcript-email";
|
||||
import OrganizerLocationChangeEmail from "./templates/organizer-location-change-email";
|
||||
import OrganizerPaymentRefundFailedEmail from "./templates/organizer-payment-refund-failed-email";
|
||||
import OrganizerReassignedEmail from "./templates/organizer-reassigned-email";
|
||||
import OrganizerRequestEmail from "./templates/organizer-request-email";
|
||||
import OrganizerRequestReminderEmail from "./templates/organizer-request-reminder-email";
|
||||
import OrganizerRequestedToRescheduleEmail from "./templates/organizer-requested-to-reschedule-email";
|
||||
@@ -191,7 +192,8 @@ export const sendRoundRobinRescheduledEmailsAndSMS = async (
|
||||
export const sendRoundRobinCancelledEmailsAndSMS = async (
|
||||
calEvent: CalendarEvent,
|
||||
members: Person[],
|
||||
eventTypeMetadata?: EventTypeMetadata
|
||||
eventTypeMetadata?: EventTypeMetadata,
|
||||
reassignedTo?: { name: string | null; email: string }
|
||||
) => {
|
||||
if (eventTypeDisableHostEmail(eventTypeMetadata)) return;
|
||||
const calendarEvent = formatCalEvent(calEvent);
|
||||
@@ -199,9 +201,16 @@ export const sendRoundRobinCancelledEmailsAndSMS = async (
|
||||
const successfullyReScheduledSMS = new EventCancelledSMS(calEvent);
|
||||
|
||||
for (const teamMember of members) {
|
||||
emailsAndSMSToSend.push(
|
||||
sendEmail(() => new OrganizerCancelledEmail({ calEvent: calendarEvent, teamMember }))
|
||||
);
|
||||
if (!reassignedTo) {
|
||||
emailsAndSMSToSend.push(
|
||||
sendEmail(() => new OrganizerCancelledEmail({ calEvent: calendarEvent, teamMember }))
|
||||
);
|
||||
} else {
|
||||
emailsAndSMSToSend.push(
|
||||
sendEmail(() => new OrganizerReassignedEmail({ calEvent: calendarEvent, teamMember, reassignedTo }))
|
||||
);
|
||||
}
|
||||
|
||||
if (teamMember.phoneNumber) {
|
||||
emailsAndSMSToSend.push(successfullyReScheduledSMS.sendSMSToAttendee(teamMember));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||
|
||||
import { Info } from "./Info";
|
||||
|
||||
const PersonInfo = ({ name = "", email = "", role = "", phoneNumber = "" }) => (
|
||||
export const PersonInfo = ({ name = "", email = "", role = "", phoneNumber = "" }) => (
|
||||
<div style={{ color: "#101010", fontWeight: 400, lineHeight: "24px" }}>
|
||||
{name} - {role} {phoneNumber}
|
||||
{!isSmsCalEmail(email) && (
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
AppsStatus,
|
||||
UserFieldsResponses,
|
||||
} from "../components";
|
||||
import { PersonInfo } from "../components/WhoInfo";
|
||||
|
||||
export const BaseScheduledEmail = (
|
||||
props: {
|
||||
@@ -26,6 +27,7 @@ export const BaseScheduledEmail = (
|
||||
locale: string;
|
||||
timeFormat: TimeFormat | undefined;
|
||||
isOrganizer?: boolean;
|
||||
reassignedTo?: { name: string | null; email: string };
|
||||
} & Partial<React.ComponentProps<typeof BaseEmailHtml>>
|
||||
) => {
|
||||
const { t, timeZone, locale, timeFormat: timeFormat_ } = props;
|
||||
@@ -79,7 +81,15 @@ export const BaseScheduledEmail = (
|
||||
withSpacer
|
||||
/>
|
||||
)}
|
||||
<Info label={t("rejection_reason")} description={props.calEvent.rejectionReason} withSpacer />
|
||||
{props.reassignedTo && (
|
||||
<Info
|
||||
label={t("reassigned_to")}
|
||||
description={
|
||||
<PersonInfo name={props.reassignedTo.name || undefined} email={props.reassignedTo.email} />
|
||||
}
|
||||
withSpacer
|
||||
/>
|
||||
)}
|
||||
<Info label={t("what")} description={props.calEvent.title} withSpacer />
|
||||
<WhenInfo timeFormat={timeFormat} calEvent={props.calEvent} t={t} timeZone={timeZone} locale={locale} />
|
||||
<WhoInfo calEvent={props.calEvent} t={t} />
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { OrganizerScheduledEmail } from "./OrganizerScheduledEmail";
|
||||
|
||||
export const OrganizerReassignedEmail = (props: React.ComponentProps<typeof OrganizerScheduledEmail>) => {
|
||||
const t = props.teamMember?.language.translate || props.calEvent.organizer.language.translate;
|
||||
return (
|
||||
<OrganizerScheduledEmail
|
||||
title="event_request_reassigned"
|
||||
headerType="xCircle"
|
||||
subject="event_reassigned_subject"
|
||||
subtitle={<>{t("event_reassigned_subtitle")}</>}
|
||||
callToAction={null}
|
||||
reassignedTo={props.reassignedTo}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -10,6 +10,7 @@ export const OrganizerScheduledEmail = (
|
||||
newSeat?: boolean;
|
||||
attendeeCancelled?: boolean;
|
||||
teamMember?: Person;
|
||||
reassignedTo?: { email: string; name: string | null };
|
||||
} & Partial<React.ComponentProps<typeof BaseScheduledEmail>>
|
||||
) => {
|
||||
let subject;
|
||||
@@ -49,12 +50,17 @@ export const OrganizerScheduledEmail = (
|
||||
timeFormat={timeFormat}
|
||||
isOrganizer
|
||||
subtitle={
|
||||
<>
|
||||
{props.attendeeCancelled
|
||||
? t("attendee_no_longer_attending_subtitle", { name: props.attendee.name })
|
||||
: ""}
|
||||
</>
|
||||
props.subtitle ? (
|
||||
props.subtitle
|
||||
) : (
|
||||
<>
|
||||
{props.attendeeCancelled
|
||||
? t("attendee_no_longer_attending_subtitle", { name: props.attendee.name })
|
||||
: ""}
|
||||
</>
|
||||
)
|
||||
}
|
||||
reassignedTo={props.reassignedTo}
|
||||
{...props}
|
||||
attendee={attendee}
|
||||
/>
|
||||
|
||||
@@ -12,6 +12,7 @@ export { SlugReplacementEmail } from "./SlugReplacementEmail";
|
||||
export { FeedbackEmail } from "./FeedbackEmail";
|
||||
export { ForgotPasswordEmail } from "./ForgotPasswordEmail";
|
||||
export { OrganizerCancelledEmail } from "./OrganizerCancelledEmail";
|
||||
export { OrganizerReassignedEmail } from "./OrganizerReassignedEmail";
|
||||
export { OrganizerLocationChangeEmail } from "./OrganizerLocationChangeEmail";
|
||||
export { OrganizerPaymentRefundFailedEmail } from "./OrganizerPaymentRefundFailedEmail";
|
||||
export { OrganizerRequestEmail } from "./OrganizerRequestEmail";
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { EMAIL_FROM_NAME } from "@calcom/lib/constants";
|
||||
|
||||
import { renderEmail } from "../";
|
||||
import generateIcsFile, { GenerateIcsRole } from "../lib/generateIcsFile";
|
||||
import OrganizerScheduledEmail from "./organizer-scheduled-email";
|
||||
|
||||
export default class OrganizerReassignedEmail extends OrganizerScheduledEmail {
|
||||
protected async getNodeMailerPayload(): Promise<Record<string, unknown>> {
|
||||
const toAddresses = [this.teamMember?.email || this.calEvent.organizer.email];
|
||||
|
||||
return {
|
||||
icalEvent: generateIcsFile({
|
||||
calEvent: this.calEvent,
|
||||
status: "CANCELLED",
|
||||
role: GenerateIcsRole.ORGANIZER,
|
||||
}),
|
||||
from: `${EMAIL_FROM_NAME} <${this.getMailerOptions().from}>`,
|
||||
to: toAddresses.join(","),
|
||||
subject: `${this.t("event_reassigned_subject", {
|
||||
title: this.calEvent.title,
|
||||
date: this.getFormattedDate(),
|
||||
})}`,
|
||||
html: await renderEmail("OrganizerReassignedEmail", {
|
||||
attendee: this.calEvent.organizer,
|
||||
calEvent: this.calEvent,
|
||||
reassignedTo: this.reassignedTo,
|
||||
}),
|
||||
text: this.getTextBody("event_request_reassigned"),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,21 @@ export default class OrganizerScheduledEmail extends BaseEmail {
|
||||
t: TFunction;
|
||||
newSeat?: boolean;
|
||||
teamMember?: Person;
|
||||
reassignedTo?: { name: string | null; email: string };
|
||||
|
||||
constructor(input: { calEvent: CalendarEvent; newSeat?: boolean; teamMember?: Person }) {
|
||||
constructor(input: {
|
||||
calEvent: CalendarEvent;
|
||||
newSeat?: boolean;
|
||||
teamMember?: Person;
|
||||
reassignedTo?: { email: string; name: string | null };
|
||||
}) {
|
||||
super();
|
||||
this.name = "SEND_BOOKING_CONFIRMATION";
|
||||
this.calEvent = input.calEvent;
|
||||
this.t = this.calEvent.organizer.language.translate;
|
||||
this.newSeat = input.newSeat;
|
||||
this.teamMember = input.teamMember;
|
||||
this.reassignedTo = input.reassignedTo;
|
||||
}
|
||||
|
||||
protected async getNodeMailerPayload(): Promise<Record<string, unknown>> {
|
||||
|
||||
@@ -244,7 +244,6 @@ export const roundRobinManualReassignment = async ({
|
||||
bookingFields: eventType.bookingFields ?? null,
|
||||
booking,
|
||||
}),
|
||||
cancellationReason: "Manually re-assigned",
|
||||
};
|
||||
|
||||
const credentials = await prisma.credential.findMany({
|
||||
@@ -287,7 +286,7 @@ export const roundRobinManualReassignment = async ({
|
||||
},
|
||||
]);
|
||||
|
||||
// Send cancellation email to original organizer
|
||||
// Send cancellation email to previous RR host
|
||||
const cancelledEvt = cloneDeep(evt);
|
||||
cancelledEvt.organizer = {
|
||||
email: originalOrganizer.email,
|
||||
@@ -308,7 +307,8 @@ export const roundRobinManualReassignment = async ({
|
||||
language: { translate: previousRRHostT, locale: previousRRHost.locale || "en" },
|
||||
},
|
||||
],
|
||||
eventType?.metadata as EventTypeMetadata
|
||||
eventType?.metadata as EventTypeMetadata,
|
||||
{ name: newUser.name, email: newUser.email }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -375,7 +375,8 @@ export const roundRobinReassignment = async ({
|
||||
language: { translate: previousRRHostT, locale: previousRRHost.locale || "en" },
|
||||
},
|
||||
],
|
||||
eventType?.metadata as EventTypeMetadata
|
||||
eventType?.metadata as EventTypeMetadata,
|
||||
{ name: reassignedRRHost.name, email: reassignedRRHost.email }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user