fix: calendar event fixes (#1260)
* fix: calendar event fixes * update after code review
This commit is contained in:
parent
c109ab1e30
commit
dd446abeec
9 changed files with 120 additions and 23 deletions
|
@ -1,15 +1,104 @@
|
||||||
|
import dayjs from "dayjs";
|
||||||
import short from "short-uuid";
|
import short from "short-uuid";
|
||||||
import { v5 as uuidv5 } from "uuid";
|
import { v5 as uuidv5 } from "uuid";
|
||||||
|
|
||||||
|
import { getIntegrationName } from "@lib/integrations";
|
||||||
|
|
||||||
import { CalendarEvent } from "./calendarClient";
|
import { CalendarEvent } from "./calendarClient";
|
||||||
import { BASE_URL } from "./config/constants";
|
import { BASE_URL } from "./config/constants";
|
||||||
|
|
||||||
const translator = short();
|
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));
|
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);
|
return BASE_URL + "/cancel/" + getUid(calEvent);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getRichDescription = (calEvent: CalendarEvent) => {
|
||||||
|
return `
|
||||||
|
${getWhat(calEvent)}
|
||||||
|
${getWhen(calEvent)}
|
||||||
|
${getWho(calEvent)}
|
||||||
|
${getLocation(calEvent)}
|
||||||
|
${getAdditionalNotes(calEvent)}
|
||||||
|
${getManageLink(calEvent)}
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
|
@ -181,7 +181,7 @@ const updateEvent = async (
|
||||||
const uid = getUid(calEvent);
|
const uid = getUid(calEvent);
|
||||||
let success = true;
|
let success = true;
|
||||||
|
|
||||||
const updationResult =
|
const updatedResult =
|
||||||
credential && bookingRefUid
|
credential && bookingRefUid
|
||||||
? await calendars([credential])[0]
|
? await calendars([credential])[0]
|
||||||
.updateEvent(bookingRefUid, calEvent)
|
.updateEvent(bookingRefUid, calEvent)
|
||||||
|
@ -192,7 +192,7 @@ const updateEvent = async (
|
||||||
})
|
})
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (!updationResult) {
|
if (!updatedResult) {
|
||||||
return {
|
return {
|
||||||
type: credential.type,
|
type: credential.type,
|
||||||
success,
|
success,
|
||||||
|
@ -205,7 +205,7 @@ const updateEvent = async (
|
||||||
type: credential.type,
|
type: credential.type,
|
||||||
success,
|
success,
|
||||||
uid,
|
uid,
|
||||||
updatedEvent: updationResult,
|
updatedEvent: updatedResult,
|
||||||
originalEvent: calEvent,
|
originalEvent: calEvent,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -121,6 +121,7 @@ export default class AttendeeScheduledEmail {
|
||||||
${this.calEvent.language("emailed_you_and_any_other_attendees")}
|
${this.calEvent.language("emailed_you_and_any_other_attendees")}
|
||||||
${this.getWhat()}
|
${this.getWhat()}
|
||||||
${this.getWhen()}
|
${this.getWhen()}
|
||||||
|
${this.getWho()}
|
||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
${this.calEvent.language("need_to_reschedule_or_cancel")}
|
${this.calEvent.language("need_to_reschedule_or_cancel")}
|
||||||
|
@ -380,7 +381,9 @@ ${this.getAdditionalNotes()}
|
||||||
<p style="height: 6px"></p>
|
<p style="height: 6px"></p>
|
||||||
<div style="line-height: 6px;">
|
<div style="line-height: 6px;">
|
||||||
<p style="color: #494949;">${this.calEvent.language("where")}</p>
|
<p style="color: #494949;">${this.calEvent.language("where")}</p>
|
||||||
<p style="color: #494949; font-weight: 400; line-height: 24px;">${providerName}</p>
|
<p style="color: #494949; font-weight: 400; line-height: 24px;">${
|
||||||
|
providerName || this.calEvent.location
|
||||||
|
}</p>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,7 @@ ${this.calEvent.language("new_event_scheduled")}
|
||||||
${this.calEvent.language("emailed_you_and_any_other_attendees")}
|
${this.calEvent.language("emailed_you_and_any_other_attendees")}
|
||||||
${this.getWhat()}
|
${this.getWhat()}
|
||||||
${this.getWhen()}
|
${this.getWhen()}
|
||||||
|
${this.getWho()}
|
||||||
${this.getLocation()}
|
${this.getLocation()}
|
||||||
${this.getAdditionalNotes()}
|
${this.getAdditionalNotes()}
|
||||||
${this.calEvent.language("need_to_reschedule_or_cancel")}
|
${this.calEvent.language("need_to_reschedule_or_cancel")}
|
||||||
|
@ -368,7 +369,9 @@ ${getCancelLink(this.calEvent)}
|
||||||
<p style="height: 6px"></p>
|
<p style="height: 6px"></p>
|
||||||
<div style="line-height: 6px;">
|
<div style="line-height: 6px;">
|
||||||
<p style="color: #494949;">${this.calEvent.language("where")}</p>
|
<p style="color: #494949;">${this.calEvent.language("where")}</p>
|
||||||
<p style="color: #494949; font-weight: 400; line-height: 24px;">${providerName}</p>
|
<p style="color: #494949; font-weight: 400; line-height: 24px;">${
|
||||||
|
providerName || this.calEvent.location
|
||||||
|
}</p>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,6 @@ export default class EventManager {
|
||||||
const result = await this.createVideoEvent(evt);
|
const result = await this.createVideoEvent(evt);
|
||||||
if (result.createdEvent) {
|
if (result.createdEvent) {
|
||||||
evt.videoCallData = result.createdEvent;
|
evt.videoCallData = result.createdEvent;
|
||||||
evt.location = result.createdEvent.url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results.push(result);
|
results.push(result);
|
||||||
|
@ -195,7 +194,6 @@ export default class EventManager {
|
||||||
const result = await this.updateVideoEvent(evt, booking);
|
const result = await this.updateVideoEvent(evt, booking);
|
||||||
if (result.updatedEvent) {
|
if (result.updatedEvent) {
|
||||||
evt.videoCallData = result.updatedEvent;
|
evt.videoCallData = result.updatedEvent;
|
||||||
evt.location = result.updatedEvent.url;
|
|
||||||
}
|
}
|
||||||
results.push(result);
|
results.push(result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
} from "tsdav";
|
} from "tsdav";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
|
import { getLocation, getRichDescription } from "@lib/CalEventParser";
|
||||||
import { symmetricDecrypt } from "@lib/crypto";
|
import { symmetricDecrypt } from "@lib/crypto";
|
||||||
import logger from "@lib/logger";
|
import logger from "@lib/logger";
|
||||||
|
|
||||||
|
@ -79,8 +80,8 @@ export class AppleCalendar implements CalendarApiAdapter {
|
||||||
start: this.convertDate(event.startTime),
|
start: this.convertDate(event.startTime),
|
||||||
duration: this.getDuration(event.startTime, event.endTime),
|
duration: this.getDuration(event.startTime, event.endTime),
|
||||||
title: event.title,
|
title: event.title,
|
||||||
description: event.description ?? "",
|
description: getRichDescription(event),
|
||||||
location: event.location,
|
location: getLocation(event),
|
||||||
organizer: { email: event.organizer.email, name: event.organizer.name },
|
organizer: { email: event.organizer.email, name: event.organizer.name },
|
||||||
attendees: this.getAttendees(event.attendees),
|
attendees: this.getAttendees(event.attendees),
|
||||||
});
|
});
|
||||||
|
@ -137,8 +138,8 @@ export class AppleCalendar implements CalendarApiAdapter {
|
||||||
start: this.convertDate(event.startTime),
|
start: this.convertDate(event.startTime),
|
||||||
duration: this.getDuration(event.startTime, event.endTime),
|
duration: this.getDuration(event.startTime, event.endTime),
|
||||||
title: event.title,
|
title: event.title,
|
||||||
description: event.description ?? "",
|
description: getRichDescription(event),
|
||||||
location: event.location,
|
location: getLocation(event),
|
||||||
organizer: { email: event.organizer.email, name: event.organizer.name },
|
organizer: { email: event.organizer.email, name: event.organizer.name },
|
||||||
attendees: this.getAttendees(event.attendees),
|
attendees: this.getAttendees(event.attendees),
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
} from "tsdav";
|
} from "tsdav";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
|
||||||
|
import { getLocation, getRichDescription } from "@lib/CalEventParser";
|
||||||
import { symmetricDecrypt } from "@lib/crypto";
|
import { symmetricDecrypt } from "@lib/crypto";
|
||||||
import logger from "@lib/logger";
|
import logger from "@lib/logger";
|
||||||
|
|
||||||
|
@ -82,8 +83,8 @@ export class CalDavCalendar implements CalendarApiAdapter {
|
||||||
start: this.convertDate(event.startTime),
|
start: this.convertDate(event.startTime),
|
||||||
duration: this.getDuration(event.startTime, event.endTime),
|
duration: this.getDuration(event.startTime, event.endTime),
|
||||||
title: event.title,
|
title: event.title,
|
||||||
description: event.description ?? "",
|
description: getRichDescription(event),
|
||||||
location: event.location,
|
location: getLocation(event),
|
||||||
organizer: { email: event.organizer.email, name: event.organizer.name },
|
organizer: { email: event.organizer.email, name: event.organizer.name },
|
||||||
attendees: this.getAttendees(event.attendees),
|
attendees: this.getAttendees(event.attendees),
|
||||||
});
|
});
|
||||||
|
@ -141,8 +142,8 @@ export class CalDavCalendar implements CalendarApiAdapter {
|
||||||
start: this.convertDate(event.startTime),
|
start: this.convertDate(event.startTime),
|
||||||
duration: this.getDuration(event.startTime, event.endTime),
|
duration: this.getDuration(event.startTime, event.endTime),
|
||||||
title: event.title,
|
title: event.title,
|
||||||
description: event.description ?? "",
|
description: getRichDescription(event),
|
||||||
location: event.location,
|
location: getLocation(event),
|
||||||
organizer: { email: event.organizer.email, name: event.organizer.name },
|
organizer: { email: event.organizer.email, name: event.organizer.name },
|
||||||
attendees: this.getAttendees(event.attendees),
|
attendees: this.getAttendees(event.attendees),
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Credential, Prisma } from "@prisma/client";
|
||||||
import { GetTokenResponse } from "google-auth-library/build/src/auth/oauth2client";
|
import { GetTokenResponse } from "google-auth-library/build/src/auth/oauth2client";
|
||||||
import { Auth, calendar_v3, google } from "googleapis";
|
import { Auth, calendar_v3, google } from "googleapis";
|
||||||
|
|
||||||
|
import { getLocation, getRichDescription } from "@lib/CalEventParser";
|
||||||
import { CalendarApiAdapter, CalendarEvent, IntegrationCalendar } from "@lib/calendarClient";
|
import { CalendarApiAdapter, CalendarEvent, IntegrationCalendar } from "@lib/calendarClient";
|
||||||
import prisma from "@lib/prisma";
|
import prisma from "@lib/prisma";
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda
|
||||||
auth.getToken().then((myGoogleAuth) => {
|
auth.getToken().then((myGoogleAuth) => {
|
||||||
const payload: calendar_v3.Schema$Event = {
|
const payload: calendar_v3.Schema$Event = {
|
||||||
summary: event.title,
|
summary: event.title,
|
||||||
description: event.description,
|
description: getRichDescription(event),
|
||||||
start: {
|
start: {
|
||||||
dateTime: event.startTime,
|
dateTime: event.startTime,
|
||||||
timeZone: event.organizer.timeZone,
|
timeZone: event.organizer.timeZone,
|
||||||
|
@ -122,7 +123,7 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda
|
||||||
};
|
};
|
||||||
|
|
||||||
if (event.location) {
|
if (event.location) {
|
||||||
payload["location"] = event.location;
|
payload["location"] = getLocation(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.conferenceData && event.location === "integrations:google:meet") {
|
if (event.conferenceData && event.location === "integrations:google:meet") {
|
||||||
|
@ -155,7 +156,7 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda
|
||||||
auth.getToken().then((myGoogleAuth) => {
|
auth.getToken().then((myGoogleAuth) => {
|
||||||
const payload: calendar_v3.Schema$Event = {
|
const payload: calendar_v3.Schema$Event = {
|
||||||
summary: event.title,
|
summary: event.title,
|
||||||
description: event.description,
|
description: getRichDescription(event),
|
||||||
start: {
|
start: {
|
||||||
dateTime: event.startTime,
|
dateTime: event.startTime,
|
||||||
timeZone: event.organizer.timeZone,
|
timeZone: event.organizer.timeZone,
|
||||||
|
@ -171,7 +172,7 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda
|
||||||
};
|
};
|
||||||
|
|
||||||
if (event.location) {
|
if (event.location) {
|
||||||
payload["location"] = event.location;
|
payload["location"] = getLocation(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
const calendar = google.calendar({
|
const calendar = google.calendar({
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Calendar as OfficeCalendar } from "@microsoft/microsoft-graph-types-beta";
|
import { Calendar as OfficeCalendar } from "@microsoft/microsoft-graph-types-beta";
|
||||||
import { Credential } from "@prisma/client";
|
import { Credential } from "@prisma/client";
|
||||||
|
|
||||||
|
import { getLocation, getRichDescription } from "@lib/CalEventParser";
|
||||||
import { CalendarApiAdapter, CalendarEvent, IntegrationCalendar } from "@lib/calendarClient";
|
import { CalendarApiAdapter, CalendarEvent, IntegrationCalendar } from "@lib/calendarClient";
|
||||||
import { handleErrorsJson, handleErrorsRaw } from "@lib/errors";
|
import { handleErrorsJson, handleErrorsRaw } from "@lib/errors";
|
||||||
import prisma from "@lib/prisma";
|
import prisma from "@lib/prisma";
|
||||||
|
@ -65,7 +66,7 @@ export const Office365CalendarApiAdapter = (credential: Credential): CalendarApi
|
||||||
subject: event.title,
|
subject: event.title,
|
||||||
body: {
|
body: {
|
||||||
contentType: "HTML",
|
contentType: "HTML",
|
||||||
content: event.description,
|
content: getRichDescription(event),
|
||||||
},
|
},
|
||||||
start: {
|
start: {
|
||||||
dateTime: event.startTime,
|
dateTime: event.startTime,
|
||||||
|
@ -82,7 +83,7 @@ export const Office365CalendarApiAdapter = (credential: Credential): CalendarApi
|
||||||
},
|
},
|
||||||
type: "required",
|
type: "required",
|
||||||
})),
|
})),
|
||||||
location: event.location ? { displayName: event.location } : undefined,
|
location: event.location ? { displayName: getLocation(event) } : undefined,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue