-
+ |
diff --git a/apps/web/lib/emails/templates/organizer-request-reschedule-email.ts b/apps/web/lib/emails/templates/organizer-request-reschedule-email.ts
index 7aa59f0082..f0307545db 100644
--- a/apps/web/lib/emails/templates/organizer-request-reschedule-email.ts
+++ b/apps/web/lib/emails/templates/organizer-request-reschedule-email.ts
@@ -6,6 +6,7 @@ import utc from "dayjs/plugin/utc";
import { createEvent, DateArray, Person } from "ics";
import { getCancelLink } from "@calcom/lib/CalEventParser";
+import { CalendarEvent } from "@calcom/types/Calendar";
import {
emailHead,
@@ -22,16 +23,13 @@ dayjs.extend(localizedFormat);
dayjs.extend(toArray);
export default class OrganizerRequestRescheduledEmail extends OrganizerScheduledEmail {
+ private metadata: { rescheduleLink: string };
+ constructor(calEvent: CalendarEvent, metadata: { rescheduleLink: string }) {
+ super(calEvent);
+ this.metadata = metadata;
+ }
protected getNodeMailerPayload(): Record {
const toAddresses = [this.calEvent.organizer.email];
- if (this.calEvent.team) {
- this.calEvent.team.members.forEach((member) => {
- const memberAttendee = this.calEvent.attendees.find((attendee) => attendee.name === member);
- if (memberAttendee) {
- toAddresses.push(memberAttendee.email);
- }
- });
- }
return {
icalEvent: {
@@ -86,7 +84,7 @@ export default class OrganizerRequestRescheduledEmail extends OrganizerScheduled
}
return icsEvent.value;
}
- // @OVERRIDe
+ // @OVERRIDE
protected getWhen(): string {
return `
@@ -108,9 +106,11 @@ export default class OrganizerRequestRescheduledEmail extends OrganizerScheduled
protected getTextBody(): string {
return `
-${this.calEvent.organizer.language.translate("request_reschedule_title_attendee")}
-${this.calEvent.organizer.language.translate("request_reschedule_subtitle", {
- organizer: this.calEvent.attendees[0],
+${this.calEvent.organizer.language.translate("request_reschedule_title_organizer", {
+ attendee: this.calEvent.attendees[0].name,
+})}
+${this.calEvent.organizer.language.translate("request_reschedule_subtitle_organizer", {
+ attendee: this.calEvent.attendees[0].name,
})},
${this.getWhat()}
${this.getWhen()}
@@ -142,9 +142,11 @@ ${getCancelLink(this.calEvent)}
${emailSchedulingBodyHeader("calendarCircle")}
${emailScheduledBodyHeaderContent(
- this.calEvent.organizer.language.translate("request_reschedule_title_attendee"),
- this.calEvent.organizer.language.translate("request_reschedule_subtitle", {
- organizer: this.calEvent.attendees[0],
+ this.calEvent.organizer.language.translate("request_reschedule_title_organizer", {
+ attendee: this.calEvent.attendees[0].name,
+ }),
+ this.calEvent.organizer.language.translate("request_reschedule_subtitle_organizer", {
+ attendee: this.calEvent.attendees[0].name,
})
)}
${emailSchedulingBodyDivider()}
@@ -159,7 +161,7 @@ ${getCancelLink(this.calEvent)}
-
+ |
${this.getWhat()}
${this.getWhen()}
@@ -178,33 +180,6 @@ ${getCancelLink(this.calEvent)}
|
- ${emailSchedulingBodyDivider()}
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${this.getManageLink()}
-
- |
-
-
-
-
-
- |
-
-
-
-
${emailBodyLogo()}
diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx
index 9748dfb6c1..4b68205f17 100644
--- a/apps/web/pages/[user]/[type].tsx
+++ b/apps/web/pages/[user]/[type].tsx
@@ -24,6 +24,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
const userParam = asStringOrNull(context.query.user);
const typeParam = asStringOrNull(context.query.type);
const dateParam = asStringOrNull(context.query.date);
+ const rescheduleUid = asStringOrNull(context.query.rescheduleUid);
if (!userParam || !typeParam) {
throw new Error(`File is not named [type]/[user]`);
@@ -214,6 +215,43 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
eventTypeObject.schedule = null;
eventTypeObject.availability = [];
+ type Booking = {
+ startTime: Date | string;
+ description: string | null;
+ attendees?: {
+ email: string;
+ name: string;
+ }[];
+ } | null;
+ // @NOTE: being used several times refactor to exported function
+ async function getBooking(rescheduleUid: string): Promise {
+ return await prisma.booking.findFirst({
+ where: {
+ uid: rescheduleUid,
+ },
+ select: {
+ startTime: true,
+ description: true,
+ attendees: {
+ select: {
+ email: true,
+ name: true,
+ },
+ },
+ },
+ });
+ }
+
+ let booking: Booking | null = null;
+ if (rescheduleUid) {
+ booking = await getBooking(rescheduleUid);
+ if (booking) {
+ // @NOTE: had to do this because Server side cant return [Object objects]
+ // probably fixable with json.stringify -> json.parse
+ booking["startTime"] = (booking?.startTime as Date)?.toISOString();
+ }
+ }
+
return {
props: {
profile: {
@@ -231,6 +269,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
workingHours,
trpcState: ssr.dehydrate(),
previousPage: context.req.headers.referer ?? null,
+ booking,
},
};
};
diff --git a/apps/web/pages/[user]/book.tsx b/apps/web/pages/[user]/book.tsx
index 3e8bd5b6f7..ec5c0f8f53 100644
--- a/apps/web/pages/[user]/book.tsx
+++ b/apps/web/pages/[user]/book.tsx
@@ -69,6 +69,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
disableGuests: true,
users: {
select: {
+ id: true,
username: true,
name: true,
email: true,
@@ -112,12 +113,23 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
};
})[0];
- async function getBooking() {
- return prisma.booking.findFirst({
+ type Booking = {
+ startTime: Date | string;
+ description: string | null;
+ attendees: {
+ email: string;
+ name: string;
+ }[];
+ } | null;
+
+ // @NOTE: being used several times refactor to exported function
+ async function getBooking(): Promise {
+ return await prisma.booking.findFirst({
where: {
uid: asStringOrThrow(context.query.rescheduleUid),
},
select: {
+ startTime: true,
description: true,
attendees: {
select: {
@@ -129,15 +141,18 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
});
}
- type Booking = Prisma.PromiseReturnType;
let booking: Booking | null = null;
-
if (context.query.rescheduleUid) {
booking = await getBooking();
+ if (booking) {
+ // @NOTE: had to do this because Server side cant return [Object objects]
+ // probably fixable with json.stringify -> json.parse
+ booking["startTime"] = (booking?.startTime as Date)?.toISOString();
+ }
}
const t = await getTranslation(context.locale ?? "en", "common");
-
+ console.log({ booking });
return {
props: {
locationLabels: getLocationLabels(t),
diff --git a/apps/web/pages/api/book/request-reschedule.ts b/apps/web/pages/api/book/request-reschedule.ts
index c57147cb05..69b3417a5e 100644
--- a/apps/web/pages/api/book/request-reschedule.ts
+++ b/apps/web/pages/api/book/request-reschedule.ts
@@ -21,8 +21,8 @@ const rescheduleSchema = z.object({
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getSession({ req });
const { bookingId, rescheduleReason: cancellationReason } = req.body;
- console.log({ bookingId });
- let userOwner: Pick | null;
+ type PersonAttendee = Pick;
+ let userOwner: PersonAttendee | null;
try {
if (session?.user?.id) {
userOwner = await prisma.user.findUnique({
@@ -82,15 +82,13 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
// @NOTE: Lets assume all guests are the same language
const [firstAttendee] = bookingToReschedule.attendees;
const tAttendees = await getTranslation(firstAttendee.locale ?? "en", "common");
- const usersToPeopleType = (
- users: Pick[],
- selectedLanguage: TFunction
- ): Person[] => {
+ const usersToPeopleType = (users: PersonAttendee[], selectedLanguage: TFunction): Person[] => {
return users?.map((user) => {
return {
id: user.id || "",
email: user.email || "",
name: user.name || "",
+ username: user?.username || "",
language: { translate: selectedLanguage, locale: user.locale || "en" },
timeZone: user?.timeZone,
};
@@ -105,7 +103,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
type: event?.title || "Nameless Event",
startTime: bookingToReschedule.startTime.toISOString(),
endTime: bookingToReschedule.endTime.toISOString(),
- attendees: usersToPeopleType(bookingToReschedule.attendees, tAttendees),
+ attendees: usersToPeopleType(
+ // username field doesn't exists on attendee but could be in the future
+ bookingToReschedule.attendees as unknown as PersonAttendee[],
+ tAttendees
+ ),
organizer: userOwnerAsPeopleType,
});
await calendarEventBuilder.buildEventObjectFromInnerClass(bookingToReschedule.eventTypeId);
@@ -117,9 +119,19 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
await calendarEventBuilder.buildLuckyUsers();
}
await calendarEventBuilder.buildAttendeesList();
+ calendarEventBuilder.setLocation(bookingToReschedule.location);
+ calendarEventBuilder.setUId(bookingToReschedule.uid);
+ calendarEventBuilder.setCancellationReason(cancellationReason);
console.log({ calendarEventBuilder });
// Send email =================
- await sendRequestRescheduleEmail(calendarEventBuilder.calendarEvent);
+ const queryParams = new URLSearchParams();
+ queryParams.set("rescheduleUid", `${bookingToReschedule.uid}`);
+ const rescheduleLink = `${process.env.WEBSITE_BASE_URL}/${userOwner.username}/${
+ event?.slug
+ }?${queryParams.toString()}`;
+ await sendRequestRescheduleEmail(calendarEventBuilder.calendarEvent, {
+ rescheduleLink,
+ });
}
return res.status(200).json(bookingToReschedule);
diff --git a/apps/web/pages/team/[slug]/[type].tsx b/apps/web/pages/team/[slug]/[type].tsx
index 8fe83b17f8..1c0ba56f43 100644
--- a/apps/web/pages/team/[slug]/[type].tsx
+++ b/apps/web/pages/team/[slug]/[type].tsx
@@ -20,6 +20,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
const slugParam = asStringOrNull(context.query.slug);
const typeParam = asStringOrNull(context.query.type);
const dateParam = asStringOrNull(context.query.date);
+ const rescheduleUid = asStringOrNull(context.query.rescheduleUid);
if (!slugParam || !typeParam) {
throw new Error(`File is not named [idOrSlug]/[user]`);
@@ -109,6 +110,43 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
eventTypeObject.availability = [];
+ type Booking = {
+ startTime: Date | string;
+ description: string | null;
+ attendees?: {
+ email: string;
+ name: string;
+ }[];
+ } | null;
+ // @NOTE: being used several times refactor to exported function
+ async function getBooking(rescheduleUid: string): Promise {
+ return await prisma.booking.findFirst({
+ where: {
+ uid: rescheduleUid,
+ },
+ select: {
+ startTime: true,
+ description: true,
+ attendees: {
+ select: {
+ email: true,
+ name: true,
+ },
+ },
+ },
+ });
+ }
+
+ let booking: Booking | null = null;
+ if (rescheduleUid) {
+ booking = await getBooking(rescheduleUid);
+ if (booking) {
+ // @NOTE: had to do this because Server side cant return [Object objects]
+ // probably fixable with json.stringify -> json.parse
+ booking["startTime"] = (booking?.startTime as Date)?.toISOString();
+ }
+ }
+
return {
props: {
// Team is always pro
@@ -126,6 +164,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
eventType: eventTypeObject,
workingHours,
previousPage: context.req.headers.referer ?? null,
+ booking,
},
};
};
diff --git a/apps/web/pages/team/[slug]/book.tsx b/apps/web/pages/team/[slug]/book.tsx
index fc63c0e413..2bb5d4431b 100644
--- a/apps/web/pages/team/[slug]/book.tsx
+++ b/apps/web/pages/team/[slug]/book.tsx
@@ -56,6 +56,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
},
users: {
select: {
+ id: true,
avatar: true,
name: true,
},
@@ -74,12 +75,23 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
};
})[0];
- async function getBooking() {
+ type Booking = {
+ startTime: Date | string;
+ description: string | null;
+ attendees: {
+ email: string;
+ name: string;
+ }[];
+ } | null;
+
+ // @NOTE: being used several times refactor to exported function
+ async function getBooking(): Promise {
return prisma.booking.findFirst({
where: {
uid: asStringOrThrow(context.query.rescheduleUid),
},
select: {
+ startTime: true,
description: true,
attendees: {
select: {
@@ -91,11 +103,14 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
});
}
- type Booking = Prisma.PromiseReturnType;
let booking: Booking | null = null;
-
if (context.query.rescheduleUid) {
booking = await getBooking();
+ if (booking) {
+ // @NOTE: had to do this because Server side cant return [Object objects]
+ // probably fixable with json.stringify -> json.parse
+ booking["startTime"] = (booking?.startTime as Date)?.toISOString();
+ }
}
const t = await getTranslation(context.locale ?? "en", "common");
diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json
index 8437863925..df8f89638e 100644
--- a/apps/web/public/static/locales/en/common.json
+++ b/apps/web/public/static/locales/en/common.json
@@ -66,6 +66,8 @@
"event_has_been_rescheduled": "Updated - Your event has been rescheduled",
"request_reschedule_title_attendee": "Request to reschedule your booking",
"request_reschedule_subtitle": "{{organizer}} has cancelled the booking and requested you to pick another time.",
+ "request_reschedule_title_organizer": "You have requested {{attendee}} to reschedule",
+ "request_reschedule_subtitle_organizer": "You have cancelled the booking and {{attendee}} should be pick a new booking time with you.",
"reschedule_reason": "Reason for reschedule",
"hi_user_name": "Hi {{name}}",
"ics_event_title": "{{eventType}} with {{name}}",
diff --git a/packages/core/builders/CalendarEvent/builder.ts b/packages/core/builders/CalendarEvent/builder.ts
index 0fc484bf9d..ee8868dfd2 100644
--- a/packages/core/builders/CalendarEvent/builder.ts
+++ b/packages/core/builders/CalendarEvent/builder.ts
@@ -248,4 +248,8 @@ export class CalendarEventBuilder implements ICalendarEventBuilder {
public setDescription(description: CalendarEventClass["description"]) {
this.calendarEvent.description = description;
}
+
+ public setCancellationReason(cancellationReason: CalendarEventClass["cancellationReason"]) {
+ this.calendarEvent.cancellationReason = cancellationReason;
+ }
}
diff --git a/packages/types/Calendar.d.ts b/packages/types/Calendar.d.ts
index ebf9e6a297..692e4df1fb 100644
--- a/packages/types/Calendar.d.ts
+++ b/packages/types/Calendar.d.ts
@@ -12,6 +12,7 @@ export type Person = {
email: string;
timeZone: string;
language: { translate: TFunction; locale: string };
+ username?: string;
};
export type EventBusyDate = Record<"start" | "end", Date | string>;
|