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 { 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)} | ||||
|   `;
 | ||||
| }; | ||||
|  |  | |||
|  | @ -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, | ||||
|   }; | ||||
| }; | ||||
|  |  | |||
|  | @ -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()} | |||
|     <p style="height: 6px"></p> | ||||
|     <div style="line-height: 6px;"> | ||||
|       <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> | ||||
|     `;
 | ||||
|   } | ||||
|  |  | |||
|  | @ -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)} | |||
|     <p style="height: 6px"></p> | ||||
|     <div style="line-height: 6px;"> | ||||
|       <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> | ||||
|     `;
 | ||||
|   } | ||||
|  |  | |||
|  | @ -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); | ||||
|     } | ||||
|  |  | |||
|  | @ -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), | ||||
|       }); | ||||
|  |  | |||
|  | @ -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), | ||||
|       }); | ||||
|  |  | |||
|  | @ -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({ | ||||
|  |  | |||
|  | @ -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, | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Mihai C
						Mihai C