diff --git a/helpers/ical.ts b/helpers/ical.ts index 88b7e83..5a287ab 100644 --- a/helpers/ical.ts +++ b/helpers/ical.ts @@ -1,4 +1,4 @@ -import { createEvents, EventStatus } from 'ics' +import { createEvents, createEvent, EventStatus } from 'ics' import { Booking } from '../db/booking' import { BOOKING_STATUS } from '../db/enums' import { getBaseURL } from './url' @@ -12,11 +12,39 @@ function convertDay(value: string): [number, number, number] { return [Number(parts[0]), Number(parts[1]), Number(parts[2])] } +export function generateCalendarEntry(booking: Booking): string { + const { error, value } = createEvent({ + productId: 'app.vercel.pfadi-bussle/ics', + title: `Pfadi-Bussle Buchung`, + start: convertDay(booking.days[0]), + startOutputType: 'local', + duration: { days: booking.days.length }, + location: 'Mömpelgardgasse 25, 72348 Rosenfeld, Deutschland', + geo: { lat: 48.287044, lon: 8.726361 }, + description: `Gebucht auf ${booking.booker.name} + +Buchungs-Link: ${getBaseURL()}/booking/${booking.uuid} +`, + status: + booking.status === BOOKING_STATUS.CONFIRMED + ? ('CONFIRMED' as EventStatus) + : ('TENTATIVE' as EventStatus), + }) + + if (error) { + throw error + } + + return value +} + export function generateBookedCalendar(bookings: Booking[]) { const events = bookings.map((booking) => ({ + productId: 'app.vercel.pfadi-bussle/ics', calName: 'Pfadi-Bussle Buchungen', start: convertDay(booking.days[0]), - end: convertDay(booking.days[booking.days.length - 1]), + startOutputType: 'local', + duration: { days: booking.days.length }, title: `Buchung ${booking.booker.name}`, description: `Name: ${booking.booker.name} Zeitraum: ${daysFormatFrontend(booking.days)} diff --git a/helpers/mail.ts b/helpers/mail.ts index 0b6ced8..886468a 100644 --- a/helpers/mail.ts +++ b/helpers/mail.ts @@ -1,6 +1,7 @@ import { Booking } from '../db/booking' import { getBaseURL } from '../helpers/url' import { daysFormatFrontend } from './date' +import { generateCalendarEntry } from './ical' const SENDGRID_API_KEY = process.env.SENDGRID_API_KEY const ADMIN_EMAIL = process.env.ADMIN_EMAIL @@ -93,7 +94,10 @@ export async function sendReceivedBookingAdminMail(booking: Booking) { textPlainContent: getReceivedBookingAdminText(booking), }) } catch (error) { - console.error(`Failed in sendReceivedBookingMail for ${booking.uuid}`) + console.error( + `Failed in sendReceivedBookingMail for ${booking.uuid}`, + error + ) } } @@ -106,7 +110,10 @@ export async function sendReceivedBookingBookerMail(booking: Booking) { textPlainContent: getReceivedBookingBookerText(booking), }) } catch (error) { - console.error(`Failed in sendReceivedBookingMail for ${booking.uuid}`) + console.error( + `Failed in sendReceivedBookingMail for ${booking.uuid}`, + error + ) } } @@ -117,9 +124,21 @@ export async function sendBookingConfirmed(booking: Booking) { from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' }, subject: `Deine Pfadi-Bussle Buchung wurde bestätigt!`, textPlainContent: getBookingConfirmedText(booking), + attachments: [ + { + content: Buffer.from(generateCalendarEntry(booking)).toString( + 'base64' + ), + type: 'text/calendar', + filename: 'pfadibussle-buchung.ics', + }, + ], }) } catch (error) { - console.error(`Failed in sendBookingConfirmedMail for ${booking.uuid}`) + console.error( + `Failed in sendBookingConfirmedMail for ${booking.uuid}`, + error + ) } } @@ -132,7 +151,10 @@ export async function sendBookingRejected(booking: Booking) { textPlainContent: getBookingRejectedText(booking), }) } catch (error) { - console.error(`Failed in sendBookingRejectedMail for ${booking.uuid}`) + console.error( + `Failed in sendBookingRejectedMail for ${booking.uuid}`, + error + ) } } @@ -141,11 +163,17 @@ async function sendMail({ from, subject, textPlainContent, + attachments, }: { to: { email: string; name?: string }[] from: { email: string; name?: string } subject: string textPlainContent: string + attachments: { + content: string + type?: string + filename: string + }[] }) { const data = { personalizations: [ @@ -156,6 +184,7 @@ async function sendMail({ from, subject, content: [{ type: 'text/plain', value: textPlainContent }], + attachments, } const fetchOptions = { @@ -166,9 +195,12 @@ async function sendMail({ }, body: JSON.stringify(data), } - const response = await fetch(SENDGRID_URL, fetchOptions) + const resp = await fetch(SENDGRID_URL, fetchOptions) + const bodyText = await resp.text() - if (!response.ok) { - throw new Error(`Unable to send mail`) + if (!resp.ok) { + throw new Error( + `Unable to send mail, status ${resp.status} ${resp.statusText}, ${bodyText}` + ) } }