Add reschedule reason to DB and emails (#2909)

* Write reschedule reason to db

* Add reschedule reason to emails

* Add reschedule reason to request reschedule email

* Add reschedule reason to request reschedule email

* Add reschedule reason to request reschedule email

* Add reschedule reason to request reschedule email

* Write reschedule reason to db

* Add reschedule reason to emails

* No longer using rescheduleReason in favor of cancellationReason

* Update apps emails accordingly with reschedule reason

* Update reschedule.test.ts

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Alan <alannnc@gmail.com>
Co-authored-by: zomars <zomars@me.com>
This commit is contained in:
authored and GitHub committed 2022-05-30 13:40:29 -06:00
1 parent 6b0cffa79a
commit 0761e8664a
17 files changed
+103 -15

No files matched your search

@@ -83,6 +83,7 @@ type BookingFormValues = {
customInputs?: {
[key: string]: string | boolean;
};
rescheduleReason?: string;
};
const BookingPage = ({
@@ -255,6 +256,7 @@ const BookingPage = ({
email: primaryAttendee.email || "",
guests: guestListEmails,
notes: booking.description || "",
rescheduleReason: "",
customInputs: eventType.customInputs.reduce(
(customInputs, input) => ({
...customInputs,
@@ -786,18 +788,31 @@ const BookingPage = ({
<label
htmlFor="notes"
className="mb-1 block text-sm font-medium text-gray-700 dark:text-white">
{t("additional_notes")}
{rescheduleUid ? t("reschedule_optional") : t("additional_notes")}
</label>
<textarea
{...bookingForm.register("notes")}
id="notes"
name="notes"
rows={3}
className={inputClassName}
placeholder={t("share_additional_notes")}
disabled={disabledExceptForOwner}
/>
{rescheduleUid ? (
<textarea
{...bookingForm.register("rescheduleReason")}
id="rescheduleReason"
name="rescheduleReason"
rows={3}
className={inputClassName}
placeholder={t("reschedule_placeholder")}
disabled={disabledExceptForOwner}
/>
) : (
<textarea
{...bookingForm.register("notes")}
id="notes"
name="notes"
rows={3}
className={inputClassName}
placeholder={t("share_additional_notes")}
disabled={disabledExceptForOwner}
/>
)}
</div>
<div className="flex items-start space-x-2 rtl:space-x-reverse">
<Button
type="submit"
@@ -102,6 +102,7 @@ ${this.calEvent.organizer.language.translate("request_reschedule_title_attendee"
${this.calEvent.organizer.language.translate("request_reschedule_subtitle", {
organizer: this.calEvent.organizer.name,
})},
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getAdditionalNotes()}
@@ -151,6 +152,7 @@ ${getCancelLink(this.calEvent)}
<tr>
<td align="left" style="font-size:0px;padding:10px 40px;word-break:break-word;">
<div style="font-family:Roboto, Helvetica, sans-serif;font-size:16px;font-weight:500;line-height:1;text-align:left;color:#3E3E3E;">
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getWho()}
@@ -52,6 +52,7 @@ export default class AttendeeRescheduledEmail extends AttendeeScheduledEmail {
return `
${this.attendee.language.translate("event_has_been_rescheduled")}
${this.attendee.language.translate("emailed_you_and_any_other_attendees")}
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getLocation()}
@@ -112,6 +113,7 @@ ${this.getCustomInputs()}
<tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Roboto, Helvetica, sans-serif;font-size:16px;font-weight:500;line-height:1;text-align:left;color:#3E3E3E;">
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getWho()}
@@ -452,4 +452,12 @@ ${getRichDescription(this.calEvent)}
protected getInviteeEnd(): Dayjs {
return dayjs(this.calEvent.endTime).tz(this.getTimezone());
}
protected getReason(): string {
return `
<div style="line-height: 6px; margin-bottom: 24px;">
<p style="color: #494949;">${this.calEvent.attendees[0].language.translate("reschedule_reason")}</p>
<p style="color: #494949; font-weight: 400; line-height: 24px;">${this.calEvent.cancellationReason}</p>
</div>`;
}
}
@@ -111,6 +111,7 @@ ${this.calEvent.organizer.language.translate("request_reschedule_title_organizer
${this.calEvent.organizer.language.translate("request_reschedule_subtitle_organizer", {
attendee: this.calEvent.attendees[0].name,
})},
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getLocation()}
@@ -163,6 +164,7 @@ ${getCancelLink(this.calEvent)}
<tr>
<td align="left" style="font-size:0px;padding:10px 40px;word-break:break-word;">
<div style="font-family:Roboto, Helvetica, sans-serif;font-size:16px;font-weight:500;line-height:1;text-align:left;color:#3E3E3E;">
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getWho()}
@@ -59,6 +59,7 @@ export default class OrganizerRescheduledEmail extends OrganizerScheduledEmail {
return `
${this.calEvent.organizer.language.translate("event_has_been_rescheduled")}
${this.calEvent.organizer.language.translate("emailed_you_and_any_other_attendees")}
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getLocation()}
@@ -108,6 +109,7 @@ ${getCancelLink(this.calEvent)}
<tr>
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:Roboto, Helvetica, sans-serif;font-size:16px;font-weight:500;line-height:1;text-align:left;color:#3E3E3E;">
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getWho()}
@@ -439,4 +439,12 @@ ${getRichDescription(this.calEvent)}
protected getOrganizerEnd(): Dayjs {
return dayjs(this.calEvent.endTime).tz(this.getTimezone());
}
protected getReason(): string {
return `
<div style="line-height: 6px; margin-bottom: 24px;">
<p style="color: #494949;">${this.calEvent.attendees[0].language.translate("reschedule_reason")}</p>
<p style="color: #494949; font-weight: 400; line-height: 24px;">${this.calEvent.cancellationReason}</p>
</div>`;
}
}
+15 -3
View File
@@ -201,7 +201,11 @@ const getEventTypesFromDB = async (eventTypeId: number) => {
type User = Prisma.UserGetPayload<typeof userSelect>;
type ExtendedBookingCreateBody = BookingCreateBody & { noEmail?: boolean; recurringCount?: number };
type ExtendedBookingCreateBody = BookingCreateBody & {
noEmail?: boolean;
recurringCount?: number;
rescheduleReason?: string;
};
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { recurringCount, noEmail, ...reqBody } = req.body as ExtendedBookingCreateBody;
@@ -677,11 +681,18 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (originalRescheduledBooking?.uid) {
// Use EventManager to conditionally use all needed integrations.
const updateManager = await eventManager.update(evt, originalRescheduledBooking.uid, booking?.id);
const updateManager = await eventManager.update(
evt,
originalRescheduledBooking.uid,
booking?.id,
reqBody.rescheduleReason
);
// This gets overridden when updating the event - to check if notes have been hidden or not. We just reset this back
// to the default description when we are sending the emails.
evt.description = eventType.description;
console.log("🚀 ~ file: event.ts ~ line 684 ~ handler ~ descripiton", reqBody);
results = updateManager.results;
referencesToCreate = updateManager.referencesToCreate;
if (results.length > 0 && results.some((res) => !res.success)) {
@@ -711,7 +722,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
{
...evt,
additionInformation: metadata,
additionalNotes, // Resets back to the addtionalNote input and not the overriden value
additionalNotes, // Resets back to the additionalNote input and not the override value
cancellationReason: reqBody.rescheduleReason,
},
reqBody.recurringEventId ? (eventType.recurringEvent as RecurringEvent) : {}
);
+1 -1
View File
@@ -87,7 +87,7 @@ test.describe("Reschedule Tests", async () => {
await expect(page.locator('[name="name"]')).toBeDisabled();
await expect(page.locator('[name="email"]')).toBeDisabled();
await expect(page.locator('[name="notes"]')).toBeDisabled();
await expect(page.locator('[name="rescheduleReason"]')).toBeDisabled();
await page.locator('[data-testid="confirm-reschedule-button"]').click();
@@ -868,6 +868,8 @@
"go_to": "Go to: ",
"zapier_invite_link": "Zapier Invite Link",
"meeting_url_provided_after_confirmed":"A Meeting URL will be created once the event is confirmed.",
"reschedule_optional": "Reason for rescheduling (optional)",
"reschedule_placeholder": "Let others know why you need to reschedule",
"event_cancelled":"This event is cancelled",
"emailed_information_about_cancelled_event": "We emailed you and the other attendees to let them know.",
"this_input_will_shown_booking_this_event": "This input will be shown when booking this event",
@@ -102,6 +102,7 @@ ${this.calEvent.organizer.language.translate("request_reschedule_title_attendee"
${this.calEvent.organizer.language.translate("request_reschedule_subtitle", {
organizer: this.calEvent.organizer.name,
})},
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getAdditionalNotes()}
@@ -150,6 +151,7 @@ ${getCancelLink(this.calEvent)}
<tr>
<td align="left" style="font-size:0px;padding:10px 40px;word-break:break-word;">
<div style="font-family:Roboto, Helvetica, sans-serif;font-size:16px;font-weight:500;line-height:1;text-align:left;color:#3E3E3E;">
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getWho()}
@@ -406,4 +406,12 @@ ${getRichDescription(this.calEvent)}
protected getOrganizerEnd(): Dayjs {
return dayjs(this.calEvent.endTime).tz(this.getTimezone());
}
protected getReason(): string {
return `
<div style="line-height: 6px; margin-bottom: 24px;">
<p style="color: #494949;">${this.calEvent.attendees[0].language.translate("reschedule_reason")}</p>
<p style="color: #494949; font-weight: 400; line-height: 24px;">${this.calEvent.cancellationReason}</p>
</div>`;
}
}
@@ -111,6 +111,7 @@ ${this.calEvent.organizer.language.translate("request_reschedule_title_organizer
${this.calEvent.organizer.language.translate("request_reschedule_subtitle_organizer", {
attendee: this.calEvent.attendees[0].name,
})},
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getLocation()}
@@ -162,6 +163,7 @@ ${getCancelLink(this.calEvent)}
<tr>
<td align="left" style="font-size:0px;padding:10px 40px;word-break:break-word;">
<div style="font-family:Roboto, Helvetica, sans-serif;font-size:16px;font-weight:500;line-height:1;text-align:left;color:#3E3E3E;">
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getWho()}
@@ -102,6 +102,7 @@ ${this.calEvent.organizer.language.translate("request_reschedule_title_attendee"
${this.calEvent.organizer.language.translate("request_reschedule_subtitle", {
organizer: this.calEvent.organizer.name,
})},
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getAdditionalNotes()}
@@ -150,6 +151,7 @@ ${getCancelLink(this.calEvent)}
<tr>
<td align="left" style="font-size:0px;padding:10px 40px;word-break:break-word;">
<div style="font-family:Roboto, Helvetica, sans-serif;font-size:16px;font-weight:500;line-height:1;text-align:left;color:#3E3E3E;">
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getWho()}
@@ -406,4 +406,12 @@ ${getRichDescription(this.calEvent)}
protected getOrganizerEnd(): Dayjs {
return dayjs(this.calEvent.endTime).tz(this.getTimezone());
}
protected getReason(): string {
return `
<div style="line-height: 6px; margin-bottom: 24px;">
<p style="color: #494949;">${this.calEvent.attendees[0].language.translate("reschedule_reason")}</p>
<p style="color: #494949; font-weight: 400; line-height: 24px;">${this.calEvent.cancellationReason}</p>
</div>`;
}
}
@@ -111,6 +111,7 @@ ${this.calEvent.organizer.language.translate("request_reschedule_title_organizer
${this.calEvent.organizer.language.translate("request_reschedule_subtitle_organizer", {
attendee: this.calEvent.attendees[0].name,
})},
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getLocation()}
@@ -162,6 +163,7 @@ ${getCancelLink(this.calEvent)}
<tr>
<td align="left" style="font-size:0px;padding:10px 40px;word-break:break-word;">
<div style="font-family:Roboto, Helvetica, sans-serif;font-size:16px;font-weight:500;line-height:1;text-align:left;color:#3E3E3E;">
${this.calEvent.cancellationReason && this.getReason()}
${this.getWhat()}
${this.getWhen()}
${this.getWho()}
+12 -1
View File
@@ -165,7 +165,8 @@ export default class EventManager {
public async update(
event: CalendarEvent,
rescheduleUid: string,
newBookingId?: number
newBookingId?: number,
rescheduleReason?: string
): Promise<CreateUpdateResult> {
const evt = processLocation(event);
@@ -201,6 +202,16 @@ export default class EventManager {
throw new Error("booking not found");
}
// Add reschedule reason to new booking
await prisma.booking.update({
where: {
id: newBookingId,
},
data: {
cancellationReason: rescheduleReason,
},
});
const isDedicated = evt.location ? isDedicatedIntegration(evt.location) : null;
const results: Array<EventResult> = [];
// If and only if event type is a dedicated meeting, update the dedicated video meeting.