import { BookingStatus } from "@prisma/client"; import { createHmac } from "crypto"; import { instance } from "gaxios"; import { GetServerSidePropsContext } from "next"; import { useRouter } from "next/router"; import { useState } from "react"; import z from "zod"; import { getEventLocationValue, getSuccessPageLocationMessage } from "@calcom/app-store/locations"; import dayjs from "@calcom/dayjs"; import { getRecurringWhen } from "@calcom/emails/src/components/WhenInfo"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent"; import { processBookingConfirmation } from "@calcom/lib/server/queries/bookings/confirm"; import prisma from "@calcom/prisma"; import { TRPCError } from "@calcom/trpc/server"; import { inferSSRProps } from "@calcom/types/inferSSRProps"; import { Button, Icon, TextArea } from "@calcom/ui"; import { HeadSeo } from "@components/seo/head-seo"; enum DirectAction { "accept" = "accept", "reject" = "reject", } const actionSchema = z.nativeEnum(DirectAction); const refineParse = (result: z.SafeParseReturnType, context: z.RefinementCtx) => { if (result.success === false) { result.error.issues.map((issue) => context.addIssue(issue)); } }; const CALENDSO_ENCRYPTION_KEY = process.env.CALENDSO_ENCRYPTION_KEY || ""; const pageErrors = { signature_mismatch: "Direct link signature doesn't match signed data", booking_not_found: "Direct link booking not found", user_not_found: "Direct link booking user not found", }; const requestSchema = z.object({ link: z .array(z.string()) .max(4) .superRefine((data, ctx) => { refineParse(actionSchema.safeParse(data[0]), ctx); const signedData = `${data[1]}/${data[2]}`; const sig = createHmac("sha1", CALENDSO_ENCRYPTION_KEY).update(signedData).digest("base64"); if (data[3] !== sig) { ctx.addIssue({ message: pageErrors.signature_mismatch, code: "custom", }); console.log(signedData, data, data[3], "==", sig); } }), reason: z.string().optional(), }); function bookingContent(status: BookingStatus | undefined | null) { switch (status) { case BookingStatus.PENDING: // Trying to reject booking without reason return { iconColor: "gray", Icon: Icon.FiCalendar, titleKey: "event_awaiting_approval", subtitleKey: "someone_requested_an_event", }; case BookingStatus.ACCEPTED: // Booking was acepted successfully return { iconColor: "green", Icon: Icon.FiCheck, titleKey: "booking_confirmed", subtitleKey: "emailed_you_and_any_other_attendees", }; case BookingStatus.REJECTED: // Booking was rejected successfully return { iconColor: "red", Icon: Icon.FiX, titleKey: "booking_rejection_success", subtitleKey: "emailed_you_and_any_other_attendees", }; default: // Booking was already accepted or rejected return { iconColor: "yellow", Icon: Icon.FiAlertTriangle, titleKey: "booking_already_accepted_rejected", }; } } export default function Directlink({ booking, reason, status }: inferSSRProps) { const { t } = useLocale(); const router = useRouter(); const acceptPath = router.asPath.replace("reject", "accept"); const rejectPath = router.asPath.replace("accept", "reject"); const [cancellationReason, setCancellationReason] = useState(""); function getRecipientStart(format: string) { return dayjs(booking.startTime).tz(booking?.user?.timeZone).format(format); } function getRecipientEnd(format: string) { return dayjs(booking.endTime).tz(booking?.user?.timeZone).format(format); } const organizer = { ...booking.attendees[0], language: { translate: t, locale: booking.attendees[0].locale ?? "en", }, }; const location: ReturnType = Array.isArray(booking.location) ? booking.location[0] : // If there is no location set then we default to Cal Video "integrations:daily"; const locationToDisplay = getSuccessPageLocationMessage(location, t); const content = bookingContent(status); const recurringInfo = getRecurringWhen({ recurringEvent: booking.eventType?.recurringEvent, attendee: organizer, }); return ( <>