diff --git a/lib/CalEventParser.ts b/lib/CalEventParser.ts index 143322cbe4..ded6fb12b4 100644 --- a/lib/CalEventParser.ts +++ b/lib/CalEventParser.ts @@ -1,15 +1,104 @@ +import dayjs from "dayjs"; import short from "short-uuid"; import { v5 as uuidv5 } from "uuid"; +import { getIntegrationName } from "@lib/integrations"; + import { CalendarEvent } from "./calendarClient"; import { BASE_URL } from "./config/constants"; const translator = short(); -export const getUid = (calEvent: CalendarEvent) => { +export const getWhat = (calEvent: CalendarEvent) => { + return ` +${calEvent.language("what")} +${calEvent.type} + `; +}; + +export const getWhen = (calEvent: CalendarEvent) => { + const inviteeStart = dayjs(calEvent.startTime).tz(calEvent.attendees[0].timeZone); + const inviteeEnd = dayjs(calEvent.endTime).tz(calEvent.attendees[0].timeZone); + + return ` +${calEvent.language("when")} +${calEvent.language(inviteeStart.format("dddd").toLowerCase())}, ${calEvent.language( + inviteeStart.format("MMMM").toLowerCase() + )} ${inviteeStart.format("D")}, ${inviteeStart.format("YYYY")} | ${inviteeStart.format( + "h:mma" + )} - ${inviteeEnd.format("h:mma")} (${calEvent.attendees[0].timeZone}) + `; +}; + +export const getWho = (calEvent: CalendarEvent) => { + const attendees = calEvent.attendees + .map((attendee) => { + return ` +${attendee?.name || calEvent.language("guest")} +${attendee.email} + `; + }) + .join(""); + + const organizer = ` +${calEvent.organizer.name} - ${calEvent.language("organizer")} +${calEvent.organizer.email} + `; + + return ` +${calEvent.language("who")} +${organizer + attendees} + `; +}; + +export const getAdditionalNotes = (calEvent: CalendarEvent) => { + return ` +${calEvent.language("additional_notes")} +${calEvent.description} + `; +}; + +export const getLocation = (calEvent: CalendarEvent) => { + let providerName = calEvent.location ? getIntegrationName(calEvent.location) : ""; + + if (calEvent.location && calEvent.location.includes("integrations:")) { + const location = calEvent.location.split(":")[1]; + providerName = location[0].toUpperCase() + location.slice(1); + } + + if (calEvent.videoCallData) { + return calEvent.videoCallData.url; + } + + if (calEvent.additionInformation?.hangoutLink) { + return calEvent.additionInformation.hangoutLink; + } + + return providerName || calEvent.location; +}; + +export const getManageLink = (calEvent: CalendarEvent) => { + return ` +${calEvent.language("need_to_reschedule_or_cancel")} +${getCancelLink(calEvent)} + `; +}; + +export const getUid = (calEvent: CalendarEvent): string => { return calEvent.uid ?? translator.fromUUID(uuidv5(JSON.stringify(calEvent), uuidv5.URL)); }; -export const getCancelLink = (calEvent: CalendarEvent) => { +export const getCancelLink = (calEvent: CalendarEvent): string => { return BASE_URL + "/cancel/" + getUid(calEvent); }; + +export const getRichDescription = (calEvent: CalendarEvent) => { + return ` + ${getWhat(calEvent)} + ${getWhen(calEvent)} + ${getWho(calEvent)} + ${getLocation(calEvent)} + ${getAdditionalNotes(calEvent)} + ${getManageLink(calEvent)} + `; +}; diff --git a/lib/calendarClient.ts b/lib/calendarClient.ts index 5f5f66a7e9..999b208784 100644 --- a/lib/calendarClient.ts +++ b/lib/calendarClient.ts @@ -181,7 +181,7 @@ const updateEvent = async ( const uid = getUid(calEvent); let success = true; - const updationResult = + const updatedResult = credential && bookingRefUid ? await calendars([credential])[0] .updateEvent(bookingRefUid, calEvent) @@ -192,7 +192,7 @@ const updateEvent = async ( }) : undefined; - if (!updationResult) { + if (!updatedResult) { return { type: credential.type, success, @@ -205,7 +205,7 @@ const updateEvent = async ( type: credential.type, success, uid, - updatedEvent: updationResult, + updatedEvent: updatedResult, originalEvent: calEvent, }; }; diff --git a/lib/emails/templates/attendee-scheduled-email.ts b/lib/emails/templates/attendee-scheduled-email.ts index eabe008f9d..0e4fea8b6d 100644 --- a/lib/emails/templates/attendee-scheduled-email.ts +++ b/lib/emails/templates/attendee-scheduled-email.ts @@ -121,6 +121,7 @@ export default class AttendeeScheduledEmail { ${this.calEvent.language("emailed_you_and_any_other_attendees")} ${this.getWhat()} ${this.getWhen()} + ${this.getWho()} ${this.getLocation()} ${this.getAdditionalNotes()} ${this.calEvent.language("need_to_reschedule_or_cancel")} @@ -380,7 +381,9 @@ ${this.getAdditionalNotes()}

${this.calEvent.language("where")}

-

${providerName}

+

${ + providerName || this.calEvent.location + }

`; } diff --git a/lib/emails/templates/organizer-scheduled-email.ts b/lib/emails/templates/organizer-scheduled-email.ts index 8b03dcf33c..3dd206352c 100644 --- a/lib/emails/templates/organizer-scheduled-email.ts +++ b/lib/emails/templates/organizer-scheduled-email.ts @@ -125,6 +125,7 @@ ${this.calEvent.language("new_event_scheduled")} ${this.calEvent.language("emailed_you_and_any_other_attendees")} ${this.getWhat()} ${this.getWhen()} +${this.getWho()} ${this.getLocation()} ${this.getAdditionalNotes()} ${this.calEvent.language("need_to_reschedule_or_cancel")} @@ -368,7 +369,9 @@ ${getCancelLink(this.calEvent)}

${this.calEvent.language("where")}

-

${providerName}

+

${ + providerName || this.calEvent.location + }

`; } diff --git a/lib/events/EventManager.ts b/lib/events/EventManager.ts index c819162911..790e6e2e59 100644 --- a/lib/events/EventManager.ts +++ b/lib/events/EventManager.ts @@ -123,7 +123,6 @@ export default class EventManager { const result = await this.createVideoEvent(evt); if (result.createdEvent) { evt.videoCallData = result.createdEvent; - evt.location = result.createdEvent.url; } results.push(result); @@ -195,7 +194,6 @@ export default class EventManager { const result = await this.updateVideoEvent(evt, booking); if (result.updatedEvent) { evt.videoCallData = result.updatedEvent; - evt.location = result.updatedEvent.url; } results.push(result); } diff --git a/lib/integrations/Apple/AppleCalendarAdapter.ts b/lib/integrations/Apple/AppleCalendarAdapter.ts index e5cf684702..a2c3244755 100644 --- a/lib/integrations/Apple/AppleCalendarAdapter.ts +++ b/lib/integrations/Apple/AppleCalendarAdapter.ts @@ -14,6 +14,7 @@ import { } from "tsdav"; import { v4 as uuidv4 } from "uuid"; +import { getLocation, getRichDescription } from "@lib/CalEventParser"; import { symmetricDecrypt } from "@lib/crypto"; import logger from "@lib/logger"; @@ -79,8 +80,8 @@ export class AppleCalendar implements CalendarApiAdapter { start: this.convertDate(event.startTime), duration: this.getDuration(event.startTime, event.endTime), title: event.title, - description: event.description ?? "", - location: event.location, + description: getRichDescription(event), + location: getLocation(event), organizer: { email: event.organizer.email, name: event.organizer.name }, attendees: this.getAttendees(event.attendees), }); @@ -137,8 +138,8 @@ export class AppleCalendar implements CalendarApiAdapter { start: this.convertDate(event.startTime), duration: this.getDuration(event.startTime, event.endTime), title: event.title, - description: event.description ?? "", - location: event.location, + description: getRichDescription(event), + location: getLocation(event), organizer: { email: event.organizer.email, name: event.organizer.name }, attendees: this.getAttendees(event.attendees), }); diff --git a/lib/integrations/CalDav/CalDavCalendarAdapter.ts b/lib/integrations/CalDav/CalDavCalendarAdapter.ts index 69b27f0779..511c9cb0dc 100644 --- a/lib/integrations/CalDav/CalDavCalendarAdapter.ts +++ b/lib/integrations/CalDav/CalDavCalendarAdapter.ts @@ -14,6 +14,7 @@ import { } from "tsdav"; import { v4 as uuidv4 } from "uuid"; +import { getLocation, getRichDescription } from "@lib/CalEventParser"; import { symmetricDecrypt } from "@lib/crypto"; import logger from "@lib/logger"; @@ -82,8 +83,8 @@ export class CalDavCalendar implements CalendarApiAdapter { start: this.convertDate(event.startTime), duration: this.getDuration(event.startTime, event.endTime), title: event.title, - description: event.description ?? "", - location: event.location, + description: getRichDescription(event), + location: getLocation(event), organizer: { email: event.organizer.email, name: event.organizer.name }, attendees: this.getAttendees(event.attendees), }); @@ -141,8 +142,8 @@ export class CalDavCalendar implements CalendarApiAdapter { start: this.convertDate(event.startTime), duration: this.getDuration(event.startTime, event.endTime), title: event.title, - description: event.description ?? "", - location: event.location, + description: getRichDescription(event), + location: getLocation(event), organizer: { email: event.organizer.email, name: event.organizer.name }, attendees: this.getAttendees(event.attendees), }); diff --git a/lib/integrations/GoogleCalendar/GoogleCalendarApiAdapter.ts b/lib/integrations/GoogleCalendar/GoogleCalendarApiAdapter.ts index 047e72f6f7..306b595a7f 100644 --- a/lib/integrations/GoogleCalendar/GoogleCalendarApiAdapter.ts +++ b/lib/integrations/GoogleCalendar/GoogleCalendarApiAdapter.ts @@ -2,6 +2,7 @@ import { Credential, Prisma } from "@prisma/client"; import { GetTokenResponse } from "google-auth-library/build/src/auth/oauth2client"; import { Auth, calendar_v3, google } from "googleapis"; +import { getLocation, getRichDescription } from "@lib/CalEventParser"; import { CalendarApiAdapter, CalendarEvent, IntegrationCalendar } from "@lib/calendarClient"; import prisma from "@lib/prisma"; @@ -105,7 +106,7 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda auth.getToken().then((myGoogleAuth) => { const payload: calendar_v3.Schema$Event = { summary: event.title, - description: event.description, + description: getRichDescription(event), start: { dateTime: event.startTime, timeZone: event.organizer.timeZone, @@ -122,7 +123,7 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda }; if (event.location) { - payload["location"] = event.location; + payload["location"] = getLocation(event); } if (event.conferenceData && event.location === "integrations:google:meet") { @@ -155,7 +156,7 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda auth.getToken().then((myGoogleAuth) => { const payload: calendar_v3.Schema$Event = { summary: event.title, - description: event.description, + description: getRichDescription(event), start: { dateTime: event.startTime, timeZone: event.organizer.timeZone, @@ -171,7 +172,7 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda }; if (event.location) { - payload["location"] = event.location; + payload["location"] = getLocation(event); } const calendar = google.calendar({ diff --git a/lib/integrations/Office365Calendar/Office365CalendarApiAdapter.ts b/lib/integrations/Office365Calendar/Office365CalendarApiAdapter.ts index 1166079b72..e0843a8063 100644 --- a/lib/integrations/Office365Calendar/Office365CalendarApiAdapter.ts +++ b/lib/integrations/Office365Calendar/Office365CalendarApiAdapter.ts @@ -1,6 +1,7 @@ import { Calendar as OfficeCalendar } from "@microsoft/microsoft-graph-types-beta"; import { Credential } from "@prisma/client"; +import { getLocation, getRichDescription } from "@lib/CalEventParser"; import { CalendarApiAdapter, CalendarEvent, IntegrationCalendar } from "@lib/calendarClient"; import { handleErrorsJson, handleErrorsRaw } from "@lib/errors"; import prisma from "@lib/prisma"; @@ -65,7 +66,7 @@ export const Office365CalendarApiAdapter = (credential: Credential): CalendarApi subject: event.title, body: { contentType: "HTML", - content: event.description, + content: getRichDescription(event), }, start: { dateTime: event.startTime, @@ -82,7 +83,7 @@ export const Office365CalendarApiAdapter = (credential: Credential): CalendarApi }, type: "required", })), - location: event.location ? { displayName: event.location } : undefined, + location: event.location ? { displayName: getLocation(event) } : undefined, }; };