From fd76bc9cab3a1398751efb253e42e2b3879f5c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Efra=C3=ADn=20Roch=C3=ADn?= Date: Fri, 17 Feb 2023 12:53:31 -0700 Subject: [PATCH] Reduce booking page bundle size (#7131) * AvailabilityPage Component loaded dynamically * update next/bundle-analyzer dependency * Load dynamically SlotPicker and TimezoneDropDown and make some imports refactoring * Gates functionality Returned * Replace crypto with md5 dependency to generate gravatar url * Refactor: AvailableTimes and SlotPicker * Update next.config.js * Update SlotPicker.tsx * Update getEventTypeAppData.ts --------- Co-authored-by: zomars --- .../web/components/booking/AvailableTimes.tsx | 230 +++++++++--------- .../components/booking/BookingDescription.tsx | 2 +- apps/web/components/booking/SlotPicker.tsx | 200 +++++++++++++++ .../components/booking/TimezoneDropdown.tsx | 26 ++ .../booking/pages/AvailabilityPage.tsx | 230 +----------------- apps/web/package.json | 4 +- apps/web/pages/[user]/[type].tsx | 34 +-- packages/app-store/BookingPageTagManager.tsx | 2 +- .../app-store/_utils/getEventTypeAppData.ts | 47 ++++ packages/app-store/utils.ts | 48 +--- packages/core/EventManager.ts | 8 +- packages/core/getBusyTimes.ts | 2 +- .../emails/src/templates/TeamInviteEmail.tsx | 1 - .../features/ee/teams/components/TeamList.tsx | 2 +- .../lib/reminders/smsReminderManager.ts | 1 - .../components/CreateEventTypeButton.tsx | 2 +- .../settings/layouts/SettingsLayout.tsx | 1 - packages/lib/defaultAvatarImage.ts | 4 +- packages/lib/getPaymentAppData.ts | 2 +- .../trpc/server/routers/viewer/workflows.tsx | 2 +- yarn.lock | 149 ++++++++---- 21 files changed, 533 insertions(+), 464 deletions(-) create mode 100644 apps/web/components/booking/SlotPicker.tsx create mode 100644 apps/web/components/booking/TimezoneDropdown.tsx create mode 100644 packages/app-store/_utils/getEventTypeAppData.ts diff --git a/apps/web/components/booking/AvailableTimes.tsx b/apps/web/components/booking/AvailableTimes.tsx index 2a05ba94e3..cbd14dd092 100644 --- a/apps/web/components/booking/AvailableTimes.tsx +++ b/apps/web/components/booking/AvailableTimes.tsx @@ -1,3 +1,4 @@ +import { useAutoAnimate } from "@formkit/auto-animate/react"; import Link from "next/link"; import { useRouter } from "next/router"; import type { FC } from "react"; @@ -39,6 +40,7 @@ const AvailableTimes: FC = ({ seatsPerTimeSlot, ethSignature, }) => { + const [slotPickerRef] = useAutoAnimate(); const { t, i18n } = useLocale(); const router = useRouter(); const { rescheduleUid } = router.query; @@ -49,122 +51,124 @@ const AvailableTimes: FC = ({ setBrand(getComputedStyle(document.documentElement).getPropertyValue("--brand-color").trim()); }, []); - if (!date) return null; - return ( -
-
-
- - {nameOfDay(i18n.language, Number(date.format("d")), "short")} - - - , {date.toDate().toLocaleString(i18n.language, { month: "short" })} {date.format(" D ")} - -
-
- onTimeFormatChange(timeFormat === "24")} - defaultValue={timeFormat === TimeFormat.TWELVE_HOUR ? "12" : "24"} - options={[ - { value: "12", label: t("12_hour_short") }, - { value: "24", label: t("24_hour_short") }, - ]} - /> -
-
-
- {slots.length > 0 && - slots.map((slot) => { - type BookingURL = { - pathname: string; - query: Record; - }; - const bookingUrl: BookingURL = { - pathname: router.pathname.endsWith("/embed") ? "../book" : "book", - query: { - ...router.query, - date: dayjs.utc(slot.time).tz(timeZone()).format(), - type: eventTypeId, - slug: eventTypeSlug, - /** Treat as recurring only when a count exist and it's not a rescheduling workflow */ - count: recurringCount && !rescheduleUid ? recurringCount : undefined, - ...(ethSignature ? { ethSignature } : {}), - }, - }; - - if (rescheduleUid) { - bookingUrl.query.rescheduleUid = rescheduleUid as string; - } - - // If event already has an attendee add booking id - if (slot.bookingUid) { - bookingUrl.query.bookingUid = slot.bookingUid; - } - - return ( -
- {/* ^ data-slot-owner is helpful in debugging and used to identify the owners of the slot. Owners are the users which have the timeslot in their schedule. It doesn't consider if a user has that timeslot booked */} - {/* Current there is no way to disable Next.js Links */} - {seatsPerTimeSlot && slot.attendees && slot.attendees >= seatsPerTimeSlot ? ( -
- {dayjs(slot.time).tz(timeZone()).format(timeFormat)} - {!!seatsPerTimeSlot &&

{t("booking_full")}

} -
- ) : ( - - {dayjs(slot.time).tz(timeZone()).format(timeFormat)} - {!!seatsPerTimeSlot && ( -

= 0.8 - ? "text-rose-600" - : slot.attendees && slot.attendees / seatsPerTimeSlot >= 0.33 - ? "text-yellow-500" - : "text-emerald-400" - } text-sm`}> - {slot.attendees ? seatsPerTimeSlot - slot.attendees : seatsPerTimeSlot} /{" "} - {seatsPerTimeSlot} {t("seats_available")} -

- )} - - )} -
- ); - })} - - {!isLoading && !slots.length && ( -
-

{t("all_booked_today")}

+
+ {!!date ? ( +
+
+
+ + {nameOfDay(i18n.language, Number(date.format("d")), "short")} + + + , {date.toDate().toLocaleString(i18n.language, { month: "short" })} {date.format(" D ")} + +
+
+ onTimeFormatChange(timeFormat === "24")} + defaultValue={timeFormat === TimeFormat.TWELVE_HOUR ? "12" : "24"} + options={[ + { value: "12", label: t("12_hour_short") }, + { value: "24", label: t("24_hour_short") }, + ]} + /> +
- )} +
+ {slots.length > 0 && + slots.map((slot) => { + type BookingURL = { + pathname: string; + query: Record; + }; + const bookingUrl: BookingURL = { + pathname: router.pathname.endsWith("/embed") ? "../book" : "book", + query: { + ...router.query, + date: dayjs.utc(slot.time).tz(timeZone()).format(), + type: eventTypeId, + slug: eventTypeSlug, + /** Treat as recurring only when a count exist and it's not a rescheduling workflow */ + count: recurringCount && !rescheduleUid ? recurringCount : undefined, + ...(ethSignature ? { ethSignature } : {}), + }, + }; - {isLoading && !slots.length && ( - <> - - - - - - - - - - - )} -
+ if (rescheduleUid) { + bookingUrl.query.rescheduleUid = rescheduleUid as string; + } + + // If event already has an attendee add booking id + if (slot.bookingUid) { + bookingUrl.query.bookingUid = slot.bookingUid; + } + + return ( +
+ {/* ^ data-slot-owner is helpful in debugging and used to identify the owners of the slot. Owners are the users which have the timeslot in their schedule. It doesn't consider if a user has that timeslot booked */} + {/* Current there is no way to disable Next.js Links */} + {seatsPerTimeSlot && slot.attendees && slot.attendees >= seatsPerTimeSlot ? ( +
+ {dayjs(slot.time).tz(timeZone()).format(timeFormat)} + {!!seatsPerTimeSlot &&

{t("booking_full")}

} +
+ ) : ( + + {dayjs(slot.time).tz(timeZone()).format(timeFormat)} + {!!seatsPerTimeSlot && ( +

= 0.8 + ? "text-rose-600" + : slot.attendees && slot.attendees / seatsPerTimeSlot >= 0.33 + ? "text-yellow-500" + : "text-emerald-400" + } text-sm`}> + {slot.attendees ? seatsPerTimeSlot - slot.attendees : seatsPerTimeSlot} /{" "} + {seatsPerTimeSlot} {t("seats_available")} +

+ )} + + )} +
+ ); + })} + + {!isLoading && !slots.length && ( +
+

{t("all_booked_today")}

+
+ )} + + {isLoading && !slots.length && ( + <> + + + + + + + + + + + )} +
+
+ ) : null}
); }; diff --git a/apps/web/components/booking/BookingDescription.tsx b/apps/web/components/booking/BookingDescription.tsx index 2a639616b2..5c9f067a4c 100644 --- a/apps/web/components/booking/BookingDescription.tsx +++ b/apps/web/components/booking/BookingDescription.tsx @@ -3,7 +3,7 @@ import type { FC, ReactNode } from "react"; import { useEffect } from "react"; import dayjs from "@calcom/dayjs"; -import { classNames } from "@calcom/lib"; +import classNames from "@calcom/lib/classNames"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Badge } from "@calcom/ui"; import { FiCheckSquare, FiClock, FiInfo } from "@calcom/ui/components/icon"; diff --git a/apps/web/components/booking/SlotPicker.tsx b/apps/web/components/booking/SlotPicker.tsx new file mode 100644 index 0000000000..3fb71b6ce0 --- /dev/null +++ b/apps/web/components/booking/SlotPicker.tsx @@ -0,0 +1,200 @@ +import type { EventType } from "@prisma/client"; +import dynamic from "next/dynamic"; +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; +import type { z } from "zod"; + +import type { Dayjs } from "@calcom/dayjs"; +import dayjs from "@calcom/dayjs"; +import DatePicker from "@calcom/features/calendars/DatePicker"; +import classNames from "@calcom/lib/classNames"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import type { TimeFormat } from "@calcom/lib/timeFormat"; +import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import { trpc } from "@calcom/trpc/react"; + +import useRouterQuery from "@lib/hooks/useRouterQuery"; + +const AvailableTimes = dynamic(() => import("@components/booking/AvailableTimes")); + +const getRefetchInterval = (refetchCount: number): number => { + const intervals = [3000, 3000, 5000, 10000, 20000, 30000] as const; + return intervals[refetchCount] || intervals[intervals.length - 1]; +}; + +const useSlots = ({ + eventTypeId, + eventTypeSlug, + startTime, + endTime, + usernameList, + timeZone, + duration, + enabled = true, +}: { + eventTypeId: number; + eventTypeSlug: string; + startTime?: Dayjs; + endTime?: Dayjs; + usernameList: string[]; + timeZone?: string; + duration?: string; + enabled?: boolean; +}) => { + const [refetchCount, setRefetchCount] = useState(0); + const refetchInterval = getRefetchInterval(refetchCount); + const { data, isLoading, isPaused, fetchStatus } = trpc.viewer.public.slots.getSchedule.useQuery( + { + eventTypeId, + eventTypeSlug, + usernameList, + startTime: startTime?.toISOString() || "", + endTime: endTime?.toISOString() || "", + timeZone, + duration, + }, + { + enabled: !!startTime && !!endTime && enabled, + refetchInterval, + trpc: { context: { skipBatch: true } }, + } + ); + useEffect(() => { + if (!!data && fetchStatus === "idle") { + setRefetchCount(refetchCount + 1); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fetchStatus, data]); + + // The very first time isPaused is set if auto-fetch is disabled, so isPaused should also be considered a loading state. + return { slots: data?.slots || {}, isLoading: isLoading || isPaused }; +}; + +export const SlotPicker = ({ + eventType, + timeFormat, + onTimeFormatChange, + timeZone, + recurringEventCount, + users, + seatsPerTimeSlot, + weekStart = 0, + ethSignature, +}: { + eventType: Pick< + EventType & { metadata: z.infer }, + "id" | "schedulingType" | "slug" | "length" | "metadata" + >; + timeFormat: TimeFormat; + onTimeFormatChange: (is24Hour: boolean) => void; + timeZone?: string; + seatsPerTimeSlot?: number; + recurringEventCount?: number; + users: string[]; + weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6; + ethSignature?: string; +}) => { + const [selectedDate, setSelectedDate] = useState(); + const [browsingDate, setBrowsingDate] = useState(); + let { duration = eventType.length.toString() } = useRouterQuery("duration"); + const { date, setQuery: setDate } = useRouterQuery("date"); + const { month, setQuery: setMonth } = useRouterQuery("month"); + const router = useRouter(); + + if (!eventType.metadata?.multipleDuration) { + duration = eventType.length.toString(); + } + + useEffect(() => { + if (!router.isReady) return; + + // Etc/GMT is not actually a timeZone, so handle this select option explicitly to prevent a hard crash. + if (timeZone === "Etc/GMT") { + setBrowsingDate(dayjs.utc(month).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0)); + if (date) { + setSelectedDate(dayjs.utc(date)); + } + } else { + // Set the start of the month without shifting time like startOf() may do. + setBrowsingDate( + dayjs.tz(month, timeZone).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0) + ); + if (date) { + // It's important to set the date immediately to the timeZone, dayjs(date) will convert to browsertime. + setSelectedDate(dayjs.tz(date, timeZone)); + } + } + }, [router.isReady, month, date, duration, timeZone]); + + const { i18n, isLocaleReady } = useLocale(); + const { slots: monthSlots, isLoading } = useSlots({ + eventTypeId: eventType.id, + eventTypeSlug: eventType.slug, + usernameList: users, + startTime: + browsingDate === undefined || browsingDate.get("month") === dayjs.tz(undefined, timeZone).get("month") + ? dayjs.tz(undefined, timeZone).subtract(2, "days").startOf("day") + : browsingDate?.startOf("month"), + endTime: browsingDate?.endOf("month"), + timeZone, + duration, + }); + const { slots: selectedDateSlots, isLoading: _isLoadingSelectedDateSlots } = useSlots({ + eventTypeId: eventType.id, + eventTypeSlug: eventType.slug, + usernameList: users, + startTime: selectedDate?.startOf("day"), + endTime: selectedDate?.endOf("day"), + timeZone, + duration, + /** Prevent refetching is we already have this data from month slots */ + enabled: !!selectedDate, + }); + + /** Hide skeleton if we have the slot loaded in the month query */ + const isLoadingSelectedDateSlots = (() => { + if (!selectedDate) return _isLoadingSelectedDateSlots; + if (!!selectedDateSlots[selectedDate.format("YYYY-MM-DD")]) return false; + if (!!monthSlots[selectedDate.format("YYYY-MM-DD")]) return false; + return false; + })(); + + return ( + <> + monthSlots[k].length > 0)} + locale={isLocaleReady ? i18n.language : "en"} + selected={selectedDate} + onChange={(newDate) => { + setDate(newDate.format("YYYY-MM-DD")); + }} + onMonthChange={(newMonth) => { + setMonth(newMonth.format("YYYY-MM")); + }} + browsingDate={browsingDate} + weekStart={weekStart} + /> + + + ); +}; diff --git a/apps/web/components/booking/TimezoneDropdown.tsx b/apps/web/components/booking/TimezoneDropdown.tsx new file mode 100644 index 0000000000..195a9acec7 --- /dev/null +++ b/apps/web/components/booking/TimezoneDropdown.tsx @@ -0,0 +1,26 @@ +import { FiGlobe } from "@calcom/ui/components/icon"; + +import { timeZone as localStorageTimeZone } from "@lib/clock"; + +import TimeOptions from "@components/booking/TimeOptions"; + +export function TimezoneDropdown({ + onChangeTimeZone, +}: { + onChangeTimeZone: (newTimeZone: string) => void; + timeZone?: string; +}) { + const handleSelectTimeZone = (newTimeZone: string) => { + onChangeTimeZone(newTimeZone); + localStorageTimeZone(newTimeZone); + }; + + return ( + <> +
+ + +
+ + ); +} diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 783933d9f3..7ba03a4e71 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -1,15 +1,11 @@ -import { useAutoAnimate } from "@formkit/auto-animate/react"; -import type { EventType } from "@prisma/client"; import dynamic from "next/dynamic"; import { useRouter } from "next/router"; import { useEffect, useMemo, useReducer, useState } from "react"; -import { Toaster } from "react-hot-toast"; import { FormattedNumber, IntlProvider } from "react-intl"; import { z } from "zod"; import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager"; import { getEventTypeAppData } from "@calcom/app-store/utils"; -import type { Dayjs } from "@calcom/dayjs"; import dayjs from "@calcom/dayjs"; import { useEmbedNonStylesConfig, @@ -18,7 +14,6 @@ import { useIsBackgroundTransparent, useIsEmbed, } from "@calcom/embed-core/embed-iframe"; -import DatePicker from "@calcom/features/calendars/DatePicker"; import CustomBranding from "@calcom/lib/CustomBranding"; import classNames from "@calcom/lib/classNames"; import getPaymentAppData from "@calcom/lib/getPaymentAppData"; @@ -28,235 +23,30 @@ import notEmpty from "@calcom/lib/notEmpty"; import { getRecurringFreq } from "@calcom/lib/recurringStrings"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import { detectBrowserTimeFormat, setIs24hClockInLocalStorage, TimeFormat } from "@calcom/lib/timeFormat"; -import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; -import { trpc } from "@calcom/trpc/react"; import { HeadSeo } from "@calcom/ui"; -import { FiCreditCard, FiGlobe, FiRefreshCcw } from "@calcom/ui/components/icon"; +import { FiCreditCard, FiRefreshCcw } from "@calcom/ui/components/icon"; import { timeZone as localStorageTimeZone } from "@lib/clock"; -import useRouterQuery from "@lib/hooks/useRouterQuery"; import type { Gate, GateState } from "@components/Gates"; import Gates from "@components/Gates"; import BookingDescription from "@components/booking/BookingDescription"; -import TimeOptions from "@components/booking/TimeOptions"; +import { SlotPicker } from "@components/booking/SlotPicker"; import type { AvailabilityPageProps } from "../../../pages/[user]/[type]"; import type { DynamicAvailabilityPageProps } from "../../../pages/d/[link]/[slug]"; import type { AvailabilityTeamPageProps } from "../../../pages/team/[slug]/[type]"; -const getRefetchInterval = (refetchCount: number): number => { - const intervals = [3000, 3000, 5000, 10000, 20000, 30000] as const; - return intervals[refetchCount] || intervals[intervals.length - 1]; -}; - const PoweredByCal = dynamic(() => import("@components/ui/PoweredByCal")); -const AvailableTimes = dynamic(() => import("@components/booking/AvailableTimes")); -const useSlots = ({ - eventTypeId, - eventTypeSlug, - startTime, - endTime, - usernameList, - timeZone, - duration, - enabled = true, -}: { - eventTypeId: number; - eventTypeSlug: string; - startTime?: Dayjs; - endTime?: Dayjs; - usernameList: string[]; - timeZone?: string; - duration?: string; - enabled?: boolean; -}) => { - const [refetchCount, setRefetchCount] = useState(0); - const refetchInterval = getRefetchInterval(refetchCount); - const { data, isLoading, isPaused, fetchStatus } = trpc.viewer.public.slots.getSchedule.useQuery( - { - eventTypeId, - eventTypeSlug, - usernameList, - startTime: startTime?.toISOString() || "", - endTime: endTime?.toISOString() || "", - timeZone, - duration, - }, - { - enabled: !!startTime && !!endTime && enabled, - refetchInterval, - trpc: { context: { skipBatch: true } }, - } - ); - useEffect(() => { - if (!!data && fetchStatus === "idle") { - setRefetchCount(refetchCount + 1); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [fetchStatus, data]); - - // The very first time isPaused is set if auto-fetch is disabled, so isPaused should also be considered a loading state. - return { slots: data?.slots || {}, isLoading: isLoading || isPaused }; -}; - -const SlotPicker = ({ - eventType, - timeFormat, - onTimeFormatChange, - timeZone, - recurringEventCount, - users, - seatsPerTimeSlot, - weekStart = 0, - ethSignature, -}: { - eventType: Pick< - EventType & { metadata: z.infer }, - "id" | "schedulingType" | "slug" | "length" | "metadata" - >; - timeFormat: TimeFormat; - onTimeFormatChange: (is24Hour: boolean) => void; - timeZone?: string; - seatsPerTimeSlot?: number; - recurringEventCount?: number; - users: string[]; - weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6; - ethSignature?: string; -}) => { - const [selectedDate, setSelectedDate] = useState(); - const [browsingDate, setBrowsingDate] = useState(); - let { duration = eventType.length.toString() } = useRouterQuery("duration"); - const { date, setQuery: setDate } = useRouterQuery("date"); - const { month, setQuery: setMonth } = useRouterQuery("month"); - const router = useRouter(); - - if (!eventType.metadata?.multipleDuration) { - duration = eventType.length.toString(); - } - - const [slotPickerRef] = useAutoAnimate(); - - useEffect(() => { - if (!router.isReady) return; - - // Etc/GMT is not actually a timeZone, so handle this select option explicitly to prevent a hard crash. - if (timeZone === "Etc/GMT") { - setBrowsingDate(dayjs.utc(month).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0)); - if (date) { - setSelectedDate(dayjs.utc(date)); - } - } else { - // Set the start of the month without shifting time like startOf() may do. - setBrowsingDate( - dayjs.tz(month, timeZone).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0) - ); - if (date) { - // It's important to set the date immediately to the timeZone, dayjs(date) will convert to browsertime. - setSelectedDate(dayjs.tz(date, timeZone)); - } - } - }, [router.isReady, month, date, duration, timeZone]); - - const { i18n, isLocaleReady } = useLocale(); - const { slots: monthSlots, isLoading } = useSlots({ - eventTypeId: eventType.id, - eventTypeSlug: eventType.slug, - usernameList: users, - startTime: - browsingDate === undefined || browsingDate.get("month") === dayjs.tz(undefined, timeZone).get("month") - ? dayjs.tz(undefined, timeZone).subtract(2, "days").startOf("day") - : browsingDate?.startOf("month"), - endTime: browsingDate?.endOf("month"), - timeZone, - duration, - }); - const { slots: selectedDateSlots, isLoading: _isLoadingSelectedDateSlots } = useSlots({ - eventTypeId: eventType.id, - eventTypeSlug: eventType.slug, - usernameList: users, - startTime: selectedDate?.startOf("day"), - endTime: selectedDate?.endOf("day"), - timeZone, - duration, - /** Prevent refetching is we already have this data from month slots */ - enabled: !!selectedDate, - }); - - /** Hide skeleton if we have the slot loaded in the month query */ - const isLoadingSelectedDateSlots = (() => { - if (!selectedDate) return _isLoadingSelectedDateSlots; - if (!!selectedDateSlots[selectedDate.format("YYYY-MM-DD")]) return false; - if (!!monthSlots[selectedDate.format("YYYY-MM-DD")]) return false; - return false; - })(); - - return ( - <> - monthSlots[k].length > 0)} - locale={isLocaleReady ? i18n.language : "en"} - selected={selectedDate} - onChange={(newDate) => { - setDate(newDate.format("YYYY-MM-DD")); - }} - onMonthChange={(newMonth) => { - setMonth(newMonth.format("YYYY-MM")); - }} - browsingDate={browsingDate} - weekStart={weekStart} - /> - -
- {selectedDate ? ( - - ) : null} -
- - ); -}; - -function TimezoneDropdown({ - onChangeTimeZone, -}: { - onChangeTimeZone: (newTimeZone: string) => void; - timeZone?: string; -}) { - const handleSelectTimeZone = (newTimeZone: string) => { - onChangeTimeZone(newTimeZone); - localStorageTimeZone(newTimeZone); - }; - - return ( - <> -
- - -
- - ); -} +const Toaster = dynamic(() => import("react-hot-toast").then((mod) => mod.Toaster), { ssr: false }); +/*const SlotPicker = dynamic(() => import("../SlotPicker").then((mod) => mod.SlotPicker), { + ssr: false, + loading: () =>
, +});*/ +const TimezoneDropdown = dynamic(() => import("../TimezoneDropdown").then((mod) => mod.TimezoneDropdown), { + ssr: false, +}); const dateQuerySchema = z.object({ rescheduleUid: z.string().optional().default(""), diff --git a/apps/web/package.json b/apps/web/package.json index 859d873e50..ee9298b832 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -44,7 +44,7 @@ "@hookform/error-message": "^2.0.0", "@hookform/resolvers": "^2.9.7", "@next-auth/prisma-adapter": "^1.0.4", - "@next/bundle-analyzer": "^12.2.5", + "@next/bundle-analyzer": "^13.1.6", "@next/font": "^13.1.1", "@radix-ui/react-avatar": "^1.0.0", "@radix-ui/react-collapsible": "^1.0.0", @@ -81,6 +81,7 @@ "lodash": "^4.17.21", "lottie-react": "^2.3.1", "markdown-it": "^13.0.1", + "md5": "^2.3.0", "memory-cache": "^0.2.0", "micro": "^10.0.1", "mime-types": "^2.1.35", @@ -140,6 +141,7 @@ "@types/glidejs__glide": "^3.4.2", "@types/lodash": "^4.14.182", "@types/markdown-it": "^12.2.3", + "@types/md5": "^2.3.2", "@types/memory-cache": "^0.2.2", "@types/micro": "7.3.7", "@types/mime-types": "^2.1.1", diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index 11c4da78a4..42bd29f017 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -1,21 +1,10 @@ -import MarkdownIt from "markdown-it"; import type { GetStaticPaths, GetStaticPropsContext } from "next"; import { z } from "zod"; import type { LocationObject } from "@calcom/app-store/locations"; -import { privacyFilteredLocations } from "@calcom/app-store/locations"; -import { getAppFromSlug } from "@calcom/app-store/utils"; import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants"; -import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent"; -import prisma from "@calcom/prisma"; import type { User } from "@calcom/prisma/client"; -import { - EventTypeMetaDataSchema, - teamMetadataSchema, - userMetadata as userMetadataSchema, -} from "@calcom/prisma/zod-utils"; import { isBrandingHidden } from "@lib/isBrandingHidden"; import type { inferSSRProps } from "@lib/types/inferSSRProps"; @@ -65,10 +54,19 @@ export default function Type(props: AvailabilityPageProps) { Type.isThemeSupported = true; +const paramsSchema = z.object({ type: z.string(), user: z.string() }); async function getUserPageProps(context: GetStaticPropsContext) { - const { type: slug, user: username } = paramsSchema.parse(context.params); + // load server side dependencies + const MarkdownIt = await import("markdown-it").then((mod) => mod.default); + const prisma = await import("@calcom/prisma").then((mod) => mod.default); + const { privacyFilteredLocations } = await import("@calcom/app-store/locations"); + const { parseRecurringEvent } = await import("@calcom/lib/isRecurringEvent"); + const { EventTypeMetaDataSchema, teamMetadataSchema } = await import("@calcom/prisma/zod-utils"); const { ssgInit } = await import("@server/lib/ssg"); + + const { type: slug, user: username } = paramsSchema.parse(context.params); const ssg = await ssgInit(context); + const user = await prisma.user.findUnique({ where: { username, @@ -191,7 +189,17 @@ async function getUserPageProps(context: GetStaticPropsContext) { } async function getDynamicGroupPageProps(context: GetStaticPropsContext) { + // load server side dependencies + const { getDefaultEvent, getGroupName, getUsernameList } = await import("@calcom/lib/defaultEvents"); + const { privacyFilteredLocations } = await import("@calcom/app-store/locations"); + const { parseRecurringEvent } = await import("@calcom/lib/isRecurringEvent"); + const prisma = await import("@calcom/prisma").then((mod) => mod.default); + const { EventTypeMetaDataSchema, userMetadata: userMetadataSchema } = await import( + "@calcom/prisma/zod-utils" + ); const { ssgInit } = await import("@server/lib/ssg"); + const { getAppFromSlug } = await import("@calcom/app-store/utils"); + const ssg = await ssgInit(context); const { type: typeParam, user: userParam } = paramsSchema.parse(context.params); const usernameList = getUsernameList(userParam); @@ -310,8 +318,6 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) { }; } -const paramsSchema = z.object({ type: z.string(), user: z.string() }); - export const getStaticProps = async (context: GetStaticPropsContext) => { const { user: userParam } = paramsSchema.parse(context.params); // dynamic groups are not generated at build time, but otherwise are probably cached until infinity. diff --git a/packages/app-store/BookingPageTagManager.tsx b/packages/app-store/BookingPageTagManager.tsx index 72abf76c59..8d7b744b38 100644 --- a/packages/app-store/BookingPageTagManager.tsx +++ b/packages/app-store/BookingPageTagManager.tsx @@ -1,7 +1,7 @@ import Script from "next/script"; +import { getEventTypeAppData } from "@calcom/app-store/_utils/getEventTypeAppData"; import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData"; -import { getEventTypeAppData } from "@calcom/app-store/utils"; import type { appDataSchemas } from "./apps.schemas.generated"; diff --git a/packages/app-store/_utils/getEventTypeAppData.ts b/packages/app-store/_utils/getEventTypeAppData.ts new file mode 100644 index 0000000000..0b1d757b9a --- /dev/null +++ b/packages/app-store/_utils/getEventTypeAppData.ts @@ -0,0 +1,47 @@ +import type { z } from "zod"; + +import type { EventTypeModel } from "@calcom/prisma/zod"; +import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; + +export type EventTypeApps = NonNullable>["apps"]>; +export type EventTypeAppsList = keyof EventTypeApps; + +export const getEventTypeAppData = ( + eventType: Pick, "price" | "currency" | "metadata">, + appId: T, + forcedGet?: boolean +): EventTypeApps[T] => { + const metadata = eventType.metadata; + const appMetadata = metadata?.apps && metadata.apps[appId]; + if (appMetadata) { + const allowDataGet = forcedGet ? true : appMetadata.enabled; + return allowDataGet ? appMetadata : null; + } + + // Backward compatibility for existing event types. + // TODO: After the new AppStore EventType App flow is stable, write a migration to migrate metadata to new format which will let us remove this compatibility code + // Migration isn't being done right now, to allow a revert if needed + const legacyAppsData = { + stripe: { + enabled: eventType.price > 0, + // Price default is 0 in DB. So, it would always be non nullish. + price: eventType.price, + // Currency default is "usd" in DB.So, it would also be available always + currency: eventType.currency, + }, + rainbow: { + enabled: !!(eventType.metadata?.smartContractAddress && eventType.metadata?.blockchainId), + smartContractAddress: eventType.metadata?.smartContractAddress || "", + blockchainId: eventType.metadata?.blockchainId || 0, + }, + giphy: { + enabled: !!eventType.metadata?.giphyThankYouPage, + thankYouPage: eventType.metadata?.giphyThankYouPage || "", + }, + } as const; + + // TODO: This assertion helps typescript hint that only one of the app's data can be returned + const legacyAppData = legacyAppsData[appId as Extract]; + const allowDataGet = forcedGet ? true : legacyAppData?.enabled; + return allowDataGet ? legacyAppData : null; +}; diff --git a/packages/app-store/utils.ts b/packages/app-store/utils.ts index 4ef88f073e..e0d113eba3 100644 --- a/packages/app-store/utils.ts +++ b/packages/app-store/utils.ts @@ -1,16 +1,15 @@ import { Prisma } from "@prisma/client"; import type { TFunction } from "next-i18next"; -import type { z } from "zod"; // If you import this file on any app it should produce circular dependency // import appStore from "./index"; import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData"; import type { EventLocationType } from "@calcom/app-store/locations"; import { defaultLocations } from "@calcom/app-store/locations"; -import type { EventTypeModel } from "@calcom/prisma/zod"; -import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import type { App, AppMeta } from "@calcom/types/App"; +export * from "./_utils/getEventTypeAppData"; + type LocationOption = { label: string; value: EventLocationType["type"]; @@ -18,9 +17,6 @@ type LocationOption = { disabled?: boolean; }; -export type EventTypeApps = NonNullable>["apps"]>; -export type EventTypeAppsList = keyof EventTypeApps; - const ALL_APPS_MAP = Object.keys(appStoreMetadata).reduce((store, key) => { const metadata = appStoreMetadata[key as keyof typeof appStoreMetadata] as AppMeta; if (metadata.logo && !metadata.logo.includes("/")) { @@ -193,44 +189,4 @@ export function getAppFromSlug(slug: string | undefined): AppMeta | undefined { return ALL_APPS.find((app) => app.slug === slug); } -export const getEventTypeAppData = ( - eventType: Pick, "price" | "currency" | "metadata">, - appId: T, - forcedGet?: boolean -): EventTypeApps[T] => { - const metadata = eventType.metadata; - const appMetadata = metadata?.apps && metadata.apps[appId]; - if (appMetadata) { - const allowDataGet = forcedGet ? true : appMetadata.enabled; - return allowDataGet ? appMetadata : null; - } - - // Backward compatibility for existing event types. - // TODO: After the new AppStore EventType App flow is stable, write a migration to migrate metadata to new format which will let us remove this compatibility code - // Migration isn't being done right now, to allow a revert if needed - const legacyAppsData = { - stripe: { - enabled: eventType.price > 0, - // Price default is 0 in DB. So, it would always be non nullish. - price: eventType.price, - // Currency default is "usd" in DB.So, it would also be available always - currency: eventType.currency, - }, - rainbow: { - enabled: !!(eventType.metadata?.smartContractAddress && eventType.metadata?.blockchainId), - smartContractAddress: eventType.metadata?.smartContractAddress || "", - blockchainId: eventType.metadata?.blockchainId || 0, - }, - giphy: { - enabled: !!eventType.metadata?.giphyThankYouPage, - thankYouPage: eventType.metadata?.giphyThankYouPage || "", - }, - } as const; - - // TODO: This assertion helps typescript hint that only one of the app's data can be returned - const legacyAppData = legacyAppsData[appId as Extract]; - const allowDataGet = forcedGet ? true : legacyAppData?.enabled; - return allowDataGet ? legacyAppData : null; -}; - export default getApps; diff --git a/packages/core/EventManager.ts b/packages/core/EventManager.ts index 51eae74e04..97f95b201b 100644 --- a/packages/core/EventManager.ts +++ b/packages/core/EventManager.ts @@ -8,14 +8,8 @@ import { getEventLocationTypeFromApp } from "@calcom/app-store/locations"; import { MeetLocationType } from "@calcom/app-store/locations"; import getApps from "@calcom/app-store/utils"; import prisma from "@calcom/prisma"; -import { Attendee } from "@calcom/prisma/client"; import { createdEventSchema } from "@calcom/prisma/zod-utils"; -import type { - AdditionalInformation, - CalendarEvent, - NewCalendarEventType, - Person, -} from "@calcom/types/Calendar"; +import type { AdditionalInformation, CalendarEvent, NewCalendarEventType } from "@calcom/types/Calendar"; import { CredentialPayload, CredentialWithAppName } from "@calcom/types/Credential"; import type { Event } from "@calcom/types/Event"; import type { diff --git a/packages/core/getBusyTimes.ts b/packages/core/getBusyTimes.ts index cd176aa6a3..fe609bf4c4 100644 --- a/packages/core/getBusyTimes.ts +++ b/packages/core/getBusyTimes.ts @@ -1,4 +1,4 @@ -import { BookingStatus, Credential, SelectedCalendar } from "@prisma/client"; +import { BookingStatus, Credential } from "@prisma/client"; import { getBusyCalendarTimes } from "@calcom/core/CalendarManager"; import dayjs from "@calcom/dayjs"; diff --git a/packages/emails/src/templates/TeamInviteEmail.tsx b/packages/emails/src/templates/TeamInviteEmail.tsx index 14a0cbc0eb..f6e0a5c92e 100644 --- a/packages/emails/src/templates/TeamInviteEmail.tsx +++ b/packages/emails/src/templates/TeamInviteEmail.tsx @@ -1,5 +1,4 @@ import type { TFunction } from "next-i18next"; -import { ReactNode } from "react"; import { APP_NAME, WEBAPP_URL, IS_PRODUCTION } from "@calcom/lib/constants"; diff --git a/packages/features/ee/teams/components/TeamList.tsx b/packages/features/ee/teams/components/TeamList.tsx index cbf78f6f43..02fab0dac2 100644 --- a/packages/features/ee/teams/components/TeamList.tsx +++ b/packages/features/ee/teams/components/TeamList.tsx @@ -3,7 +3,7 @@ import { useState } from "react"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { RouterOutputs, trpc } from "@calcom/trpc/react"; import { Card, showToast } from "@calcom/ui"; -import { FiUserPlus, FiUsers, FiUnlock, FiEdit } from "@calcom/ui/components/icon"; +import { FiUserPlus, FiUsers, FiEdit } from "@calcom/ui/components/icon"; import TeamListItem from "./TeamListItem"; diff --git a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts index b79dd391c6..fad4d945bc 100644 --- a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts @@ -10,7 +10,6 @@ import dayjs from "@calcom/dayjs"; import prisma from "@calcom/prisma"; import { Prisma } from "@calcom/prisma/client"; import { bookingMetadataSchema } from "@calcom/prisma/zod-utils"; -import { Person } from "@calcom/types/Calendar"; import { getSenderId } from "../alphanumericSenderIdSupport"; import * as twilio from "./smsProviders/twilioProvider"; diff --git a/packages/features/eventtypes/components/CreateEventTypeButton.tsx b/packages/features/eventtypes/components/CreateEventTypeButton.tsx index 01aff2ebde..331afe9e0c 100644 --- a/packages/features/eventtypes/components/CreateEventTypeButton.tsx +++ b/packages/features/eventtypes/components/CreateEventTypeButton.tsx @@ -32,7 +32,7 @@ import { TextAreaField, TextField, } from "@calcom/ui"; -import { FiPlus, FiChevronDown } from "@calcom/ui/components/icon"; +import { FiPlus } from "@calcom/ui/components/icon"; import { DuplicateDialog } from "./DuplicateDialog"; diff --git a/packages/features/settings/layouts/SettingsLayout.tsx b/packages/features/settings/layouts/SettingsLayout.tsx index 7655b66d90..2cb65d8892 100644 --- a/packages/features/settings/layouts/SettingsLayout.tsx +++ b/packages/features/settings/layouts/SettingsLayout.tsx @@ -32,7 +32,6 @@ import { FiChevronRight, FiPlus, FiMenu, - FiExternalLink, } from "@calcom/ui/components/icon"; const tabs: VerticalTabItemProps[] = [ diff --git a/packages/lib/defaultAvatarImage.ts b/packages/lib/defaultAvatarImage.ts index 316c2d5b1e..6f9076ac22 100644 --- a/packages/lib/defaultAvatarImage.ts +++ b/packages/lib/defaultAvatarImage.ts @@ -1,10 +1,10 @@ -import crypto from "crypto"; +import md5Parser from "md5"; export const defaultAvatarSrc = function ({ email, md5 }: { md5?: string; email?: string }) { if (!email && !md5) return ""; if (email && !md5) { - md5 = crypto.createHash("md5").update(email).digest("hex"); + md5 = md5Parser(email); } return `https://www.gravatar.com/avatar/${md5}?s=160&d=mp&r=PG`; diff --git a/packages/lib/getPaymentAppData.ts b/packages/lib/getPaymentAppData.ts index fa9ccc4d6a..4653a346ed 100644 --- a/packages/lib/getPaymentAppData.ts +++ b/packages/lib/getPaymentAppData.ts @@ -1,9 +1,9 @@ import type { z } from "zod"; +import { getEventTypeAppData } from "@calcom/app-store/_utils/getEventTypeAppData"; import type { appDataSchemas } from "@calcom/app-store/apps.schemas.generated"; import type { appDataSchema } from "@calcom/app-store/stripepayment/zod"; import type { EventTypeAppsList } from "@calcom/app-store/utils"; -import { getEventTypeAppData } from "@calcom/app-store/utils"; export default function getPaymentAppData( eventType: Parameters[0], diff --git a/packages/trpc/server/routers/viewer/workflows.tsx b/packages/trpc/server/routers/viewer/workflows.tsx index 5d279763e5..f9f2d9ff0f 100644 --- a/packages/trpc/server/routers/viewer/workflows.tsx +++ b/packages/trpc/server/routers/viewer/workflows.tsx @@ -40,7 +40,7 @@ import { WorkflowStep } from "@calcom/prisma/client"; import { TRPCError } from "@trpc/server"; -import { router, authedProcedure, authedRateLimitedProcedure } from "../../trpc"; +import { router, authedProcedure } from "../../trpc"; import { viewerTeamsRouter } from "./teams"; function getSender( diff --git a/yarn.lock b/yarn.lock index e27ebdb51f..2e27c6e660 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3802,10 +3802,10 @@ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-0.7.3.tgz#d274116678ffae87f6b60e90f88cc4083eefab86" integrity sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg== -"@floating-ui/core@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.0.tgz#ae7ae7923d41f3d84cb2fd88740a89436610bbec" - integrity sha512-GHUXPEhMEmTpnpIfesFA2KAoMJPb1SPQw964tToQwt+BbGXdhqTCWT1rOb0VURGylsxsYxiGMnseJ3IlclVpVA== +"@floating-ui/core@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.1.tgz#074182a1d277f94569c50a6b456e62585d463c8e" + integrity sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg== "@floating-ui/dom@^0.5.3": version "0.5.4" @@ -3814,12 +3814,12 @@ dependencies: "@floating-ui/core" "^0.7.3" -"@floating-ui/dom@^1.1.1": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.0.tgz#a60212069cc58961c478037c30eba4b191c75316" - integrity sha512-QXzg57o1cjLz3cGETzKXjI3kx1xyS49DW9l7kV2jw2c8Yftd434t2hllX0sVGn2Q8MtcW/4pNm8bfE1/4n6mng== +"@floating-ui/dom@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.1.tgz#8f93906e1a3b9f606ce78afb058e874344dcbe07" + integrity sha512-Rt45SmRiV8eU+xXSB9t0uMYiQ/ZWGE/jumse2o3i5RGlyvcbqOF4q+1qBnzLE2kZ5JGhq0iMkcGXUKbFe7MpTA== dependencies: - "@floating-ui/core" "^1.2.0" + "@floating-ui/core" "^1.2.1" "@floating-ui/react-dom@0.7.2": version "0.7.2" @@ -3830,11 +3830,11 @@ use-isomorphic-layout-effect "^1.1.1" "@floating-ui/react-dom@^1.0.0": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-1.2.2.tgz#ed256992fd44fcfcddc96da68b4b92f123d61871" - integrity sha512-DbmFBLwFrZhtXgCI2ra7wXYT8L2BN4/4AMQKyu05qzsVji51tXOfF36VE2gpMB6nhJGHa85PdEg75FB4+vnLFQ== + version "1.3.0" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-1.3.0.tgz#4d35d416eb19811c2b0e9271100a6aa18c1579b3" + integrity sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g== dependencies: - "@floating-ui/dom" "^1.1.1" + "@floating-ui/dom" "^1.2.1" "@formatjs/ecma402-abstract@1.11.4": version "1.11.4" @@ -3934,9 +3934,9 @@ "@hapi/hoek" "^9.0.0" "@headlessui/react@^1.5.0": - version "1.7.10" - resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.10.tgz#0971a33843a76f2bf4b801a43f76e3730fe15884" - integrity sha512-1m66h/5eayTEZVT2PI13/2PG3EVC7a9XalmUtVSC8X76pcyKYMuyX1XAL2RUtCr8WhoMa/KrDEyoeU5v+kSQOw== + version "1.7.11" + resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.11.tgz#1cc5750226abe5af2c94f72e975c0c8d2f5cc5a6" + integrity sha512-EaDbVgcyiylhtskZZf4Qb/JiiByY7cYbd0qgZ9xm2pm2X7hKojG0P4TaQYKgPOV3vojPhd/pZyQh3nmRkkcSyw== dependencies: client-only "^0.0.1" @@ -4944,12 +4944,19 @@ integrity sha512-jIOM6CzCbl2/Mzbx9kb2IjtHoJOeRN9wtQgLk4EUm5bhneSVGv1rtz5TDskvp2UfCa+EK9nDmug+lje41z80Gg== "@next/bundle-analyzer@^12.2.5": - version "12.3.1" - resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-12.3.1.tgz#df168652c43ed0dc3e91a5108e34cdcc8fc7f05a" - integrity sha512-2f/eei0YqZZBMTs4g1+HbgHyAFH5MbI/w9wLXmE8ly9SFze2D40sRH46JcC//EFVM/TIynVBh5sxn9CVO/vtxg== + version "12.3.4" + resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-12.3.4.tgz#37c587525288a3dea64213c991590532246e8bb8" + integrity sha512-eKjgRICzbLTmod0UnJcArFVs5uEAiuZwB6NCf84m+btW7jdylUVoOYf1wi5tA14xk5L9Lho7Prm6/XJ8gxYzfQ== dependencies: webpack-bundle-analyzer "4.3.0" +"@next/bundle-analyzer@^13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-13.1.6.tgz#36ff2f9c4b19aa75cd589ee080b1e2edb24f1192" + integrity sha512-rJS9CtLoGT58mL+v2ISKANosFFWP/0YKYByHQ3vTaZrbQP8b1rYRxd2QVMJmnSXaFkiP9URt1XJ6OdGyVq5b6g== + dependencies: + webpack-bundle-analyzer "4.7.0" + "@next/env@13.1.1": version "13.1.1" resolved "https://registry.yarnpkg.com/@next/env/-/env-13.1.1.tgz#6ff26488dc7674ef2bfdd1ca28fe43eed1113bea" @@ -4962,6 +4969,11 @@ dependencies: glob "7.1.7" +"@next/font@^13.1.1": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/font/-/font-13.1.6.tgz#2bf99e3321ec9b4d65781c0d0ebff072e8752e1a" + integrity sha512-AITjmeb1RgX1HKMCiA39ztx2mxeAyxl4ljv2UoSBUGAbFFMg8MO7YAvjHCgFhD39hL7YTbFjol04e/BPBH5RzQ== + "@next/swc-android-arm-eabi@13.1.1": version "13.1.1" resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.1.tgz#b5c3cd1f79d5c7e6a3b3562785d4e5ac3555b9e1" @@ -5183,10 +5195,10 @@ ms "2.1.3" strip-ansi "6.0.1" -"@prisma/debug@4.10.0": - version "4.10.0" - resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-4.10.0.tgz#5b1ab6b6d4812765a60ad8cccb7b5d38afb3b802" - integrity sha512-rxVOZKsEyjlQCwN/pkkJO7wEdARt1yRyukSjLa+BF2QTvy2+VgtBmrfys4WDQSnj3jVWeHMpi5GeAoJjKkSKyA== +"@prisma/debug@4.10.1": + version "4.10.1" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-4.10.1.tgz#02d0f98aec5b0bb8b5358e1fcf6cb9462fbb6de2" + integrity sha512-NCWX+uJiEItzQsOS/4kiAKsT1hgSbN7n+1cNCmzoA6TsEn+WKzN0ZjBKyKuR937z57n2WVGXo5DfnCiW9ClqUg== dependencies: "@types/debug" "4.1.7" debug "4.3.4" @@ -5203,11 +5215,11 @@ integrity sha512-93tctjNXcIS+i/e552IO6tqw17sX8liivv8WX9lDMCpEEe3ci+nT9F+1oHtAafqruXLepKF80i/D20Mm+ESlOw== "@prisma/generator-helper@^4.0.0": - version "4.10.0" - resolved "https://registry.yarnpkg.com/@prisma/generator-helper/-/generator-helper-4.10.0.tgz#dc47265cbd55dc14ce08822f99c14d08f7e2cdb7" - integrity sha512-NkQOfZpHUjVjqJ7NN2FymHSLkGd/E0fz5c3RkyESKvQqBy2sFBxt+aFxGsUbUy3FfwvkckC04HdQOXpisAko0A== + version "4.10.1" + resolved "https://registry.yarnpkg.com/@prisma/generator-helper/-/generator-helper-4.10.1.tgz#6a71043855ec670d9b8a497e4a77f181cedfbc20" + integrity sha512-OS2hAnRAiWpy+icccG6wp/yx/2jaeLdfYG6ZwD8RR0vZC2PC5VsrJz8tv6kYmuOs3qQwzVcPt9YSj3frWfD5WA== dependencies: - "@prisma/debug" "4.10.0" + "@prisma/debug" "4.10.1" "@types/cross-spawn" "6.0.2" chalk "4.1.2" cross-spawn "7.0.3" @@ -6311,7 +6323,7 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@sideway/formula@^3.0.0": +"@sideway/formula@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== @@ -8248,6 +8260,11 @@ "@types/linkify-it" "*" "@types/mdurl" "*" +"@types/md5@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.3.2.tgz#529bb3f8a7e9e9f621094eb76a443f585d882528" + integrity sha512-v+JFDu96+UYJ3/UWzB0mEglIS//MZXgRaJ4ubUPwOM0gvLc/kcQ3TWNYwENEK7/EcXGQVrW8h/XqednSjBd/Og== + "@types/mdast@^3.0.0": version "3.0.10" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" @@ -8812,9 +8829,9 @@ integrity sha512-aW6CfMMToX4a+baLuVxwcT0FSACjX3xrNt8wdi/3LLRlLAfhyue8OK7kJxhcYNZfydBeWTP59aRy8p5FUTIeew== "@vercel/analytics@^0.1.6": - version "0.1.8" - resolved "https://registry.yarnpkg.com/@vercel/analytics/-/analytics-0.1.8.tgz#71f1f8c7bb98ac0c5c47eb3fb8ccbe8141b9fe47" - integrity sha512-PQrOI8BJ9qUiVJuQfnKiJd15eDjDJH9TBKsNeMrtelT4NAk7d9mBVz1CoZkvoFnHQ0OW7Xnqmr1F2nScfAnznQ== + version "0.1.9" + resolved "https://registry.yarnpkg.com/@vercel/analytics/-/analytics-0.1.9.tgz#434f14a0dbe635a6d92c693a3607dbac0468dd47" + integrity sha512-Lfus/6HAZXQBr4uv5eCtNKrEvthKWQFFcUNh5wC3fWn+4PbHxT8bqcm1TMQfYvGJ8O4TWzn0IfrQFNWh1DHDbQ== "@vercel/edge-config@^0.1.1": version "0.1.1" @@ -11775,6 +11792,11 @@ commander@^6.2.0, commander@^6.2.1: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + commander@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -15443,9 +15465,9 @@ gifwrap@^0.9.2: omggif "^1.0.10" github-buttons@^2.22.0: - version "2.22.3" - resolved "https://registry.yarnpkg.com/github-buttons/-/github-buttons-2.22.3.tgz#ad0766618dadfbb46c12c5dca1f8c14c1e7d90f3" - integrity sha512-09/pJ7X6D1whEeFz+fKSjOBEEDnY/nxbPN7zduxAU5zGwo4RkXjL86AiOVHgIYDothbHAS43eaVk76a8Lp4YYw== + version "2.23.0" + resolved "https://registry.yarnpkg.com/github-buttons/-/github-buttons-2.23.0.tgz#b113ec9ed733f55bf5ab07652015bd0e5128dd1d" + integrity sha512-2REUOV3ue6NmT0QThhfzfYmeSoYpCG73+tL7Ir2C7P+gshRerI05WuIQuhDkE2Zlg5Wc39hc2DHj+pE23mGJvw== github-slugger@^1.0.0, github-slugger@^1.3.0: version "1.4.0" @@ -15999,9 +16021,9 @@ hast-util-from-parse5@^6.0.0: web-namespaces "^1.0.0" hast-util-from-parse5@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-7.1.1.tgz#1887b4dd4e19f29d9c48c2e1c8bfeaac13a1f3a0" - integrity sha512-R6PoNcUs89ZxLJmMWsVbwSWuz95/9OriyQZ3e2ybwqGsRXzhA6gv49rgGmQvLbZuSNDv9fCg7vV7gXUsvtUFaA== + version "7.1.2" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz#aecfef73e3ceafdfa4550716443e4eb7b02e22b0" + integrity sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw== dependencies: "@types/hast" "^2.0.0" "@types/unist" "^2.0.0" @@ -17951,14 +17973,14 @@ jimp@^0.16.1: regenerator-runtime "^0.13.3" joi@^17.7.0: - version "17.7.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3" - integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg== + version "17.7.1" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.1.tgz#854fc85c7fa3cfc47c91124d30bffdbb58e06cec" + integrity sha512-teoLhIvWE298R6AeJywcjR4sX2hHjB3/xJX4qPjg+gTg+c0mzUDsziYlqPmLomq9gVsfaMcgPaGc7VxtD/9StA== dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" "@sideway/address" "^4.1.3" - "@sideway/formula" "^3.0.0" + "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" jose@4.11.1, jose@^4.9.3: @@ -19939,11 +19961,16 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.7: +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minimist@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" @@ -22121,9 +22148,9 @@ prism-react-renderer@^1.3.5: integrity sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg== prisma-field-encryption@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/prisma-field-encryption/-/prisma-field-encryption-1.4.0.tgz#fb644cf98b782c81e443f022e27b0108fe31c7ed" - integrity sha512-ZJLIZL4IzdkZSmeVnpvW7E4/P74txf2OLZWzzfHQlkLcDJOddfIcXPytCxwHiPZX12aXfsVt0bA2T11mdA/lMw== + version "1.4.1" + resolved "https://registry.yarnpkg.com/prisma-field-encryption/-/prisma-field-encryption-1.4.1.tgz#caec1712d0d1ac1525f64eaae3d76501f0e33a7f" + integrity sha512-J9uOdp/biLw4Gb3Z2dmXpDYXWG6jmqn0Wnp9/ga0Adg53jP0jVeIfO6+iIIY+H07xDUZa635seYs0pTMqYzyhA== dependencies: "@47ng/cloak" "^1.1.0" "@prisma/generator-helper" "^4.0.0" @@ -22613,11 +22640,11 @@ react-debounce-input@=3.2.4: prop-types "^15.7.2" react-device-detect@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-2.2.2.tgz#dbabbce798ec359c83f574c3edb24cf1cca641a5" - integrity sha512-zSN1gIAztUekp5qUT/ybHwQ9fmOqVT1psxpSlTn1pe0CO+fnJHKRLOWWac5nKxOxvOpD/w84hk1I+EydrJp7SA== + version "2.2.3" + resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-2.2.3.tgz#97a7ae767cdd004e7c3578260f48cf70c036e7ca" + integrity sha512-buYY3qrCnQVlIFHrC5UcUoAj7iANs/+srdkwsnNjI7anr3Tt7UY6MqNxtMLlr0tMBied0O49UZVK8XKs3ZIiPw== dependencies: - ua-parser-js "^1.0.2" + ua-parser-js "^1.0.33" react-devtools-core@^4.19.1: version "4.24.6" @@ -26318,7 +26345,7 @@ tzdata@^1.0.30: resolved "https://registry.yarnpkg.com/tzdata/-/tzdata-1.0.36.tgz#e5f3998d5e95e0e65dee417ae917a16314df69e9" integrity sha512-QxRODDsXS8UVxlPazCZXplqVuD6mCe7tSyDE5SCSHXv1nmrfflXxNH4pD+fjU8KJwePhWqhp6n+FpygQeGLv7w== -ua-parser-js@^1.0.2: +ua-parser-js@^1.0.33: version "1.0.33" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.33.tgz#f21f01233e90e7ed0f059ceab46eb190ff17f8f4" integrity sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ== @@ -27389,6 +27416,21 @@ webpack-bundle-analyzer@4.3.0: sirv "^1.0.7" ws "^7.3.1" +webpack-bundle-analyzer@4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz#33c1c485a7fcae8627c547b5c3328b46de733c66" + integrity sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg== + dependencies: + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^7.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" + webpack-dev-middleware@^3.7.3: version "3.7.3" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" @@ -28118,7 +28160,12 @@ zod-prisma@^0.5.4: parenthesis "^3.1.8" ts-morph "^13.0.2" -zod@^3.17.3, zod@^3.20.2: +zod@^3.17.3: + version "3.20.6" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.20.6.tgz#2f2f08ff81291d47d99e86140fedb4e0db08361a" + integrity sha512-oyu0m54SGCtzh6EClBVqDDlAYRz4jrVtKwQ7ZnsEmMI9HnzuZFj8QFwAY1M5uniIYACdGvv0PBWPF2kO0aNofA== + +zod@^3.20.2: version "3.20.2" resolved "https://registry.yarnpkg.com/zod/-/zod-3.20.2.tgz#068606642c8f51b3333981f91c0a8ab37dfc2807" integrity sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ==