From 89e6198edddb33b86f44ede0c1a1e6ec668cb5bc Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Sat, 23 Apr 2022 18:24:06 +0200 Subject: [PATCH 01/33] Extracted UI related logic on the DatePicker, stripped out all logic --- .../booking/pages/AvailabilityPage.tsx | 23 ++- packages/lib/date-fns/index.ts | 6 + packages/ui/booker/DatePicker.tsx | 145 ++++++++++++++++++ packages/ui/package.json | 1 + 4 files changed, 163 insertions(+), 12 deletions(-) create mode 100644 packages/lib/date-fns/index.ts create mode 100644 packages/ui/booker/DatePicker.tsx diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 6adcfe08d45e25..5e068df12d6e7b 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -21,6 +21,7 @@ import { FormattedNumber, IntlProvider } from "react-intl"; import { useEmbedStyles, useIsEmbed, useIsBackgroundTransparent, sdkActionManager } from "@calcom/embed-core"; import classNames from "@calcom/lib/classNames"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import DatePicker from "@calcom/ui/booker/DatePicker"; import { asStringOrNull } from "@lib/asStringOrNull"; import { timeZone } from "@lib/clock"; @@ -34,7 +35,6 @@ import { detectBrowserTimeFormat } from "@lib/timeFormat"; import CustomBranding from "@components/CustomBranding"; import AvailableTimes from "@components/booking/AvailableTimes"; -import DatePicker from "@components/booking/DatePicker"; import TimeOptions from "@components/booking/TimeOptions"; import { HeadSeo } from "@components/seo/head-seo"; import AvatarGroup from "@components/ui/AvatarGroup"; @@ -305,17 +305,16 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage changeDate(dayjs(date))} + weekStart={profile.weekStart === "Monday" ? 1 : 0} />
diff --git a/packages/lib/date-fns/index.ts b/packages/lib/date-fns/index.ts new file mode 100644 index 00000000000000..fe763d4e1c0ffd --- /dev/null +++ b/packages/lib/date-fns/index.ts @@ -0,0 +1,6 @@ +import dayjs from "dayjs"; + +// converts a date to 2022-04-25 for example. +export const yyyymmdd = (date: Date) => dayjs(date).format("YYYY-MM-DD"); + +export const daysInMonth = (date: Date) => dayjs(date).daysInMonth(); diff --git a/packages/ui/booker/DatePicker.tsx b/packages/ui/booker/DatePicker.tsx new file mode 100644 index 00000000000000..f0414dfe4dd8c0 --- /dev/null +++ b/packages/ui/booker/DatePicker.tsx @@ -0,0 +1,145 @@ +import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid"; +import { useState } from "react"; + +import classNames from "@calcom/lib/classNames"; +import { yyyymmdd, daysInMonth } from "@calcom/lib/date-fns"; +import { weekdayNames } from "@calcom/lib/weekday"; + +export type DatePickerProps = { + // which day of the week to render the calendar. Usually Sunday (=0) or Monday (=1) - default: Sunday + weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6; + // Fires whenever a selected date is changed. + onChange: (date: Date) => void; + // which date is currently selected (not tracked from here) + selected?: Date; + // defaults to current date. + minDate?: Date; + // Furthest date selectable in the future, default = UNLIMITED + maxDate?: Date; + // locale, any IETF language tag, e.g. "hu-HU" - defaults to Browser settings + locale: string; + // Defaults to [], which dates are not bookable. Array of strings like: ["2022-04-23", "2022-04-24"] + excludedDates?: string[]; + // allows adding classes to the container + className?: string; +}; + +const Day = ({ checked, children, ...props }: JSX.IntrinsicElements["button"] & { checked: boolean }) => { + return ( + + ); +}; + +const Days = ({ + minDate = new Date(), + excludedDates = [], + browsingDate, + weekStart, + selected, + ...props +}: Omit & { + browsingDate: Date; + weekStart: number; +}) => { + // Create placeholder elements for empty days in first week + let weekdayOfFirst = new Date(new Date(browsingDate).setDate(1)).getDay(); + + const days: (Date | null)[] = Array((weekdayOfFirst - weekStart + 7) % 7).fill(null); + for (let day = 1, dayCount = daysInMonth(browsingDate); day <= dayCount; day++) { + const date = new Date(new Date(browsingDate).setDate(day)); + days.push(date); + } + + return ( + <> + {days.map((day, idx) => ( +
+ {day === null ? ( +
+ ) : ( + props.onChange(day)} + disabled={excludedDates.includes(yyyymmdd(day)) || day < minDate} + checked={selected ? yyyymmdd(selected) === yyyymmdd(day) : false}> + {day.getDate()} + + )} +
+ ))} + + ); +}; + +const DatePicker = ({ + weekStart = 0, + className, + excludedDates = [], + locale, + selected, + ...passThroughProps +}: DatePickerProps) => { + const [month, setMonth] = useState(selected ? selected.getMonth() : new Date().getMonth()); + + return ( +
+
+ + + {new Date(new Date().setMonth(month)).toLocaleString(locale, { month: "long" })} + {" "} + {new Date(new Date().setMonth(month)).getFullYear()} + + + +
+
+ {weekdayNames(locale, weekStart, "short").map((weekDay) => ( +
+ {weekDay} +
+ ))} +
+
+ +
+
+ ); +}; + +export default DatePicker; diff --git a/packages/ui/package.json b/packages/ui/package.json index e0ada51b682367..66dca6cc0292bf 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -5,6 +5,7 @@ "types": "./index.tsx", "license": "MIT", "dependencies": { + "@heroicons/react": "^1.0.6", "@radix-ui/react-dialog": "^0.1.0", "next": "^12.1.0" }, From d529cd37c7cc857b8d55f237b5809ffb589c33c2 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Thu, 9 Jun 2022 19:44:00 +0100 Subject: [PATCH 02/33] wip --- .../web/components/booking/AvailableTimes.tsx | 43 +- .../booking/pages/AvailabilityPage.tsx | 292 ++- apps/web/pages/[user]/[type].tsx | 1253 ++++++++++++- apps/web/server/routers/viewer.tsx | 4 +- apps/web/server/routers/viewer/slots.tsx | 160 ++ packages/lib/defaultEvents.ts | 34 +- packages/lib/hooks/useSlots.ts | 8 + packages/ui/booker/DatePicker.tsx | 21 +- yarn.lock | 1668 +---------------- 9 files changed, 1672 insertions(+), 1811 deletions(-) create mode 100644 apps/web/server/routers/viewer/slots.tsx create mode 100644 packages/lib/hooks/useSlots.ts diff --git a/apps/web/components/booking/AvailableTimes.tsx b/apps/web/components/booking/AvailableTimes.tsx index 855e49a038df9b..156e01dcde8e78 100644 --- a/apps/web/components/booking/AvailableTimes.tsx +++ b/apps/web/components/booking/AvailableTimes.tsx @@ -30,37 +30,22 @@ type AvailableTimesProps = { }[]; schedulingType: SchedulingType | null; seatsPerTimeSlot?: number | null; + slots?: []; }; const AvailableTimes: FC = ({ + slots = [], date, - eventLength, eventTypeId, eventTypeSlug, - slotInterval, - minimumBookingNotice, recurringCount, timeFormat, - users, schedulingType, - beforeBufferTime, - afterBufferTime, seatsPerTimeSlot, }) => { const { t, i18n } = useLocale(); const router = useRouter(); const { rescheduleUid } = router.query; - const { slots, loading, error } = useSlots({ - date, - slotInterval, - eventLength, - schedulingType, - users, - minimumBookingNotice, - beforeBufferTime, - afterBufferTime, - eventTypeId, - }); const [brand, setBrand] = useState("#292929"); @@ -80,8 +65,7 @@ const AvailableTimes: FC = ({
- {!loading && - slots?.length > 0 && + {slots?.length > 0 && slots.map((slot) => { type BookingURL = { pathname: string; @@ -133,7 +117,7 @@ const AvailableTimes: FC = ({ )} data-testid="time"> {dayjs.tz(slot.time, timeZone()).format(timeFormat)} - {seatsPerTimeSlot && ( + {!!seatsPerTimeSlot && (

= 0.8 @@ -143,7 +127,7 @@ const AvailableTimes: FC = ({ : "text-emerald-400" } text-sm`}> {slot.attendees ? seatsPerTimeSlot - slot.attendees : seatsPerTimeSlot} /{" "} - {seatsPerTimeSlot} {t("seats_available")} + {!!seatsPerTimeSlot} {t("seats_available")}

)} @@ -152,26 +136,11 @@ const AvailableTimes: FC = ({
); })} - {!loading && !error && !slots.length && ( + {!slots.length && (

{t("all_booked_today")}

)} - - {loading && } - - {error && ( -
-
-
-
-
-

{t("slots_load_fail")}

-
-
-
- )}
); diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index e2c0f75ac8efea..df8ba382f900ba 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -33,6 +33,7 @@ import { } from "@calcom/embed-core/embed-iframe"; import classNames from "@calcom/lib/classNames"; import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants"; +import { yyyymmdd } from "@calcom/lib/date-fns"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { localStorage } from "@calcom/lib/webstorage"; import DatePicker from "@calcom/ui/booker/DatePicker"; @@ -45,6 +46,7 @@ import { isBrandingHidden } from "@lib/isBrandingHidden"; import { parseDate } from "@lib/parseDate"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry"; import { detectBrowserTimeFormat } from "@lib/timeFormat"; +import { trpc } from "@lib/trpc"; import CustomBranding from "@components/CustomBranding"; import AvailableTimes from "@components/booking/AvailableTimes"; @@ -90,21 +92,133 @@ export const locationKeyToString = (location: LocationObject, t: TFunction) => { } }; -const TimezoneDropdown = () => { - const handleSelectTimeZone = (selectedTimeZone: string): void => { - timeZone(selectedTimeZone); - setIsTimeOptionsOpen(false); - }; +const GoBackToPreviousPage = ({ slug }: { slug: string }) => { + const router = useRouter(); + const [previousPage, setPreviousPage] = useState(); + useEffect(() => { + setPreviousPage(document.referrer); + }, []); - const handleToggle24hClock = (is24hClock: boolean) => { - setTimeFormat(is24hClock ? "HH:mm" : "h:mma"); - }; + return previousPage === `${WEBAPP_URL}/${slug}` ? ( +
+ router.back()} + /> +

Go Back

+
+ ) : ( + <> + ); +}; + +const useSlots = ({ + eventTypeId, + startTime, + endTime, +}: { + eventTypeId: number; + startTime: Date; + endTime: Date; +}) => { + const { data } = trpc.useQuery([ + "viewer.slots.getSchedule", + { + eventTypeId, + startTime: startTime.toISOString(), + endTime: endTime.toISOString(), + }, + ]); + + return data?.slots || {}; +}; + +const SlotPicker = ({ + eventTypeId, + timezoneDropdown, + timeFormat, + timeZone, + weekStart = 0, +}: { + eventTypeId: number; + timezoneDropdown: JSX.Element; + timeFormat: string; + timeZone?: string; + weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6; +}) => { + const { selectedDate, changeDate } = useDateSelected({ timeZone }); + const startTime = + selectedDate || + (() => { + const d = new Date(); + d.setMinutes(Math.ceil(new Date().getMinutes() / 5) * 5, 0, 0); + return d; + })(); + + const slots = useSlots({ + eventTypeId: eventTypeId, + startTime: dayjs(startTime).toDate(), + endTime: dayjs(startTime) + .endOf(selectedDate ? "day" : "month") + .toDate(), + }); + + return ( + <> + slots[k].length === 0)} + date={selectedDate?.toDate()} + onChange={(date) => changeDate(dayjs(date))} + weekStart={weekStart} + /> + +
{timezoneDropdown}
+ + {selectedDate && ( + + )} + + ); +}; + +function TimezoneDropdown({ + onChangeTimeFormat, + onChangeTimeZone, +}: { + onChangeTimeFormat: (newTimeFormat: string) => void; + onChangeTimeZone: (newTimeZone: string) => void; +}) { + const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false); useEffect(() => { handleToggle24hClock(localStorage.getItem("timeOption.is24hClock") === "true"); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false); + const handleSelectTimeZone = (newTimeZone: string) => { + timeZone(newTimeZone); + onChangeTimeZone(newTimeZone); + setIsTimeOptionsOpen(false); + }; + + const handleToggle24hClock = (is24hClock: boolean) => { + onChangeTimeFormat(is24hClock ? "HH:mm" : "h:mma"); + }; + return ( @@ -121,27 +235,10 @@ const TimezoneDropdown = () => { ); -}; +} -const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage, booking }: Props) => { +const useDateSelected = ({ timeZone }: { timeZone?: string }) => { const router = useRouter(); - const isEmbed = useIsEmbed(); - const { rescheduleUid } = router.query; - const { isReady, Theme } = useTheme(profile.theme); - const { t, i18n } = useLocale(); - const { contracts } = useContracts(); - const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker"); - const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left"; - const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed; - const isBackgroundTransparent = useIsBackgroundTransparent(); - useExposePlanGlobally(plan); - useEffect(() => { - if (eventType.metadata.smartContractAddress) { - const eventOwner = eventType.users[0]; - if (!contracts[(eventType.metadata.smartContractAddress || null) as number]) - router.replace(`/${eventOwner.username}`); - } - }, [contracts, eventType.metadata.smartContractAddress, eventType.users, router]); const selectedDate = useMemo(() => { const dateString = asStringOrNull(router.query.date); @@ -163,54 +260,85 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage return null; }, [router.query.date]); - if (selectedDate) { - // Let iframe take the width available due to increase in max-width - sdkActionManager?.fire("__refreshWidth", {}); - } - const [timeFormat, setTimeFormat] = useState(detectBrowserTimeFormat); - const [recurringEventCount, setRecurringEventCount] = useState(eventType.recurringEvent?.count); - - const telemetry = useTelemetry(); - - useEffect(() => { - handleToggle24hClock(localStorage.getItem("timeOption.is24hClock") === "true"); - - if (top !== window) { - //page_view will be collected automatically by _middleware.ts - telemetry.event( - telemetryEventTypes.embedView, - collectPageParameters("/availability", { isTeamBooking: document.URL.includes("team/") }) - ); - } - }, [telemetry]); - const changeDate = useCallback( (newDate: Dayjs) => { router.replace( { query: { ...router.query, - date: newDate.tz(timeZone(), true).format("YYYY-MM-DDZZ"), + date: newDate.tz(timeZone, true).format("YYYY-MM-DDZZ"), }, }, undefined, { shallow: true } ); }, - [router] + [router.query] ); useEffect(() => { if ( selectedDate != null && - selectedDate?.utcOffset() !== selectedDate.clone().utcOffset(0).tz(timeZone()).utcOffset() + selectedDate?.utcOffset() !== selectedDate.clone().utcOffset(0).tz(timeZone).utcOffset() ) { - changeDate(selectedDate.tz(timeZone(), true)); + changeDate(selectedDate.tz(timeZone, true)); } - }, [selectedDate, changeDate]); + }, [selectedDate, changeDate, timeZone]); + + return { + selectedDate, + changeDate, + }; +}; + +const AvailabilityPage = ({ profile, plan, eventTypeId, eventType, workingHours, booking }: Props) => { + const router = useRouter(); + const isEmbed = useIsEmbed(); + const { rescheduleUid } = router.query; + const { isReady, Theme } = useTheme(profile.theme); + const { t, i18n } = useLocale(); + const { contracts } = useContracts(); + const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker"); + const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left"; + const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed; + const isBackgroundTransparent = useIsBackgroundTransparent(); + + const [timeZone, setTimeZone] = useState(); + const [timeFormat, setTimeFormat] = useState(detectBrowserTimeFormat); + const [isAvailableTimesVisible, setIsAvailableTimesVisible] = useState(); + + useEffect(() => { + setIsAvailableTimesVisible(!!router.query.date); + }, [router.query.date]); + + useExposePlanGlobally(plan); + useEffect(() => { + if (eventType.metadata.smartContractAddress) { + const eventOwner = eventType.users[0]; + if (!contracts[(eventType.metadata.smartContractAddress || null) as number]) + router.replace(`/${eventOwner.username}`); + } + }, [contracts, eventType.metadata.smartContractAddress, eventType.users, router]); + + if (isAvailableTimesVisible) { + // Let iframe take the width available due to increase in max-width + sdkActionManager?.fire("__refreshWidth", {}); + } + const [recurringEventCount, setRecurringEventCount] = useState(eventType.recurringEvent?.count); + + const telemetry = useTelemetry(); + useEffect(() => { + if (top !== window) { + //page_view will be collected automatically by _middleware.ts + telemetry.event( + telemetryEventTypes.embedView, + collectPageParameters("/availability", { isTeamBooking: document.URL.includes("team/") }) + ); + } + }, [telemetry]); // Recurring event sidebar requires more space - const maxWidth = selectedDate + const maxWidth = isAvailableTimesVisible ? recurringEventCount ? "max-w-6xl" : "max-w-5xl" @@ -218,7 +346,9 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage ? "max-w-4xl" : "max-w-3xl"; - const timezoneDropdown = ; + const timezoneDropdown = ( + + ); return ( <> @@ -343,7 +473,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage
- {previousPage === `${WEBAPP_URL}/${profile.slug}` && ( -
- router.back()} - /> -

Go Back

-
- )} + + {booking?.startTime && rescheduleUid && (

)}

- - changeDate(dayjs(date))} - weekStart={profile.weekStart === "Monday" ? 1 : 0} + - -
{timezoneDropdown}
- - {selectedDate && ( - - )}
)} diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index 879ee702c6bdb0..4c72d92a9ecd61 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -1,27 +1,1215 @@ -import { Prisma } from "@prisma/client"; -import { UserPlan } from "@prisma/client"; -import { GetServerSidePropsContext } from "next"; -import { JSONObject } from "superjson/dist/types"; - -import { locationHiddenFilter, LocationObject } from "@calcom/app-store/locations"; -import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { RecurringEvent } from "@calcom/types/Calendar"; -import { asStringOrNull } from "@lib/asStringOrNull"; -import { getWorkingHours } from "@lib/availability"; -import getBooking, { GetBookingType } from "@lib/getBooking"; -import prisma from "@lib/prisma"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import AvailabilityPage from "@components/booking/pages/AvailabilityPage"; -import { ssrInit } from "@server/lib/ssr"; - -export type AvailabilityPageProps = inferSSRProps; +export type AvailabilityPageProps = inferSSRProps; export default function Type(props: AvailabilityPageProps) { + console.log("Rerender"); + + const data = { + ...props, + isDynamicGroup: false, + date: null, + eventType: { + id: 3, + title: "30min", + availability: [], + description: "", + length: 30, + price: 0, + currency: "usd", + periodType: "UNLIMITED", + periodStartDate: "Thu May 26 2022 15:23:21 GMT+0100 (British Summer Time)", + periodEndDate: "Thu May 26 2022 15:23:21 GMT+0100 (British Summer Time)", + periodDays: 30, + periodCountCalendarDays: false, + locations: [], + schedulingType: null, + recurringEvent: {}, + schedule: null, + hidden: false, + slug: "30min", + minimumBookingNotice: 120, + beforeEventBuffer: 0, + afterEventBuffer: 0, + timeZone: null, + metadata: {}, + slotInterval: null, + seatsPerTimeSlot: null, + users: [{ ...props.profile }], + }, + workingHours: [ + { + days: [1, 2, 3, 4, 5], + startTime: 480, + endTime: 960, + }, + ], + trpcState: { + json: { + mutations: [], + queries: [ + { + state: { + data: { + i18n: { + _nextI18Next: { + initialI18nStore: { + en: { + common: { + trial_days_left: 'You have $t(day, {"count": {{days}} }) left on your PRO trial', + day: "{{count}} day", + day_plural: "{{count}} days", + second: "{{count}} second", + second_plural: "{{count}} seconds", + upgrade_now: "Upgrade now", + accept_invitation: "Accept Invitation", + calcom_explained: + "Cal.com is the open source Calendly alternative putting you in control of your own data, workflow and appearance.", + have_any_questions: "Have questions? We're here to help.", + reset_password_subject: "Cal.com: Reset password instructions", + event_declined_subject: "Declined: {{eventType}} with {{name}} at {{date}}", + event_cancelled_subject: "Cancelled: {{eventType}} with {{name}} at {{date}}", + event_request_declined: "Your event request has been declined", + event_request_declined_recurring: "Your recurring event request has been declined", + event_request_cancelled: "Your scheduled event was cancelled", + organizer: "Organizer", + need_to_reschedule_or_cancel: "Need to reschedule or cancel?", + cancellation_reason: "Reason for cancellation", + cancellation_reason_placeholder: "Why are you cancelling? (optional)", + rejection_reason: "Reason for rejecting", + rejection_reason_title: "Reject the booking request?", + rejection_reason_description: + "Are you sure you want to reject the booking? We'll let the person who tried to book know. You can provide a reason below.", + rejection_confirmation: "Reject the booking", + manage_this_event: "Manage this event", + your_event_has_been_scheduled: "Your event has been scheduled", + your_event_has_been_scheduled_recurring: "Your recurring event has been scheduled", + accept_our_license: + "Accept our license by changing the .env variable <1>NEXT_PUBLIC_LICENSE_CONSENT to '{{agree}}'.", + remove_banner_instructions: + "To remove this banner, please open your .env file and change the <1>NEXT_PUBLIC_LICENSE_CONSENT variable to '{{agree}}'.", + error_message: "The error message was: '{{errorMessage}}'", + refund_failed_subject: "Refund failed: {{name}} - {{date}} - {{eventType}}", + refund_failed: + "The refund for the event {{eventType}} with {{userName}} on {{date}} failed.", + check_with_provider_and_user: + "Please check with your payment provider and {{user}} how to handle this.", + a_refund_failed: "A refund failed", + awaiting_payment_subject: + "Awaiting Payment: {{eventType}} with {{name}} on {{date}}", + meeting_awaiting_payment: "Your meeting is awaiting payment", + help: "Help", + price: "Price", + paid: "Paid", + refunded: "Refunded", + pay_later_instructions: + "You have also received an email with this link, if you want to pay later.", + payment: "Payment", + missing_card_fields: "Missing card fields", + pay_now: "Pay now", + codebase_has_to_stay_opensource: + "The codebase has to stay open source, whether it was modified or not", + cannot_repackage_codebase: "You can not repackage or sell the codebase", + acquire_license: "Acquire a commercial license to remove these terms by emailing", + terms_summary: "Summary of terms", + open_env: "Open .env and agree to our License", + env_changed: "I've changed my .env", + accept_license: "Accept License", + still_waiting_for_approval: "An event is still waiting for approval", + event_is_still_waiting: + "Event request is still waiting: {{attendeeName}} - {{date}} - {{eventType}}", + no_more_results: "No more results", + load_more_results: "Load more results", + integration_meeting_id: "{{integrationName}} meeting ID: {{meetingId}}", + confirmed_event_type_subject: "Confirmed: {{eventType}} with {{name}} at {{date}}", + new_event_request: "New event request: {{attendeeName}} - {{date}} - {{eventType}}", + confirm_or_reject_request: "Confirm or reject the request", + check_bookings_page_to_confirm_or_reject: + "Check your bookings page to confirm or reject the booking.", + event_awaiting_approval: "An event is waiting for your approval", + event_awaiting_approval_recurring: "A recurring event is waiting for your approval", + someone_requested_an_event: + "Someone has requested to schedule an event on your calendar.", + someone_requested_password_reset: + "Someone has requested a link to change your password.", + password_reset_instructions: + "If you didn't request this, you can safely ignore this email and your password will not be changed.", + event_awaiting_approval_subject: + "Awaiting Approval: {{eventType}} with {{name}} at {{date}}", + event_still_awaiting_approval: "An event is still waiting for your approval", + booking_submitted_subject: + "Booking Submitted: {{eventType}} with {{name}} at {{date}}", + your_meeting_has_been_booked: "Your meeting has been booked", + event_type_has_been_rescheduled_on_time_date: + "Your {{eventType}} with {{name}} has been rescheduled to {{time}} ({{timeZone}}) on {{date}}.", + event_has_been_rescheduled: "Updated - Your event has been rescheduled", + request_reschedule_title_attendee: "Request to reschedule your booking", + request_reschedule_subtitle: + "{{organizer}} has cancelled the booking and requested you to pick another time.", + request_reschedule_title_organizer: "You have requested {{attendee}} to reschedule", + request_reschedule_subtitle_organizer: + "You have cancelled the booking and {{attendee}} should pick a new booking time with you.", + reschedule_reason: "Reason for reschedule", + hi_user_name: "Hi {{name}}", + ics_event_title: "{{eventType}} with {{name}}", + new_event_subject: "New event: {{attendeeName}} - {{date}} - {{eventType}}", + join_by_entrypoint: "Join by {{entryPoint}}", + notes: "Notes", + manage_my_bookings: "Manage my bookings", + need_to_make_a_change: "Need to make a change?", + new_event_scheduled: "A new event has been scheduled.", + new_event_scheduled_recurring: "A new recurring event has been scheduled.", + invitee_email: "Invitee Email", + invitee_timezone: "Invitee Time Zone", + event_type: "Event Type", + enter_meeting: "Enter Meeting", + video_call_provider: "Video call provider", + meeting_id: "Meeting ID", + meeting_password: "Meeting Password", + meeting_url: "Meeting URL", + meeting_request_rejected: "Your meeting request has been rejected", + rescheduled_event_type_subject: + "Rescheduled: {{eventType}} with {{name}} at {{date}}", + requested_to_reschedule_subject_attendee: + "Action Required Reschedule: Please book a new time for {{eventType}} with {{name}}", + rejected_event_type_with_organizer: + "Rejected: {{eventType}} with {{organizer}} on {{date}}", + hi: "Hi", + join_team: "Join team", + manage_this_team: "Manage this team", + team_info: "Team Info", + request_another_invitation_email: + "If you prefer not to use {{toEmail}} as your Cal.com email or already have a Cal.com account, please request another invitation to that email.", + you_have_been_invited: "You have been invited to join the team {{teamName}}", + user_invited_you: "{{user}} invited you to join the team {{team}} on Cal.com", + hidden_team_member_title: "You are hidden in this team", + hidden_team_member_message: + "Your seat is not paid for, either upgrade to Pro or let the team owner know they can pay for your seat.", + hidden_team_owner_message: + "You need a pro account to use teams, you are hidden until you upgrade.", + link_expires: "p.s. It expires in {{expiresIn}} hours.", + upgrade_to_per_seat: "Upgrade to Per-Seat", + team_upgrade_seats_details: + "Of the {{memberCount}} members in your team, {{unpaidCount}} seat(s) are unpaid. At ${{seatPrice}}/month per seat the estimated total cost of your membership is ${{totalCost}}/month.", + team_upgraded_successfully: "Your team was upgraded successfully!", + use_link_to_reset_password: "Use the link below to reset your password", + hey_there: "Hey there,", + forgot_your_password_calcom: "Forgot your password? - Cal.com", + event_type_title: "{{eventTypeTitle}} | Event Type", + delete_webhook_confirmation_message: + "Are you sure you want to delete this webhook? You will no longer receive Cal.com meeting data at a specified URL, in real-time, when an event is scheduled or canceled.", + confirm_delete_webhook: "Yes, delete webhook", + edit_webhook: "Edit Webhook", + delete_webhook: "Delete Webhook", + webhook_status: "Webhook Status", + webhook_enabled: "Webhook Enabled", + webhook_disabled: "Webhook Disabled", + webhook_response: "Webhook response", + webhook_test: "Webhook test", + manage_your_webhook: "Manage your webhook", + webhook_created_successfully: "Webhook created successfully!", + webhook_updated_successfully: "Webhook updated successfully!", + webhook_removed_successfully: "Webhook removed successfully!", + payload_template: "Payload Template", + dismiss: "Dismiss", + no_data_yet: "No data yet", + ping_test: "Ping test", + add_to_homescreen: + "Add this app to your home screen for faster access and improved experience.", + upcoming: "Upcoming", + recurring: "Recurring", + past: "Past", + choose_a_file: "Choose a file...", + upload_image: "Upload image", + upload_target: "Upload {{target}}", + no_target: "No {{target}}", + slide_zoom_drag_instructions: "Slide to zoom, drag to reposition", + view_notifications: "View notifications", + view_public_page: "View public page", + sign_out: "Sign out", + add_another: "Add another", + until: "until", + powered_by: "powered by", + unavailable: "Unavailable", + set_work_schedule: "Set your work schedule", + change_bookings_availability: "Change when you are available for bookings", + select: "Select...", + "2fa_confirm_current_password": "Confirm your current password to get started.", + "2fa_scan_image_or_use_code": + "Scan the image below with the authenticator app on your phone or manually enter the text code instead.", + text: "Text", + multiline_text: "Multiline Text", + number: "Number", + checkbox: "Checkbox", + is_required: "Is required", + required: "Required", + input_type: "Input type", + rejected: "Rejected", + unconfirmed: "Unconfirmed", + guests: "Guests", + guest: "Guest", + web_conferencing_details_to_follow: + "Web conferencing details to follow in the confirmation email.", + the_username: "The username", + username: "Username", + is_still_available: "is still available.", + documentation: "Documentation", + documentation_description: "Learn how to integrate our tools with your app", + api_reference: "API Reference", + api_reference_description: "A complete API reference for our libraries", + blog: "Blog", + blog_description: "Read our latest news and articles", + join_our_community: "Join our community", + join_our_slack: "Join our Slack", + claim_username_and_schedule_events: "Claim your username and schedule events", + popular_pages: "Popular pages", + register_now: "Register now", + register: "Register", + page_doesnt_exist: "This page does not exist.", + check_spelling_mistakes_or_go_back: + "Check for spelling mistakes or go back to the previous page.", + "404_page_not_found": "404: This page could not be found.", + getting_started: "Getting Started", + "15min_meeting": "15 Min Meeting", + "30min_meeting": "30 Min Meeting", + secret_meeting: "Secret Meeting", + login_instead: "Login instead", + already_have_an_account: "Already have an account?", + create_account: "Create Account", + confirm_password: "Confirm password", + create_your_account: "Create your account", + sign_up: "Sign up", + youve_been_logged_out: "You've been logged out", + hope_to_see_you_soon: "We hope to see you again soon!", + logged_out: "Logged out", + please_try_again_and_contact_us: + "Please try again and contact us if the issue persists.", + incorrect_2fa_code: "Two-factor code is incorrect.", + no_account_exists: "No account exists matching that email address.", + "2fa_enabled_instructions": + "Two-factor authentication enabled. Please enter the six-digit code from your authenticator app.", + "2fa_enter_six_digit_code": + "Enter the six-digit code from your authenticator app below.", + create_an_account: "Create an account", + dont_have_an_account: "Don't have an account?", + "2fa_code": "Two-Factor Code", + sign_in_account: "Sign in to your account", + sign_in: "Sign in", + go_back_login: "Go back to the login page", + error_during_login: + "An error occurred when logging you in. Head back to the login screen and try again.", + request_password_reset: "Request Password Reset", + forgot_password: "Forgot Password", + forgot: "Forgot?", + done: "Done", + check_email_reset_password: + "Check your email. We sent you a link to reset your password.", + finish: "Finish", + few_sentences_about_yourself: + "A few sentences about yourself. This will appear on your personal url page.", + nearly_there: "Nearly there", + nearly_there_instructions: + "Last thing, a brief description about you and a photo really help you get bookings and let people know who they’re booking with.", + set_availability_instructions: + "Define ranges of time when you are available on a recurring basis. You can create more of these later and assign them to different calendars.", + set_availability: "Set your availability", + continue_without_calendar: "Continue without calendar", + connect_your_calendar: "Connect your calendar", + connect_your_calendar_instructions: + "Connect your calendar to automatically check for busy times and new events as they’re scheduled.", + set_up_later: "Set up later", + current_time: "Current time", + welcome: "Welcome", + welcome_to_calcom: "Welcome to Cal.com", + welcome_instructions: + "Tell us what to call you and let us know what timezone you’re in. You’ll be able to edit this later.", + connect_caldav: "Connect to CalDav Server", + credentials_stored_and_encrypted: "Your credentials will be stored and encrypted.", + connect: "Connect", + try_for_free: "Try it for free", + create_booking_link_with_calcom: "Create your own booking link with Cal.com", + who: "Who", + what: "What", + when: "When", + where: "Where", + add_to_calendar: "Add to calendar", + other: "Other", + emailed_you_and_attendees: + "We emailed you and the other attendees a calendar invitation with all the details.", + emailed_you_and_attendees_recurring: + "We emailed you and the other attendees a calendar invitation for the first of these recurring events.", + emailed_you_and_any_other_attendees: + "You and any other attendees have been emailed with this information.", + needs_to_be_confirmed_or_rejected: + "Your booking still needs to be confirmed or rejected.", + needs_to_be_confirmed_or_rejected_recurring: + "Your recurring meeting still needs to be confirmed or rejected.", + user_needs_to_confirm_or_reject_booking: + "{{user}} still needs to confirm or reject the booking.", + user_needs_to_confirm_or_reject_booking_recurring: + "{{user}} still needs to confirm or reject each booking of the recurring meeting.", + meeting_is_scheduled: "This meeting is scheduled", + meeting_is_scheduled_recurring: "The recurring events are scheduled", + submitted: "Your booking has been submitted", + submitted_recurring: "Your recurring meeting has been submitted", + booking_submitted: "Your booking has been submitted", + booking_submitted_recurring: "Your recurring meeting has been submitted", + booking_confirmed: "Your booking has been confirmed", + booking_confirmed_recurring: "Your recurring meeting has been confirmed", + warning_recurring_event_payment: + "Payments are not supported with Recurring Events yet", + warning_payment_recurring_event: + "Recurring events are not supported with Payments yet", + enter_new_password: "Enter the new password you'd like for your account.", + reset_password: "Reset Password", + change_your_password: "Change your password", + try_again: "Try Again", + request_is_expired: "That Request is Expired.", + reset_instructions: + "Enter the email address associated with your account and we will send you a link to reset your password.", + request_is_expired_instructions: + "That request is expired. Go back and enter the email associated with your account and we will send you another link to reset your password.", + whoops: "Whoops", + login: "Login", + success: "Success", + failed: "Failed", + password_has_been_reset_login: + "Your password has been reset. You can now login with your newly created password.", + unexpected_error_try_again: "An unexpected error occurred. Try again.", + sunday_time_error: "Invalid time on Sunday", + monday_time_error: "Invalid time on Monday", + tuesday_time_error: "Invalid time on Tuesday", + wednesday_time_error: "Invalid time on Wednesday", + thursday_time_error: "Invalid time on Thursday", + friday_time_error: "Invalid time on Friday", + saturday_time_error: "Invalid time on Saturday", + error_end_time_before_start_time: "End time cannot be before start time", + error_end_time_next_day: "End time cannot be greater than 24 hours", + back_to_bookings: "Back to bookings", + free_to_pick_another_event_type: "Feel free to pick another event anytime.", + cancelled: "Cancelled", + cancellation_successful: "Cancellation successful", + really_cancel_booking: "Really cancel your booking?", + cannot_cancel_booking: "You cannot cancel this booking", + reschedule_instead: "Instead, you could also reschedule it.", + event_is_in_the_past: "The event is in the past", + error_with_status_code_occured: "An error with status code {{status}} occurred.", + booking_already_cancelled: "This booking was already cancelled", + go_back_home: "Go back home", + or_go_back_home: "Or go back home", + no_availability: "Unavailable", + no_meeting_found: "No Meeting Found", + no_meeting_found_description: + "This meeting does not exist. Contact the meeting owner for an updated link.", + no_status_bookings_yet: "No {{status}} bookings, yet", + no_status_bookings_yet_description: + "You have no {{status}} bookings. {{description}}", + event_between_users: "{{eventName}} between {{host}} and {{attendeeName}}", + bookings: "Bookings", + bookings_description: + "See upcoming and past events booked through your event type links.", + upcoming_bookings: "As soon as someone books a time with you it will show up here.", + recurring_bookings: + "As soon as someone books a recurring meeting with you it will show up here.", + past_bookings: "Your past bookings will show up here.", + cancelled_bookings: "Your cancelled bookings will show up here.", + on: "on", + and: "and", + calendar_shows_busy_between: "Your calendar shows you as busy between", + troubleshoot: "Troubleshoot", + troubleshoot_description: + "Understand why certain times are available and others are blocked.", + overview_of_day: "Here is an overview of your day on", + hover_over_bold_times_tip: "Tip: Hover over the bold times for a full timestamp", + start_time: "Start time", + end_time: "End time", + buffer_time: "Buffer time", + before_event: "Before event", + after_event: "After event", + event_buffer_default: "No buffer time", + buffer: "Buffer", + your_day_starts_at: "Your day starts at", + your_day_ends_at: "Your day ends at", + launch_troubleshooter: "Launch troubleshooter", + troubleshoot_availability: + "Troubleshoot your availability to explore why your times are showing as they are.", + change_available_times: "Change available times", + change_your_available_times: "Change your available times", + change_start_end: "Change the start and end times of your day", + change_start_end_buffer: + "Set the start and end time of your day and a minimum buffer between your meetings.", + current_start_date: "Currently, your day is set to start at", + start_end_changed_successfully: + "The start and end times for your day have been changed successfully.", + and_end_at: "and end at", + light: "Light", + dark: "Dark", + automatically_adjust_theme: + "Automatically adjust theme based on invitee preferences", + user_dynamic_booking_disabled: + "Some of the users in the group have currently disabled dynamic group bookings", + allow_dynamic_booking_tooltip: + "Group booking links that can be created dynamically by adding multiple usernames with a '+'. example: 'cal.com/bailey+peer'", + allow_dynamic_booking: "Allow attendees to book you through dynamic group bookings", + email: "Email", + email_placeholder: "jdoe@example.com", + full_name: "Full name", + browse_api_documentation: "Browse our API documentation", + leverage_our_api: "Leverage our API for full control and customizability.", + create_webhook: "Create Webhook", + booking_cancelled: "Booking Cancelled", + booking_rescheduled: "Booking Rescheduled", + booking_created: "Booking Created", + event_triggers: "Event Triggers", + subscriber_url: "Subscriber Url", + create_new_webhook: "Create a new webhook", + webhooks: "Webhooks", + team_webhooks: "Team Webhooks", + create_new_webhook_to_account: "Create a new webhook to your account", + new_webhook: "New Webhook", + receive_cal_meeting_data: + "Receive Cal meeting data at a specified URL, in real-time, when an event is scheduled or cancelled.", + receive_cal_event_meeting_data: + "Receive Cal meeting data at a specified URL, in real-time, when this event is scheduled or cancelled.", + responsive_fullscreen_iframe: "Responsive full screen iframe", + loading: "Loading...", + deleting: "Deleting...", + standard_iframe: "Standard iframe", + developer: "Developer", + manage_developer_settings: "Manage your developer settings.", + iframe_embed: "iframe Embed", + embed_calcom: "The easiest way to embed Cal.com on your website.", + integrate_using_embed_or_webhooks: + "Integrate with your website using our embed options, or get real-time booking information using custom webhooks.", + schedule_a_meeting: "Schedule a meeting", + view_and_manage_billing_details: "View and manage your billing details", + view_and_edit_billing_details: + "View and edit your billing details, as well as cancel your subscription.", + go_to_billing_portal: "Go to the billing portal", + need_anything_else: "Need anything else?", + further_billing_help: + "If you need any further help with billing, our support team are here to help.", + contact_our_support_team: "Contact our support team", + uh_oh: "Uh oh!", + no_event_types_have_been_setup: "This user hasn't set up any event types yet.", + edit_logo: "Edit logo", + upload_a_logo: "Upload a logo", + remove_logo: "Remove logo", + enable: "Enable", + code: "Code", + code_is_incorrect: "Code is incorrect.", + add_an_extra_layer_of_security: + "Add an extra layer of security to your account in case your password is stolen.", + "2fa": "Two-Factor Authentication", + enable_2fa: "Enable two-factor authentication", + disable_2fa: "Disable two-factor authentication", + disable_2fa_recommendation: + "If you need to disable 2FA, we recommend re-enabling it as soon as possible.", + error_disabling_2fa: "Error disabling two-factor authentication", + error_enabling_2fa: "Error setting up two-factor authentication", + security: "Security", + manage_account_security: "Manage your account's security.", + password: "Password", + password_updated_successfully: "Password updated successfully", + password_has_been_changed: "Your password has been successfully changed.", + error_changing_password: "Error changing password", + something_went_wrong: "Something went wrong.", + something_doesnt_look_right: "Something doesn't look right?", + please_try_again: "Please try again.", + super_secure_new_password: "Your super secure new password", + new_password: "New Password", + your_old_password: "Your old password", + current_password: "Current Password", + change_password: "Change Password", + new_password_matches_old_password: + "New password matches your old password. Please choose a different password.", + current_incorrect_password: "Current password is incorrect", + incorrect_password: "Password is incorrect.", + "1_on_1": "1-on-1", + "24_h": "24h", + use_setting: "Use setting", + am_pm: "am/pm", + time_options: "Time options", + january: "January", + february: "February", + march: "March", + april: "April", + may: "May", + june: "June", + july: "July", + august: "August", + september: "September", + october: "October", + november: "November", + december: "December", + monday: "Monday", + tuesday: "Tuesday", + wednesday: "Wednesday", + thursday: "Thursday", + friday: "Friday", + saturday: "Saturday", + sunday: "Sunday", + all_booked_today: "All booked today.", + slots_load_fail: "Could not load the available time slots.", + additional_guests: "+ Additional Guests", + your_name: "Your name", + email_address: "Email address", + location: "Location", + yes: "yes", + no: "no", + additional_notes: "Additional notes", + booking_fail: "Could not book the meeting.", + reschedule_fail: "Could not reschedule the meeting.", + share_additional_notes: + "Please share anything that will help prepare for our meeting.", + booking_confirmation: "Confirm your {{eventTypeTitle}} with {{profileName}}", + booking_reschedule_confirmation: + "Reschedule your {{eventTypeTitle}} with {{profileName}}", + in_person_meeting: "In-person meeting", + link_meeting: "Link meeting", + phone_call: "Phone call", + your_number: "Your phone number", + phone_number: "Phone Number", + attendee_phone_number: "Attendee Phone Number", + host_phone_number: "Your Phone Number", + enter_phone_number: "Enter phone number", + reschedule: "Reschedule", + reschedule_this: "Reschedule instead", + book_a_team_member: "Book a team member instead", + or: "OR", + go_back: "Go back", + email_or_username: "Email or Username", + send_invite_email: "Send an invite email", + role: "Role", + edit_role: "Edit Role", + edit_team: "Edit team", + reject: "Reject", + reject_all: "Reject all", + accept: "Accept", + leave: "Leave", + profile: "Profile", + my_team_url: "My team URL", + team_name: "Team name", + your_team_name: "Your team name", + team_updated_successfully: "Team updated successfully", + your_team_updated_successfully: "Your team has been updated successfully.", + about: "About", + team_description: + "A few sentences about your team. This will appear on your team's URL page.", + members: "Members", + member: "Member", + owner: "Owner", + admin: "Admin", + new_member: "New Member", + invite: "Invite", + invite_new_member: "Invite a new member", + invite_new_team_member: "Invite someone to your team.", + change_member_role: "Change team member role", + disable_cal_branding: "Disable Cal.com branding", + disable_cal_branding_description: + "Hide all Cal.com branding from your public pages.", + danger_zone: "Danger Zone", + back: "Back", + cancel: "Cancel", + apply: "Apply", + cancel_event: "Cancel event", + continue: "Continue", + confirm: "Confirm", + confirm_all: "Confirm all", + disband_team: "Disband Team", + disband_team_confirmation_message: + "Are you sure you want to disband this team? Anyone who you've shared this team link with will no longer be able to book using it.", + remove_member_confirmation_message: + "Are you sure you want to remove this member from the team?", + confirm_disband_team: "Yes, disband team", + confirm_remove_member: "Yes, remove member", + remove_member: "Remove member", + manage_your_team: "Manage your team", + no_teams: "You don't have any teams yet.", + no_teams_description: + "Teams allow others to book events shared between your coworkers.", + submit: "Submit", + delete: "Delete", + update: "Update", + save: "Save", + pending: "Pending", + open_options: "Open options", + copy_link: "Copy link to event", + share: "Share", + share_event: "Would you mind booking my cal or send me your link?", + copy_link_team: "Copy link to team", + leave_team: "Leave team", + confirm_leave_team: "Yes, leave team", + leave_team_confirmation_message: + "Are you sure you want to leave this team? You will no longer be able to book using it.", + user_from_team: "{{user}} from {{team}}", + preview: "Preview", + link_copied: "Link copied!", + private_link_copied: "Private link copied!", + link_shared: "Link shared!", + title: "Title", + description: "Description", + quick_video_meeting: "A quick video meeting.", + scheduling_type: "Scheduling Type", + preview_team: "Preview team", + collective: "Collective", + collective_description: + "Schedule meetings when all selected team members are available.", + duration: "Duration", + minutes: "Minutes", + round_robin: "Round Robin", + round_robin_description: "Cycle meetings between multiple team members.", + url: "URL", + hidden: "Hidden", + readonly: "Readonly", + one_time_link: "One-time link", + plan_description: "You're currently on the {{plan}} plan.", + plan_upgrade_invitation: + "Upgrade your account to the pro plan to unlock all of the features we have to offer.", + plan_upgrade: + "You need to upgrade your plan to have more than one active event type.", + plan_upgrade_teams: "You need to upgrade your plan to create a team.", + plan_upgrade_instructions: "You can <1>upgrade here.", + event_types_page_title: "Event Types", + event_types_page_subtitle: + "Create events to share for people to book on your calendar.", + new_event_type_btn: "New event type", + new_event_type_heading: "Create your first event type", + new_event_type_description: + "Event types enable you to share links that show available times on your calendar and allow people to make bookings with you.", + new_event_title: "Add a new event type", + new_event_subtitle: "Create an event type under your name or a team.", + new_team_event: "Add a new team event type", + new_event_description: "Create a new event type for people to book times with.", + event_type_created_successfully: + "{{eventTypeTitle}} event type created successfully", + event_type_updated_successfully: + "{{eventTypeTitle}} event type updated successfully", + event_type_deleted_successfully: "Event type deleted successfully", + web3_metamask_added: "Metamask added successfully", + web3_metamask_disconnected: "Metamask disconnected successfully", + hours: "Hours", + your_email: "Your Email", + change_avatar: "Change Avatar", + language: "Language", + timezone: "Timezone", + first_day_of_week: "First Day of Week", + repeats_up_to: "Repeats up to {{count}} time", + repeats_up_to_plural: "Repeats up to {{count}} times", + every_for_freq: "Every {{freq}} for", + repeats_every: "Repeats every", + weekly: "week", + weekly_plural: "weeks", + monthly: "month", + monthly_plural: "months", + yearly: "year", + yearly_plural: "years", + plus_more: "+ {{count}} more", + max: "Max", + single_theme: "Single Theme", + brand_color: "Brand Color", + light_brand_color: "Brand Color (Light Theme)", + dark_brand_color: "Brand Color (Dark Theme)", + file_not_named: "File is not named [idOrSlug]/[user]", + create_team: "Create Team", + name: "Name", + create_new_team_description: "Create a new team to collaborate with users.", + create_new_team: "Create a new team", + open_invitations: "Open Invitations", + new_team: "New Team", + create_first_team_and_invite_others: + "Create your first team and invite other users to work together with you.", + create_team_to_get_started: "Create a team to get started", + teams: "Teams", + team_billing: "Team Billing", + upgrade_to_flexible_pro_title: "We've changed billing for teams", + upgrade_to_flexible_pro_message: + "There are members in your team without a seat. Upgrade your pro plan to cover missing seats.", + changed_team_billing_info: + "As of January 2022 we charge on a per-seat basis for team members. Members of your team who had PRO for free are now on a 14 day trial. Once their trial expires these members will be hidden from your team unless you upgrade now.", + create_manage_teams_collaborative: + "Create and manage teams to use collaborative features.", + only_available_on_pro_plan: "This feature is only available in Pro plan", + remove_cal_branding_description: + "In order to remove the Cal branding from your booking pages, you need to upgrade to a Pro account.", + edit_profile_info_description: + "Edit your profile information, which shows on your scheduling link.", + change_email_tip: + "You may need to log out and back in to see the change take effect.", + little_something_about: "A little something about yourself.", + profile_updated_successfully: "Profile updated successfully", + your_user_profile_updated_successfully: + "Your user profile has been updated successfully.", + user_cannot_found_db: "User seems logged in but cannot be found in the db", + embed_and_webhooks: "Embed & Webhooks", + enabled: "Enabled", + disabled: "Disabled", + disable: "Disable", + billing: "Billing", + manage_your_billing_info: + "Manage your billing information and cancel your subscription.", + availability: "Availability", + configure_availability: "Configure times when you are available for bookings.", + change_weekly_schedule: "Change your weekly schedule", + logo: "Logo", + error: "Error", + team_logo: "Team Logo", + add_location: "Add a location", + attendees: "Attendees", + add_attendees: "Add attendees", + show_advanced_settings: "Show advanced settings", + event_name: "Event Name", + event_name_tooltip: "The name that will appear in calendars", + meeting_with_user: "Meeting with {ATTENDEE}", + additional_inputs: "Additional Inputs", + label: "Label", + placeholder: "Placeholder", + type: "Type", + edit: "Edit", + add_input: "Add an Input", + disable_notes: "Hide notes in calendar", + disable_notes_description: + "For privacy reasons, additional inputs and notes will be hidden in the calendar entry. They will still be sent to your email.", + opt_in_booking: "Opt-in Booking", + opt_in_booking_description: + "The booking needs to be manually confirmed before it is pushed to the integrations and a confirmation mail is sent.", + recurring_event: "Recurring Event", + recurring_event_description: "People can subscribe for recurring events", + starting: "Starting", + disable_guests: "Disable Guests", + disable_guests_description: "Disable adding additional guests while booking.", + private_link: "Generate private URL", + copy_private_link: "Copy private link", + private_link_description: + "Generate a private URL to share without exposing your Cal username", + invitees_can_schedule: "Invitees can schedule", + date_range: "Date Range", + calendar_days: "calendar days", + business_days: "business days", + set_address_place: "Set an address or place", + set_link_meeting: "Set a link to the meeting", + cal_invitee_phone_number_scheduling: + "Cal will ask your invitee to enter a phone number before scheduling.", + cal_provide_google_meet_location: "Cal will provide a Google Meet location.", + cal_provide_zoom_meeting_url: "Cal will provide a Zoom meeting URL.", + cal_provide_tandem_meeting_url: "Cal will provide a Tandem meeting URL.", + cal_provide_video_meeting_url: "Cal will provide a video meeting URL.", + cal_provide_jitsi_meeting_url: "We will generate a Jitsi Meet URL for you.", + cal_provide_huddle01_meeting_url: + "Cal will provide a Huddle01 web3 video meeting URL.", + cal_provide_teams_meeting_url: + "Cal will provide a MS Teams meeting URL. NOTE: MUST HAVE A WORK OR SCHOOL ACCOUNT", + require_payment: "Require Payment", + commission_per_transaction: "commission per transaction", + event_type_updated_successfully_description: + "Your event type has been updated successfully.", + hide_event_type: "Hide event type", + edit_location: "Edit location", + into_the_future: "into the future", + within_date_range: "Within a date range", + indefinitely_into_future: "Indefinitely into the future", + add_new_custom_input_field: "Add new custom input field", + quick_chat: "Quick Chat", + add_new_team_event_type: "Add a new team event type", + add_new_event_type: "Add a new event type", + new_event_type_to_book_description: + "Create a new event type for people to book times with.", + length: "Length", + minimum_booking_notice: "Minimum booking notice", + slot_interval: "Time-slot intervals", + slot_interval_default: "Use event length (default)", + delete_event_type_description: + "Are you sure you want to delete this event type? Anyone who you've shared this link with will no longer be able to book using it.", + delete_event_type: "Delete Event Type", + confirm_delete_event_type: "Yes, delete event type", + delete_account: "Delete account", + confirm_delete_account: "Yes, delete account", + delete_account_confirmation_message: + "Are you sure you want to delete your Cal.com account? Anyone who you've shared your account link with will no longer be able to book using it and any preferences you have saved will be lost.", + integrations: "Integrations", + apps: "Apps", + app_store: "App Store", + app_store_description: "Connecting people, technology and the workplace.", + settings: "Settings", + event_type_moved_successfully: "Event type has been moved successfully", + next_step: "Skip step", + prev_step: "Prev step", + installed: "Installed", + active_install: "{{count}} active install", + active_install_plural: "{{count}} active installs", + globally_install: "Globally installed", + disconnect: "Disconnect", + embed_your_calendar: "Embed your calendar within your webpage", + connect_your_favourite_apps: "Connect your favourite apps.", + automation: "Automation", + configure_how_your_event_types_interact: + "Configure how your event types should interact with your calendars.", + select_destination_calendar: "Create events on", + connect_additional_calendar: "Connect additional calendar", + conferencing: "Conferencing", + calendar: "Calendar", + not_installed: "Not installed", + error_password_mismatch: "Passwords don't match.", + error_required_field: "This field is required.", + status: "Status", + team_view_user_availability: "View user availability", + team_view_user_availability_disabled: + "User needs to accept invite to view availability", + set_as_away: "Set yourself as away", + set_as_free: "Disable away status", + user_away: "This user is currently away.", + user_away_description: + "The person you are trying to book has set themselves to away, and therefore is not accepting new bookings.", + meet_people_with_the_same_tokens: "Meet people with the same tokens", + only_book_people_and_allow: + "Only book and allow bookings from people who share the same tokens, DAOs, or NFTs.", + saml_config_deleted_successfully: "SAML configuration deleted successfully", + account_created_with_identity_provider: + "Your account was created using an Identity Provider.", + account_managed_by_identity_provider: "Your account is managed by {{provider}}", + account_managed_by_identity_provider_description: + "To change your email, password, enable two-factor authentication and more, please visit your {{provider}} account settings.", + signin_with_google: "Sign in with Google", + signin_with_saml: "Sign in with SAML", + saml_configuration: "SAML configuration", + delete_saml_configuration: "Delete SAML configuration", + delete_saml_configuration_confirmation_message: + "Are you sure you want to delete the SAML configuration? Your team members who use SAML login will no longer be able to access Cal.com.", + confirm_delete_saml_configuration: "Yes, delete SAML configuration", + saml_not_configured_yet: "SAML not configured yet", + saml_configuration_description: + "Please paste the SAML metadata from your Identity Provider in the textbox below to update your SAML configuration.", + saml_configuration_placeholder: + "Please paste the SAML metadata from your Identity Provider here", + saml_configuration_update_failed: "SAML configuration update failed", + saml_configuration_delete_failed: "SAML configuration delete failed", + saml_email_required: + "Please enter an email so we can find your SAML Identity Provider", + you_will_need_to_generate: + "You will need to generate an access token from your old scheduling tool.", + import: "Import", + import_from: "Import from", + access_token: "Access token", + visit_roadmap: "Roadmap", + popular_categories: "Popular Categories", + trending_apps: "Trending Apps", + all_apps: "All Apps", + installed_apps: "Installed Apps", + empty_installed_apps_headline: "No apps installed", + empty_installed_apps_description: + "Apps enable you to enhance your workflow and improve your scheduling life significantly.", + empty_installed_apps_button: "Explore the App Store", + manage_your_connected_apps: "Manage your installed apps or change settings", + browse_apps: "Browse Apps", + features: "Features", + permissions: "Permissions", + terms_and_privacy: "Terms and Privacy", + published_by: "Published by {{author}}", + subscribe: "Subscribe", + buy: "Buy", + install_app: "Install App", + categories: "Categories", + pricing: "Pricing", + learn_more: "Learn more", + privacy_policy: "Privacy Policy", + terms_of_service: "Terms of Service", + remove: "Remove", + add: "Add", + verify_wallet: "Verify Wallet", + connect_metamask: "Connect Metamask", + create_events_on: "Create events on:", + missing_license: "Missing License", + signup_requires: "Commercial license required", + signup_requires_description: + "Cal.com, Inc. currently does not offer a free open source version of the sign up page. To receive full access to the signup components you need to acquire a commercial license. For personal use we recommend the Prisma Data Platform or any other Postgres interface to create accounts.", + next_steps: "Next Steps", + acquire_commercial_license: "Acquire a commercial license", + the_infrastructure_plan: + "The infrastructure plan is usage-based and has startup-friendly discounts.", + prisma_studio_tip: "Create an account via Prisma Studio", + prisma_studio_tip_description: "Learn how to set up your first user", + contact_sales: "Contact Sales", + error_404: "Error 404", + default: "Default", + set_to_default: "Set to Default", + new_schedule_btn: "New schedule", + add_new_schedule: "Add a new schedule", + delete_schedule: "Delete schedule", + schedule_created_successfully: "{{scheduleName}} schedule created successfully", + availability_updated_successfully: "{{scheduleName}} schedule updated successfully", + schedule_deleted_successfully: "Schedule deleted successfully", + default_schedule_name: "Working Hours", + new_schedule_heading: "Create an availability schedule", + new_schedule_description: + "Creating availability schedules allows you to manage availability across event types. They can be applied to one or more event types.", + requires_ownership_of_a_token: + "Requires ownership of a token belonging to the following address:", + example_name: "John Doe", + time_format: "Time format", + "12_hour": "12 hour", + "24_hour": "24 hour", + redirect_success_booking: "Redirect on booking ", + you_are_being_redirected: + 'You are being redirected to {{ url }} in $t(second, {"count": {{seconds}} }).', + external_redirect_url: "https://example.com/redirect-to-my-success-page", + redirect_url_upgrade_description: + "In order to use this feature, you need to upgrade to a Pro account.", + duplicate: "Duplicate", + offer_seats: "Offer seats", + offer_seats_description: + "Offer seats to bookings (This disables guests & opt in bookings)", + seats_available: "Seats available", + number_of_seats: "Number of seats per booking", + enter_number_of_seats: "Enter number of seats", + you_can_manage_your_schedules: + "You can manage your schedules on the Availability page.", + booking_full: "No more seats available", + api_keys: "API Keys", + api_key_modal_subtitle: + "API keys allow you to make API calls for your own account.", + api_keys_subtitle: "Generate API keys to use for accessing your own account.", + create_api_key: "Create an API key", + personal_note: "Name this key", + personal_note_placeholder: "E.g. Development", + api_key_no_note: "Nameless API key", + api_key_never_expires: "This API key has no expiration date", + edit_api_key: "Edit API key", + never_expire_key: "Never expires", + delete_api_key: "Revoke API key", + success_api_key_created: "API key created successfully", + success_api_key_edited: "API key updated successfully", + create: "Create", + success_api_key_created_bold_tagline: "Save this API key somewhere safe.", + you_will_only_view_it_once: + "You will not be able to view it again once you close this modal.", + copy_to_clipboard: "Copy to clipboard", + enabled_after_update: "Enabled after update", + enabled_after_update_description: "The private link will work after saving", + confirm_delete_api_key: "Revoke this API key", + revoke_api_key: "Revoke API key", + api_key_copied: "API key copied!", + delete_api_key_confirm_title: "Permanently remove this API key from your account?", + copy: "Copy", + expire_date: "Expiration date", + expired: "Expired", + never_expires: "Never expires", + expires: "Expires", + request_reschedule_booking: "Request to reschedule your booking", + reason_for_reschedule: "Reason for reschedule", + book_a_new_time: "Book a new time", + reschedule_request_sent: "Reschedule request sent", + reschedule_modal_description: + "This will cancel the scheduled meeting, notify the scheduler and ask them to pick a new time.", + reason_for_reschedule_request: "Reason for reschedule request", + send_reschedule_request: "Request reschedule ", + edit_booking: "Edit booking", + reschedule_booking: "Reschedule booking", + former_time: "Former time", + confirmation_page_gif: "Gif for confirmation page", + search: "Search", + impersonate: "Impersonate", + user_impersonation_heading: "User Impersonation", + user_impersonation_description: + "Allows our support team to temporarily sign in as you to help us quickly resolve any issues you report to us.", + impersonate_user_tip: "All uses of this feature is audited.", + impersonating_user_warning: 'Impersonating username "{{user}}".', + impersonating_stop_instructions: "<0>Click Here to stop.", + event_location_changed: "Updated - Your event changed the location", + location_changed_event_type_subject: + "Location Changed: {{eventType}} with {{name}} at {{date}}", + current_location: "Current Location", + user_phone: "Your phone number", + new_location: "New Location", + no_location: "No location defined", + set_location: "Set Location", + update_location: "Update Location", + location_updated: "Location updated", + email_validation_error: "That doesn't look like an email address", + place_where_cal_widget_appear: + "Place this code in your HTML where you want your Cal widget to appear.", + copy_code: "Copy Code", + code_copied: "Code copied!", + how_you_want_add_cal_site: "How do you want to add Cal to your site?", + choose_ways_put_cal_site: + "Choose one of the following ways to put Cal on your site.", + setting_up_zapier: "Setting up your Zapier integration", + generate_api_key: "Generate Api Key", + your_unique_api_key: "Your unique API key", + copy_safe_api_key: + "Copy this API key and save it somewhere safe. If you lose this key you have to generate a new one.", + zapier_setup_instructions: + "<0>Log into your Zapier account and create a new Zap.<1>Select Cal.com as your Trigger app. Also choose a Trigger event.<2>Choose your account and then enter your Unique API Key.<3>Test your Trigger.<4>You're set!", + install_zapier_app: "Please first install the Zapier App in the app store.", + go_to_app_store: "Go to App Store", + calendar_error: + "Something went wrong, try reconnecting your calendar with all necessary permissions", + set_your_phone_number: "Set a phone number for the meeting", + calendar_no_busy_slots: "There are no busy slots", + display_location_label: "Display on booking page", + display_location_info_badge: + "Location will be visible before the booking is confirmed", + add_gif: "Add GIF", + search_giphy: "Search Giphy", + add_link_from_giphy: "Add link from Giphy", + add_gif_to_confirmation: "Adding a GIF to confirmation page", + find_gif_spice_confirmation: "Find GIF to spice up your confirmation page", + share_feedback: "Share feedback", + resources: "Resources", + support_documentation: "Support documentation", + developer_documentation: "Developer Documentation", + get_in_touch: "Get in touch", + contact_support: "Contact Support", + feedback: "Feedback", + submitted_feedback: "Thank you for your feedback!", + feedback_error: "Error sending feedback", + comments: "Comments", + booking_details: "Booking details", + or_lowercase: "or", + nevermind: "Nevermind", + go_to: "Go to: ", + zapier_invite_link: "Zapier Invite Link", + meeting_url_provided_after_confirmed: + "A Meeting URL will be created once the event is confirmed.", + attendees_name: "Attendee's name", + dynamically_display_attendee_or_organizer: + "Dynamically display the name of your attendee for you, or your name if it's viewed by your attendee", + event_location: "Event's location", + reschedule_optional: "Reason for rescheduling (optional)", + reschedule_placeholder: "Let others know why you need to reschedule", + event_cancelled: "This event is cancelled", + emailed_information_about_cancelled_event: + "We emailed you and the other attendees to let them know.", + this_input_will_shown_booking_this_event: + "This input will be shown when booking this event", + meeting_url_in_conformation_email: "Meeting url is in the confirmation email", + url_start_with_https: "URL needs to start with http:// or https://", + number_provided: "Phone number will be provided", + remove_app: "Remove App", + yes_remove_app: "Yes, remove app", + are_you_sure_you_want_to_remove_this_app: + "Are you sure you want to remove this app?", + web_conference: "Web conference", + }, + vital: { + connected_vital_app: "Connected with", + vital_app_sleep_automation: "Sleeping reschedule automation", + vital_app_automation_description: + "You can select different parameters to trigger the reschedule based on your sleeping metrics.", + vital_app_parameter: "Parameter", + vital_app_trigger: "Trigger at below or equal than", + vital_app_save_button: "Save configuration", + vital_app_total_label: "Total (total = rem + light sleep + deep sleep)", + vital_app_duration_label: "Duration (duration = bedtime end - bedtime start)", + vital_app_hours: "hours", + vital_app_save_success: "Success saving your Vital Configurations", + vital_app_save_error: "An error ocurred saving your Vital Configurations", + }, + }, + }, + initialLocale: "en", + userConfig: { + i18n: { + defaultLocale: "en", + locales: [ + "en", + "fr", + "it", + "ru", + "es", + "de", + "pt", + "ro", + "nl", + "pt-BR", + "es-419", + "ko", + "ja", + "pl", + "ar", + "iw", + "zh-CN", + "zh-TW", + "cs", + "sr", + "sv", + "vi", + ], + }, + localePath: "/Users/alex/Repositories/cal.com/apps/web/public/static/locales", + reloadOnPrerender: true, + default: { + i18n: { + defaultLocale: "en", + locales: [ + "en", + "fr", + "it", + "ru", + "es", + "de", + "pt", + "ro", + "nl", + "pt-BR", + "es-419", + "ko", + "ja", + "pl", + "ar", + "iw", + "zh-CN", + "zh-TW", + "cs", + "sr", + "sv", + "vi", + ], + }, + localePath: "/Users/alex/Repositories/cal.com/apps/web/public/static/locales", + reloadOnPrerender: true, + }, + }, + }, + }, + locale: "en", + }, + dataUpdateCount: 1, + dataUpdatedAt: 1654602871785, + error: null, + errorUpdateCount: 0, + errorUpdatedAt: 0, + fetchFailureCount: 0, + fetchMeta: null, + isFetching: false, + isInvalidated: false, + isPaused: false, + status: "success", + }, + queryKey: ["viewer.i18n"], + queryHash: '["viewer.i18n"]', + }, + ], + }, + meta: { + referentialEqualities: { + "queries.0.state.data.i18n._nextI18Next.userConfig.i18n": [ + "queries.0.state.data.i18n._nextI18Next.userConfig.default.i18n", + ], + "queries.0.state.data.i18n._nextI18Next.userConfig.i18n.locales": [ + "queries.0.state.data.i18n._nextI18Next.userConfig.default.i18n.locales", + ], + }, + }, + }, + booking: null, + }; + const { t } = useLocale(); + return props.away ? (
@@ -37,7 +1225,7 @@ export default function Type(props: AvailabilityPageProps) {
- ) : props.isDynamicGroup && !props.profile.allowDynamicBooking ? ( + ) : data.isDynamicGroup && !data.profile.allowDynamicBooking ? (
@@ -53,15 +1241,14 @@ export default function Type(props: AvailabilityPageProps) {
) : ( - + ); } -export const getServerSideProps = async (context: GetServerSidePropsContext) => { +/*export const getServerSideProps = async (context: GetServerSidePropsContext) => { const ssr = await ssrInit(context); - // get query params and typecast them to string - // (would be even better to assert them instead of typecasting) - const usernameList = getUsernameList(context.query.user as string); + + const usernameList = getUsernameList(context.query.user); const userParam = asStringOrNull(context.query.user); const typeParam = asStringOrNull(context.query.type); @@ -331,8 +1518,30 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => eventType: eventTypeObject, workingHours, trpcState: ssr.dehydrate(), - previousPage: context.req.headers.referer ?? null, booking, }, }; +};*/ + +export const getStaticProps = async () => { + return { + props: { + profile: { + name: "Pro Example", + username: "pro", + hideBranding: false, + plan: "PRO", + timeZone: "Europe/London", + image: "http://localhost:3000/pro/avatar.png", + }, + away: false, + eventTypeId: 1, + }, + }; +}; + +export const getStaticPaths = async () => { + const paths = ["/pro/30min"]; + + return { paths, fallback: "blocking" }; }; diff --git a/apps/web/server/routers/viewer.tsx b/apps/web/server/routers/viewer.tsx index 3c70706a92dcb6..6107d9e1bb2f18 100644 --- a/apps/web/server/routers/viewer.tsx +++ b/apps/web/server/routers/viewer.tsx @@ -31,6 +31,7 @@ import { apiKeysRouter } from "@server/routers/viewer/apiKeys"; import { availabilityRouter } from "@server/routers/viewer/availability"; import { bookingsRouter } from "@server/routers/viewer/bookings"; import { eventTypesRouter } from "@server/routers/viewer/eventTypes"; +import { slotsRouter } from "@server/routers/viewer/slots"; import { TRPCError } from "@trpc/server"; import { createProtectedRouter, createRouter } from "../createRouter"; @@ -945,4 +946,5 @@ export const viewerRouter = createRouter() .merge("availability.", availabilityRouter) .merge("teams.", viewerTeamsRouter) .merge("webhook.", webhookRouter) - .merge("apiKeys.", apiKeysRouter); + .merge("apiKeys.", apiKeysRouter) + .merge("slots.", slotsRouter); diff --git a/apps/web/server/routers/viewer/slots.tsx b/apps/web/server/routers/viewer/slots.tsx new file mode 100644 index 00000000000000..214731108c0323 --- /dev/null +++ b/apps/web/server/routers/viewer/slots.tsx @@ -0,0 +1,160 @@ +import dayjs from "dayjs"; +import { z } from "zod"; + +import { getWorkingHours } from "@calcom/lib/availability"; +import { yyyymmdd } from "@calcom/lib/date-fns"; + +import getBusyTimes from "@lib/getBusyTimes"; +import { getFilteredTimes } from "@lib/hooks/useSlots"; +import getSlots from "@lib/slots"; + +import { createRouter } from "@server/createRouter"; +import { TRPCError } from "@trpc/server"; + +const getScheduleSchema = z.object({ + // startTime ISOString + startTime: z.string(), + // endTime ISOString + endTime: z.string(), + // Event type ID + eventTypeId: z.number(), + // Additional organizers for dynamic events. + additionalOrganizers: z.array(z.string()).optional(), +}); + +export const slotsRouter = createRouter().query("getSchedule", { + input: getScheduleSchema, + async resolve({ input, ctx }) { + const eventType = await ctx.prisma.eventType.findUnique({ + where: { + id: input.eventTypeId, + }, + select: { + minimumBookingNotice: true, + length: true, + seatsPerTimeSlot: true, + timeZone: true, + slotInterval: true, + schedule: { + select: { + availability: true, + timeZone: true, + }, + }, + availability: { + select: { + startTime: true, + endTime: true, + days: true, + }, + }, + users: { + select: { + id: true, + bufferTime: true, + schedules: { + select: { + id: true, + availability: true, + timeZone: true, + }, + }, + selectedCalendars: true, + credentials: true, + defaultScheduleId: true, + availability: true, + timeZone: true, + }, + }, + }, + }); + + if (!eventType) { + throw new TRPCError({ code: "NOT_FOUND" }); + } + + const currentUser = eventType.users[0]; + + const startTime = dayjs(input.startTime); + const endTime = dayjs(input.endTime); + if (!startTime.isValid() || !endTime.isValid()) { + throw new TRPCError({ message: "Invalid time range given.", code: "BAD_REQUEST" }); + } + + const busyTimes = await getBusyTimes({ + credentials: currentUser.credentials, + startTime: startTime.format(), + endTime: endTime.format(), + eventTypeId: input.eventTypeId, + userId: currentUser.id, + selectedCalendars: currentUser.selectedCalendars, + }); + + const bufferedBusyTimes = busyTimes.map((a) => ({ + start: dayjs(a.start).subtract(currentUser.bufferTime, "minute"), + end: dayjs(a.end).add(currentUser.bufferTime, "minute"), + })); + + console.log(bufferedBusyTimes); + + const schedule = eventType?.schedule + ? { ...eventType?.schedule } + : { + ...currentUser.schedules.filter( + (schedule) => !currentUser.defaultScheduleId || schedule.id === currentUser.defaultScheduleId + )[0], + }; + + const timeZone = schedule.timeZone || eventType?.timeZone || currentUser.timeZone; + + const workingHours = getWorkingHours( + { + timeZone, + }, + schedule.availability || + (eventType?.availability.length ? eventType.availability : currentUser.availability) + ); + + /* Current logic is if a booking is in a time slot mark it as busy, but seats can have more than one attendee so grab + current bookings with a seats event type and display them on the calendar, even if they are full */ + let currentSeats; + if (eventType?.seatsPerTimeSlot) { + currentSeats = await ctx.prisma.booking.findMany({ + where: { + eventTypeId: input.eventTypeId, + startTime: { + gte: startTime.format(), + lte: endTime.format(), + }, + }, + select: { + uid: true, + startTime: true, + _count: { + select: { + attendees: true, + }, + }, + }, + }); + } + + const slots: Record = {}; + + let time = dayjs(startTime); + do { + slots[yyyymmdd(time.toDate())] = getSlots({ + inviteeDate: time, + eventLength: eventType.length, + workingHours, + minimumBookingNotice: eventType.minimumBookingNotice, + frequency: eventType.slotInterval || eventType.length, + }); + time = time.add(1, "day"); + } while (time.isBefore(endTime)); + + return { + slots, + }; + }, +}); diff --git a/packages/lib/defaultEvents.ts b/packages/lib/defaultEvents.ts index e2afe80f0ac183..41e813bf25e39c 100644 --- a/packages/lib/defaultEvents.ts +++ b/packages/lib/defaultEvents.ts @@ -143,29 +143,23 @@ export const getUsernameSlugLink = ({ users, slug }: UsernameSlugLinkProps): str return slugLink; }; +const arrayCast = (value: unknown | unknown[]) => { + return Array.isArray(value) ? value : value ? [value] : []; +}; + export const getUsernameList = (users: string | string[] | undefined): string[] => { - if (!users) { - return []; - } - if (!(users instanceof Array)) { - users = [users]; - } - const allUsers: string[] = []; // Multiple users can come in case of a team round-robin booking and in that case dynamic link won't be a user. // So, even though this code handles even if individual user is dynamic link, that isn't a possibility right now. - users.forEach((user) => { - allUsers.push( - ...user - ?.toLowerCase() - .replace(/ /g, "+") - .replace(/%20/g, "+") - .split("+") - .filter((el) => { - return el.length != 0; - }) - ); - }); - return allUsers; + users = arrayCast(users); + + const allUsers = users.map((user) => + user + .toLowerCase() + .replace(/( |%20)/g, "+") + .split("+") + ); + + return Array.prototype.concat(...allUsers); }; export default defaultEvents; diff --git a/packages/lib/hooks/useSlots.ts b/packages/lib/hooks/useSlots.ts new file mode 100644 index 00000000000000..0abce069c54832 --- /dev/null +++ b/packages/lib/hooks/useSlots.ts @@ -0,0 +1,8 @@ +type UseSlotsProps = { + boundaries: { + startTime: Date; + endTime: Date; + }[]; +}; + +export const useSlots = ({ boundaries }: UseSlotsProps) => {}; diff --git a/packages/ui/booker/DatePicker.tsx b/packages/ui/booker/DatePicker.tsx index f0414dfe4dd8c0..ec18d53fc1100c 100644 --- a/packages/ui/booker/DatePicker.tsx +++ b/packages/ui/booker/DatePicker.tsx @@ -11,14 +11,14 @@ export type DatePickerProps = { // Fires whenever a selected date is changed. onChange: (date: Date) => void; // which date is currently selected (not tracked from here) - selected?: Date; + date?: Date; // defaults to current date. minDate?: Date; // Furthest date selectable in the future, default = UNLIMITED maxDate?: Date; // locale, any IETF language tag, e.g. "hu-HU" - defaults to Browser settings locale: string; - // Defaults to [], which dates are not bookable. Array of strings like: ["2022-04-23", "2022-04-24"] + // Defaults to [], which dates are not bookable. Array of valid dates like: ["2022-04-23", "2022-04-24"] excludedDates?: string[]; // allows adding classes to the container className?: string; @@ -51,14 +51,14 @@ const Days = ({ excludedDates = [], browsingDate, weekStart, - selected, + date, ...props }: Omit & { browsingDate: Date; weekStart: number; }) => { // Create placeholder elements for empty days in first week - let weekdayOfFirst = new Date(new Date(browsingDate).setDate(1)).getDay(); + const weekdayOfFirst = new Date(new Date(browsingDate).setDate(1)).getDay(); const days: (Date | null)[] = Array((weekdayOfFirst - weekStart + 7) % 7).fill(null); for (let day = 1, dayCount = daysInMonth(browsingDate); day <= dayCount; day++) { @@ -81,7 +81,7 @@ const Days = ({ props.onChange(day)} disabled={excludedDates.includes(yyyymmdd(day)) || day < minDate} - checked={selected ? yyyymmdd(selected) === yyyymmdd(day) : false}> + checked={date ? yyyymmdd(date) === yyyymmdd(day) : false}> {day.getDate()} )} @@ -91,15 +91,8 @@ const Days = ({ ); }; -const DatePicker = ({ - weekStart = 0, - className, - excludedDates = [], - locale, - selected, - ...passThroughProps -}: DatePickerProps) => { - const [month, setMonth] = useState(selected ? selected.getMonth() : new Date().getMonth()); +const DatePicker = ({ weekStart = 0, className, locale, date, ...passThroughProps }: DatePickerProps) => { + const [month, setMonth] = useState(date ? date.getMonth() : new Date().getMonth()); return (
diff --git a/yarn.lock b/yarn.lock index 354358e858606a..444232ad9061b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,38 +9,6 @@ dependencies: "@jridgewell/trace-mapping" "^0.3.0" -"@apidevtools/json-schema-ref-parser@^9.0.6": - version "9.0.9" - resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#d720f9256e3609621280584f2b47ae165359268b" - integrity sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w== - dependencies: - "@jsdevtools/ono" "^7.1.3" - "@types/json-schema" "^7.0.6" - call-me-maybe "^1.0.1" - js-yaml "^4.1.0" - -"@apidevtools/openapi-schemas@^2.0.4": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz#9fa08017fb59d80538812f03fc7cac5992caaa17" - integrity sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ== - -"@apidevtools/swagger-methods@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz#b789a362e055b0340d04712eafe7027ddc1ac267" - integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg== - -"@apidevtools/swagger-parser@10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@apidevtools/swagger-parser/-/swagger-parser-10.0.2.tgz#f4145afb7c3a3bafe0376f003b5c3bdeae17a952" - integrity sha512-JFxcEyp8RlNHgBCE98nwuTkZT6eNFPc1aosWV6wPcQph72TSEEu1k3baJD4/x1qznU+JiDdz8F5pTwabZh+Dhg== - dependencies: - "@apidevtools/json-schema-ref-parser" "^9.0.6" - "@apidevtools/openapi-schemas" "^2.0.4" - "@apidevtools/swagger-methods" "^3.0.2" - "@jsdevtools/ono" "^7.1.3" - call-me-maybe "^1.0.1" - z-schema "^4.2.3" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" @@ -101,27 +69,6 @@ json5 "^2.1.2" semver "^6.3.0" -"@babel/core@^7.11.6": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.2.tgz#87b2fcd7cce9becaa7f5acebdc4f09f3dd19d876" - integrity sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.18.2" - "@babel/helper-compilation-targets" "^7.18.2" - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helpers" "^7.18.2" - "@babel/parser" "^7.18.0" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.2" - "@babel/types" "^7.18.2" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" - "@babel/core@^7.17.10": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.10.tgz#74ef0fbf56b7dfc3f198fc2d927f4f03e12f4b05" @@ -179,15 +126,6 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/generator@^7.18.2", "@babel/generator@^7.7.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d" - integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw== - dependencies: - "@babel/types" "^7.18.2" - "@jridgewell/gen-mapping" "^0.3.0" - jsesc "^2.5.1" - "@babel/helper-annotate-as-pure@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" @@ -215,16 +153,6 @@ browserslist "^4.20.2" semver "^6.3.0" -"@babel/helper-compilation-targets@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b" - integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.20.2" - semver "^6.3.0" - "@babel/helper-environment-visitor@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" @@ -232,11 +160,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-environment-visitor@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd" - integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ== - "@babel/helper-function-name@^7.12.13", "@babel/helper-function-name@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" @@ -289,30 +212,11 @@ "@babel/traverse" "^7.17.3" "@babel/types" "^7.17.0" -"@babel/helper-module-transforms@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd" - integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA== - dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.0" - "@babel/types" "^7.18.0" - "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== -"@babel/helper-plugin-utils@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" - integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== - "@babel/helper-simple-access@^7.17.7": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" @@ -355,15 +259,6 @@ "@babel/traverse" "^7.17.9" "@babel/types" "^7.17.0" -"@babel/helpers@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384" - integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg== - dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.2" - "@babel/types" "^7.18.2" - "@babel/highlight@^7.16.7": version "7.16.10" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" @@ -393,11 +288,6 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== -"@babel/parser@^7.18.0": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.4.tgz#6774231779dd700e0af29f6ad8d479582d7ce5ef" - integrity sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow== - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -489,13 +379,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.7.2": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz#b54fc3be6de734a56b87508f99d6428b5b605a7b" - integrity sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-transform-react-jsx-development@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8" @@ -630,22 +513,6 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.7.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.2.tgz#b77a52604b5cc836a9e1e08dca01cba67a12d2e8" - integrity sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.18.2" - "@babel/helper-environment-visitor" "^7.18.2" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.18.0" - "@babel/types" "^7.18.2" - debug "^4.1.0" - globals "^11.1.0" - "@babel/types@7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" @@ -671,14 +538,6 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" -"@babel/types@^7.18.0", "@babel/types@^7.18.2": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354" - integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - to-fast-properties "^2.0.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -876,21 +735,6 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/eslintrc@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" - integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.3.2" - globals "^13.15.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - "@ethereumjs/common@^2.5.0", "@ethereumjs/common@^2.6.3": version "2.6.3" resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.3.tgz#39ddece7300b336276bad6c02f6a9f1a082caa05" @@ -1153,27 +997,12 @@ intl-messageformat "9.13.0" tslib "^2.1.0" -"@fullstory/browser@^1.5.1": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@fullstory/browser/-/browser-1.5.1.tgz#4b6ce95df3a91408a8b56bac06038f4d128f9ec8" - integrity sha512-eOTR0lc2BwwyGYn7ke7aJrJpl8Ae4+KdWyiibixVn+RUG3NaQgrP7hj/KmlMnndRSO9I4YqoqZCKYwtqDFo3rw== - "@glidejs/glide@^3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@glidejs/glide/-/glide-3.5.2.tgz#7012c5920ecf202bbda44d8526fc979984b6dd54" integrity sha512-7jGciNJ2bQ4eZLSNlSZ+VAyW63kALf420CvkEpK4lEsUfWJq9odqimci0YCiyNyMUFB+pWHwLYyNc57dijYsCg== -"@headlessui/react@^1.4.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.6.1.tgz#d822792e589aac005462491dd62f86095e0c3bef" - integrity sha512-gMd6uIs1U4Oz718Z5gFoV0o/vD43/4zvbyiJN9Dt7PK9Ubxn+TmJwTmYwyNJc5KxxU1t0CmgTNgwZX9+4NjCnQ== - -"@headlessui/react@^1.5.0": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.6.3.tgz#6e52477ea3aee7d99f153b98c1b41765ed77db3d" - integrity sha512-WNu/ypGzl0JmJ+sD34KtdycEu2n7EZjKFx2rq6fivsszPdoEyOVZ/GYQMJ437dfAJI0/ZxoRYfrOVduZHjlokQ== - -"@heroicons/react@^1.0.4", "@heroicons/react@^1.0.6": +"@heroicons/react@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-1.0.6.tgz#35dd26987228b39ef2316db3b1245c42eb19e324" integrity sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ== @@ -1183,11 +1012,6 @@ resolved "https://registry.yarnpkg.com/@hookform/error-message/-/error-message-2.0.0.tgz#9b1b037fd816ea9b1531c06aa7fab5f5154aa740" integrity sha512-Y90nHzjgL2MP7GFy75kscdvxrCTjtyxGmOLLxX14nd08OXRIh9lMH/y9Kpdo0p1IPowJBiZMHyueg7p+yrqynQ== -"@hookform/resolvers@^2.8.1": - version "2.8.10" - resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.8.10.tgz#b66d7a7848b1b1dd5b976a73fff36bb366666e7d" - integrity sha512-DDFtNlugsbwAhCJHYp3NcN5LvJrwSsCLPi41Wo5O8UAIbUFnBfY/jW+zKnlX57BZ4jE0j/g6R9rB3JlO89ad0g== - "@hookform/resolvers@^2.8.9": version "2.8.9" resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.8.9.tgz#0177a6b2b5b0dfa7860625f9a1b71803d467e78a" @@ -1249,18 +1073,6 @@ jest-util "^26.6.2" slash "^3.0.0" -"@jest/console@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.0.tgz#db78222c3d3b0c1db82f1b9de51094c2aaff2176" - integrity sha512-tscn3dlJFGay47kb4qVruQg/XWlmvU0xp3EJOjzzY+sBaI+YgwKcvAmTcyYU7xEiLLIY5HCdWRooAL8dqkFlDA== - dependencies: - "@jest/types" "^28.1.0" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^28.1.0" - jest-util "^28.1.0" - slash "^3.0.0" - "@jest/core@^26.6.3": version "26.6.3" resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" @@ -1295,41 +1107,6 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/core@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.0.tgz#784a1e6ce5358b46fcbdcfbbd93b1b713ed4ea80" - integrity sha512-/2PTt0ywhjZ4NwNO4bUqD9IVJfmFVhVKGlhvSpmEfUCuxYf/3NHcKmRFI+I71lYzbTT3wMuYpETDCTHo81gC/g== - dependencies: - "@jest/console" "^28.1.0" - "@jest/reporters" "^28.1.0" - "@jest/test-result" "^28.1.0" - "@jest/transform" "^28.1.0" - "@jest/types" "^28.1.0" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^28.0.2" - jest-config "^28.1.0" - jest-haste-map "^28.1.0" - jest-message-util "^28.1.0" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.0" - jest-resolve-dependencies "^28.1.0" - jest-runner "^28.1.0" - jest-runtime "^28.1.0" - jest-snapshot "^28.1.0" - jest-util "^28.1.0" - jest-validate "^28.1.0" - jest-watcher "^28.1.0" - micromatch "^4.0.4" - pretty-format "^28.1.0" - rimraf "^3.0.0" - slash "^3.0.0" - strip-ansi "^6.0.0" - "@jest/environment@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" @@ -1340,31 +1117,6 @@ "@types/node" "*" jest-mock "^26.6.2" -"@jest/environment@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.0.tgz#dedf7d59ec341b9292fcf459fd0ed819eb2e228a" - integrity sha512-S44WGSxkRngzHslhV6RoAExekfF7Qhwa6R5+IYFa81mpcj0YgdBnRSmvHe3SNwOt64yXaE5GG8Y2xM28ii5ssA== - dependencies: - "@jest/fake-timers" "^28.1.0" - "@jest/types" "^28.1.0" - "@types/node" "*" - jest-mock "^28.1.0" - -"@jest/expect-utils@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.0.tgz#a5cde811195515a9809b96748ae8bcc331a3538a" - integrity sha512-5BrG48dpC0sB80wpeIX5FU6kolDJI4K0n5BM9a5V38MGx0pyRvUBSS0u2aNTdDzmOrCjhOg8pGs6a20ivYkdmw== - dependencies: - jest-get-type "^28.0.2" - -"@jest/expect@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.0.tgz#2e5a31db692597070932366a1602b5157f0f217c" - integrity sha512-be9ETznPLaHOmeJqzYNIXv1ADEzENuQonIoobzThOYPuK/6GhrWNIJDVTgBLCrz3Am73PyEU2urQClZp0hLTtA== - dependencies: - expect "^28.1.0" - jest-snapshot "^28.1.0" - "@jest/fake-timers@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" @@ -1377,18 +1129,6 @@ jest-mock "^26.6.2" jest-util "^26.6.2" -"@jest/fake-timers@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.0.tgz#ea77878aabd5c5d50e1fc53e76d3226101e33064" - integrity sha512-Xqsf/6VLeAAq78+GNPzI7FZQRf5cCHj1qgQxCjws9n8rKw8r1UYoeaALwBvyuzOkpU3c1I6emeMySPa96rxtIg== - dependencies: - "@jest/types" "^28.1.0" - "@sinonjs/fake-timers" "^9.1.1" - "@types/node" "*" - jest-message-util "^28.1.0" - jest-mock "^28.1.0" - jest-util "^28.1.0" - "@jest/globals@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" @@ -1398,15 +1138,6 @@ "@jest/types" "^26.6.2" expect "^26.6.2" -"@jest/globals@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.0.tgz#a4427d2eb11763002ff58e24de56b84ba79eb793" - integrity sha512-3m7sTg52OTQR6dPhsEQSxAvU+LOBbMivZBwOvKEZ+Rb+GyxVnXi9HKgOTYkx/S99T8yvh17U4tNNJPIEQmtwYw== - dependencies: - "@jest/environment" "^28.1.0" - "@jest/expect" "^28.1.0" - "@jest/types" "^28.1.0" - "@jest/reporters@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" @@ -1439,43 +1170,6 @@ optionalDependencies: node-notifier "^8.0.0" -"@jest/reporters@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.0.tgz#5183a28b9b593b6000fa9b89b031c7216b58a9a0" - integrity sha512-qxbFfqap/5QlSpIizH9c/bFCDKsQlM4uAKSOvZrP+nIdrjqre3FmKzpTtYyhsaVcOSNK7TTt2kjm+4BJIjysFA== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^28.1.0" - "@jest/test-result" "^28.1.0" - "@jest/transform" "^28.1.0" - "@jest/types" "^28.1.0" - "@jridgewell/trace-mapping" "^0.3.7" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-util "^28.1.0" - jest-worker "^28.1.0" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - terminal-link "^2.0.0" - v8-to-istanbul "^9.0.0" - -"@jest/schemas@^28.0.2": - version "28.0.2" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.0.2.tgz#08c30df6a8d07eafea0aef9fb222c5e26d72e613" - integrity sha512-YVDJZjd4izeTDkij00vHHAymNXQ6WWsdChFRK86qck6Jpr3DCL5W3Is3vslviRlP+bLuMYRLbdp98amMvqudhA== - dependencies: - "@sinclair/typebox" "^0.23.3" - "@jest/source-map@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" @@ -1485,15 +1179,6 @@ graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/source-map@^28.0.2": - version "28.0.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.0.2.tgz#914546f4410b67b1d42c262a1da7e0406b52dc90" - integrity sha512-Y9dxC8ZpN3kImkk0LkK5XCEneYMAXlZ8m5bflmSL5vrwyeUpJfentacCUg6fOb8NOpOO7hz2+l37MV77T6BFPw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.7" - callsites "^3.0.0" - graceful-fs "^4.2.9" - "@jest/test-result@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" @@ -1504,16 +1189,6 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-result@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.0.tgz#fd149dee123510dd2fcadbbf5f0020f98ad7f12c" - integrity sha512-sBBFIyoPzrZho3N+80P35A5oAkSKlGfsEFfXFWuPGBsW40UAjCkGakZhn4UQK4iQlW2vgCDMRDOob9FGKV8YoQ== - dependencies: - "@jest/console" "^28.1.0" - "@jest/types" "^28.1.0" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - "@jest/test-sequencer@^26.6.3": version "26.6.3" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" @@ -1525,16 +1200,6 @@ jest-runner "^26.6.3" jest-runtime "^26.6.3" -"@jest/test-sequencer@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.0.tgz#ce7294bbe986415b9a30e218c7e705e6ebf2cdf2" - integrity sha512-tZCEiVWlWNTs/2iK9yi6o3AlMfbbYgV4uuZInSVdzZ7ftpHZhCMuhvk2HLYhCZzLgPFQ9MnM1YaxMnh3TILFiQ== - dependencies: - "@jest/test-result" "^28.1.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.0" - slash "^3.0.0" - "@jest/transform@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" @@ -1577,27 +1242,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.0.tgz#224a3c9ba4cc98e2ff996c0a89a2d59db15c74ce" - integrity sha512-omy2xe5WxlAfqmsTjTPxw+iXRTRnf+NtX0ToG+4S0tABeb4KsKmPUHq5UBuwunHg3tJRwgEQhEp0M/8oiatLEA== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^28.1.0" - "@jridgewell/trace-mapping" "^0.3.7" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.0" - jest-regex-util "^28.0.2" - jest-util "^28.1.0" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.1" - "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -1620,18 +1264,6 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^28.1.0": - version "28.1.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.0.tgz#508327a89976cbf9bd3e1cc74641a29fd7dfd519" - integrity sha512-xmEggMPr317MIOjjDoZ4ejCSr9Lpbt/u34+dvc99t7DS8YirW5rwZEhzKPC2BMUFkUhI48qs6qLUSGw5FuL0GA== - dependencies: - "@jest/schemas" "^28.0.2" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - "@jimp/bmp@^0.16.1": version "0.16.1" resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.16.1.tgz#6e2da655b2ba22e721df0795423f34e92ef13768" @@ -1930,15 +1562,6 @@ "@jridgewell/set-array" "^1.0.0" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/gen-mapping@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9" - integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - "@jridgewell/resolve-uri@^3.0.3": version "3.0.5" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" @@ -1962,19 +1585,6 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" - integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jsdevtools/ono@^7.1.3": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" - integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== - "@mdx-js/loader@^2.0.0-next.9": version "2.1.1" resolved "https://registry.yarnpkg.com/@mdx-js/loader/-/loader-2.1.1.tgz#98f0f2d8fc7f6b2f2c51ea2f4185e9c1ebcb187a" @@ -2682,7 +2292,7 @@ "@radix-ui/react-use-callback-ref" "0.1.0" "@radix-ui/react-use-controllable-state" "0.1.0" -"@radix-ui/react-slider@^0.1.0", "@radix-ui/react-slider@^0.1.1": +"@radix-ui/react-slider@^0.1.1": version "0.1.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-slider/-/react-slider-0.1.4.tgz#a7b7a480ee00158195794b08cd3f1583cf102518" integrity sha512-0z3bCcdrAi+FIcoLXS6r0ESVWuuyMnUJoCsFm7tC7Rtv95x34YtaI8YfSyQmzuMVS4rTsNtCCTZ/s727uRaVkQ== @@ -2856,145 +2466,6 @@ resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.3.tgz#6801033be7ff87a6b7cadaf5b337c9f366a3c4b0" integrity sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw== -"@sentry/browser@6.19.7": - version "6.19.7" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.19.7.tgz#a40b6b72d911b5f1ed70ed3b4e7d4d4e625c0b5f" - integrity sha512-oDbklp4O3MtAM4mtuwyZLrgO1qDVYIujzNJQzXmi9YzymJCuzMLSRDvhY83NNDCRxf0pds4DShgYeZdbSyKraA== - dependencies: - "@sentry/core" "6.19.7" - "@sentry/types" "6.19.7" - "@sentry/utils" "6.19.7" - tslib "^1.9.3" - -"@sentry/cli@^1.73.0": - version "1.74.4" - resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.4.tgz#7df82f68045a155e1885bfcbb5d303e5259eb18e" - integrity sha512-BMfzYiedbModsNBJlKeBOLVYUtwSi99LJ8gxxE4Bp5N8hyjNIN0WVrozAVZ27mqzAuy6151Za3dpmOLO86YlGw== - dependencies: - https-proxy-agent "^5.0.0" - mkdirp "^0.5.5" - node-fetch "^2.6.7" - npmlog "^4.1.2" - progress "^2.0.3" - proxy-from-env "^1.1.0" - which "^2.0.2" - -"@sentry/core@6.19.7": - version "6.19.7" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.19.7.tgz#156aaa56dd7fad8c89c145be6ad7a4f7209f9785" - integrity sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw== - dependencies: - "@sentry/hub" "6.19.7" - "@sentry/minimal" "6.19.7" - "@sentry/types" "6.19.7" - "@sentry/utils" "6.19.7" - tslib "^1.9.3" - -"@sentry/hub@6.19.7": - version "6.19.7" - resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.19.7.tgz#58ad7776bbd31e9596a8ec46365b45cd8b9cfd11" - integrity sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA== - dependencies: - "@sentry/types" "6.19.7" - "@sentry/utils" "6.19.7" - tslib "^1.9.3" - -"@sentry/integrations@6.19.7": - version "6.19.7" - resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-6.19.7.tgz#e6e126b692077c8731644224c754012bed65b425" - integrity sha512-yNeeFyuygJaV7Mdc5qWuDa13xVj5mVdECaaw2Xs4pfeHaXmRfRzZY17N8ypWFegKWxKBHynyQRMD10W5pBwJvA== - dependencies: - "@sentry/types" "6.19.7" - "@sentry/utils" "6.19.7" - localforage "^1.8.1" - tslib "^1.9.3" - -"@sentry/minimal@6.19.7": - version "6.19.7" - resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.19.7.tgz#b3ee46d6abef9ef3dd4837ebcb6bdfd01b9aa7b4" - integrity sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ== - dependencies: - "@sentry/hub" "6.19.7" - "@sentry/types" "6.19.7" - tslib "^1.9.3" - -"@sentry/nextjs@^6.19.7": - version "6.19.7" - resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-6.19.7.tgz#2c40692d89a99ec1382189f11702b1498c91fb77" - integrity sha512-029gpqhR6gHF7zfE9oxFOf3Zm68CShDu8/6azC8mwfIfJtyLC9dqztJJi48j0Uxs+sR1TEkN5Dw3wZbfWtFd8g== - dependencies: - "@sentry/core" "6.19.7" - "@sentry/hub" "6.19.7" - "@sentry/integrations" "6.19.7" - "@sentry/node" "6.19.7" - "@sentry/react" "6.19.7" - "@sentry/tracing" "6.19.7" - "@sentry/utils" "6.19.7" - "@sentry/webpack-plugin" "1.18.8" - tslib "^1.9.3" - -"@sentry/node@6.19.7": - version "6.19.7" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.19.7.tgz#32963b36b48daebbd559e6f13b1deb2415448592" - integrity sha512-gtmRC4dAXKODMpHXKfrkfvyBL3cI8y64vEi3fDD046uqYcrWdgoQsffuBbxMAizc6Ez1ia+f0Flue6p15Qaltg== - dependencies: - "@sentry/core" "6.19.7" - "@sentry/hub" "6.19.7" - "@sentry/types" "6.19.7" - "@sentry/utils" "6.19.7" - cookie "^0.4.1" - https-proxy-agent "^5.0.0" - lru_map "^0.3.3" - tslib "^1.9.3" - -"@sentry/react@6.19.7": - version "6.19.7" - resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.19.7.tgz#58cc2d6da20f7d3b0df40638dfbbbc86c9c85caf" - integrity sha512-VzJeBg/v41jfxUYPkH2WYrKjWc4YiMLzDX0f4Zf6WkJ4v3IlDDSkX6DfmWekjTKBho6wiMkSNy2hJ1dHfGZ9jA== - dependencies: - "@sentry/browser" "6.19.7" - "@sentry/minimal" "6.19.7" - "@sentry/types" "6.19.7" - "@sentry/utils" "6.19.7" - hoist-non-react-statics "^3.3.2" - tslib "^1.9.3" - -"@sentry/tracing@6.19.7": - version "6.19.7" - resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.19.7.tgz#54bb99ed5705931cd33caf71da347af769f02a4c" - integrity sha512-ol4TupNnv9Zd+bZei7B6Ygnr9N3Gp1PUrNI761QSlHtPC25xXC5ssSD3GMhBgyQrcvpuRcCFHVNNM97tN5cZiA== - dependencies: - "@sentry/hub" "6.19.7" - "@sentry/minimal" "6.19.7" - "@sentry/types" "6.19.7" - "@sentry/utils" "6.19.7" - tslib "^1.9.3" - -"@sentry/types@6.19.7": - version "6.19.7" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.7.tgz#c6b337912e588083fc2896eb012526cf7cfec7c7" - integrity sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg== - -"@sentry/utils@6.19.7": - version "6.19.7" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.19.7.tgz#6edd739f8185fd71afe49cbe351c1bbf5e7b7c79" - integrity sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA== - dependencies: - "@sentry/types" "6.19.7" - tslib "^1.9.3" - -"@sentry/webpack-plugin@1.18.8": - version "1.18.8" - resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.18.8.tgz#247a73a0aa9e28099a736bbe89ca0d35cbac7636" - integrity sha512-PtKr0NL62b5L3kPFGjwSNbIUwwcW5E5G6bQxAYZGpkgL1MFPnS4ND0SAsySuX0byQJRFFium5A19LpzyvQZSlQ== - dependencies: - "@sentry/cli" "^1.73.0" - -"@sinclair/typebox@^0.23.3": - version "0.23.5" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.5.tgz#93f7b9f4e3285a7a9ade7557d9a8d36809cbc47d" - integrity sha512-AFBVi/iT4g20DHoujvMH1aEDn8fGJh4xsRGCP6d8RpLPMqsNPvW01Jcn0QysXTsg++/xj25NmJsGyH9xug/wKg== - "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -3019,13 +2490,6 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@sinonjs/fake-timers@^9.1.1": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== - dependencies: - "@sinonjs/commons" "^1.7.0" - "@slack/logger@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-3.0.0.tgz#b736d4e1c112c22a10ffab0c2d364620aedcb714" @@ -3082,11 +2546,6 @@ dependencies: prop-types "^15.7.2" -"@stripe/stripe-js@^1.17.1": - version "1.31.0" - resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.31.0.tgz#2920bdddc3eabb5734f9ca794824e25b378533ac" - integrity sha512-drlXsNvd/s9S1+Ghja0aDB81N3Wr/6iRAZ7MOzKv7AtKxVdvGpOwESw3z1lHl/Wx/rvPpA7whKdB3W2CnVSArw== - "@stripe/stripe-js@^1.29.0": version "1.29.0" resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.29.0.tgz#f41e46aee711d1eabcb3bbc77376016a250ec962" @@ -3099,13 +2558,6 @@ dependencies: defer-to-connect "^1.0.1" -"@tailwindcss/forms@^0.5.0": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.5.2.tgz#4ef45f9916dcb37838cbe7fecdcc4ba7a7c2ab59" - integrity sha512-pSrFeJB6Bg1Mrg9CdQW3+hqZXAKsBrSG9MAfFLKy1pVA4Mb4W7C0k7mEhlmS2Dfo/otxrQOET7NJiJ9RrS563w== - dependencies: - mini-svg-data-uri "^1.2.3" - "@tailwindcss/forms@^0.5.1": version "0.5.1" resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.5.1.tgz#7fe86b9b67e6d91cb902e2d3f4ebe561cc057a13" @@ -3213,19 +2665,6 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== -"@typeform/embed-react@^1.2.4": - version "1.16.0" - resolved "https://registry.yarnpkg.com/@typeform/embed-react/-/embed-react-1.16.0.tgz#013f510927525d023fa27b466cd66fbd9f0b42d2" - integrity sha512-MLIPXZ7uxvoyLc5RMK0Tvmf93DoqQXbpu3DleXyRk7ilrYx+TfXUqOtJfaqBK4qR2HpslhSckgvtHPfv0zMGlQ== - dependencies: - "@typeform/embed" "1.35.1" - fast-deep-equal "^3.1.3" - -"@typeform/embed@1.35.1": - version "1.35.1" - resolved "https://registry.yarnpkg.com/@typeform/embed/-/embed-1.35.1.tgz#96e0c84e0af385f90411b2f75277ca7ae0d50fa1" - integrity sha512-dDLewxUKwfCfU/nMaVpwQ3nOF1nMhey3wtALAQRgS9fe6XRbZH6YkmtqvTsxTjilOnswJqghGFJ3GfaZh87n/A== - "@types/accept-language-parser@1.5.2": version "1.5.2" resolved "https://registry.yarnpkg.com/@types/accept-language-parser/-/accept-language-parser-1.5.2.tgz#ea48ed07a3dc9d2ba6666d45c018ad1b5e59d665" @@ -3417,7 +2856,7 @@ resolved "https://registry.yarnpkg.com/@types/glidejs__glide/-/glidejs__glide-3.4.2.tgz#fb486e376aab88788e09bbdeb81f89dcf363be1c" integrity sha512-RM095h5dzR4645Z2moR1kFEEyr26BteRySLsAsUXWvI1Nxh4z2SU3qhNx68uYutvLbRIHCIve40ItylC8r+kEw== -"@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3": +"@types/graceful-fs@^4.1.2": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== @@ -3483,7 +2922,7 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138" integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA== -"@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.7", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== @@ -3533,13 +2972,6 @@ "@types/node" "*" "@types/socket.io" "2.1.13" -"@types/micro@7.3.7": - version "7.3.7" - resolved "https://registry.yarnpkg.com/@types/micro/-/micro-7.3.7.tgz#84bef63ef8cc113a70b9a64345ebea2d99946647" - integrity sha512-MFsX7eCj0Tg3TtphOQvANNvNtFpya+s/rYOCdV6o+DFjOQPFi2EVRbBALjbbgZTXUaJP1Q281MJiJOD40d0UxQ== - dependencies: - "@types/node" "*" - "@types/mime-types@^2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.1.tgz#d9ba43490fa3a3df958759adf69396c3532cf2c1" @@ -3599,11 +3031,6 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== -"@types/prettier@^2.1.5": - version "2.6.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a" - integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg== - "@types/prop-types@*": version "15.7.5" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" @@ -3792,13 +3219,6 @@ dependencies: "@types/yargs-parser" "*" -"@types/yargs@^17.0.8": - version "17.0.10" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a" - integrity sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA== - dependencies: - "@types/yargs-parser" "*" - "@types/yoga-layout@1.9.2": version "1.9.2" resolved "https://registry.yarnpkg.com/@types/yoga-layout/-/yoga-layout-1.9.2.tgz#efaf9e991a7390dc081a0b679185979a83a9639a" @@ -3994,7 +3414,7 @@ accept-language-parser@^1.5.0: resolved "https://registry.yarnpkg.com/accept-language-parser/-/accept-language-parser-1.5.0.tgz#8877c54040a8dcb59e0a07d9c1fde42298334791" integrity sha1-iHfFQECo3LWeCgfZwf3kIpgzR5E= -accepts@^1.3.7, accepts@~1.3.7, accepts@~1.3.8: +accepts@~1.3.7, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -4156,11 +3576,6 @@ app-root-path@^3.0.0: resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - arch@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" @@ -4173,14 +3588,6 @@ archive-type@^4.0.0: dependencies: file-type "^4.2.0" -are-we-there-yet@~1.1.2: - version "1.1.7" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" - integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - arg@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/arg/-/arg-1.0.0.tgz#444d885a4e25b121640b55155ef7cd03975d6050" @@ -4417,7 +3824,7 @@ autolinker@^3.11.0: dependencies: tslib "^2.3.0" -autoprefixer@^10.3.4, autoprefixer@^10.4.2, autoprefixer@^10.4.7: +autoprefixer@^10.4.7: version "10.4.7" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.7.tgz#1db8d195f41a52ca5069b7593be167618edbbedf" integrity sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA== @@ -4511,19 +3918,6 @@ babel-jest@^27.3.1: graceful-fs "^4.2.9" slash "^3.0.0" -babel-jest@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.0.tgz#95a67f8e2e7c0042e7b3ad3951b8af41a533b5ea" - integrity sha512-zNKk0yhDZ6QUwfxh9k07GII6siNGMJWVUU49gmFj5gfdqDKLqa2RArXOF2CODp4Dr7dLxN2cvAV+667dGJ4b4w== - dependencies: - "@jest/transform" "^28.1.0" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^28.0.2" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - babel-plugin-istanbul@^6.0.0, babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -4555,16 +3949,6 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.0.2.tgz#9307d03a633be6fc4b1a6bc5c3a87e22bd01dd3b" - integrity sha512-Kizhn/ZL+68ZQHxSnHyuvJv8IchXD62KQxV77TBDV/xoBFBOfgRAk97GNs6hXdTTCiVES9nB2I6+7MXXrk5llQ== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - babel-plugin-macros@^2.6.1: version "2.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" @@ -4608,14 +3992,6 @@ babel-preset-jest@^27.5.1: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -babel-preset-jest@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.0.2.tgz#d8210fe4e46c1017e9fa13d7794b166e93aa9f89" - integrity sha512-sYzXIdgIXXroJTFeB3S6sNDWtlJ2dllCdTEsnZ65ACrMojj3hVNFRmnJ1HZtomGi+Be7aqpY/HJ92fr8OhKVkQ== - dependencies: - babel-plugin-jest-hoist "^28.0.2" - babel-preset-current-node-syntax "^1.0.0" - bail@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" @@ -4643,7 +4019,7 @@ base-x@^3.0.2, base-x@^3.0.8: dependencies: safe-buffer "^5.0.1" -base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -4850,13 +4226,6 @@ brorand@^1.0.1, brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= -brotli@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/brotli/-/brotli-1.3.2.tgz#525a9cad4fcba96475d7d388f6aecb13eed52f46" - integrity sha512-K0HNa0RRpUpcF8yS4yNSd6vmkrvA+wRd+symIcwhfqGLAi7YgGlKfO4oDYVgiahiLGNviO9uY7Zlb1MCPeTmSA== - dependencies: - base64-js "^1.1.2" - browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" @@ -5131,11 +4500,6 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" -call-me-maybe@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" - integrity sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw== - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -5168,7 +4532,7 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0, camelcase@^6.2.0: +camelcase@^6.0.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -5314,11 +4678,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chart.js@^3.7.1: - version "3.8.0" - resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.8.0.tgz#c6c14c457b9dc3ce7f1514a59e9b262afd6f1a94" - integrity sha512-cr8xhrXjLIXVLOBZPkBZVF6NDeiVIrPLHcMhnON7UufudL+CNeRrD+wpYanswlm8NpudMdrt3CHoLMQMxJhHRg== - chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -5373,11 +4732,6 @@ cjs-module-lexer@^0.6.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== -cjs-module-lexer@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" - integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== - class-is@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" @@ -5403,14 +4757,6 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -cleye@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/cleye/-/cleye-1.2.0.tgz#ab6479ad1364e959bc87746579bbe556496d9761" - integrity sha512-91phRUNIHDJz+DDKsJQP5t0a82/PGaxpzbhbU+l6qJFRd35u0f0SCpovxWtWhYe6QGfypuj5E4e7k0U7/DRHtw== - dependencies: - terminal-columns "^1.4.1" - type-flag "^2.1.0" - cli-boxes@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" @@ -5514,13 +4860,6 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= -cobe@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cobe/-/cobe-0.4.2.tgz#c3787f961a30d6adfbaf6cb446c891f45a516bdf" - integrity sha512-EHHCAWUom+dm1p5l/uy8pUiKnxzKyHXVWr4ky3Qk0Fav6Z0iW4iPMf7+nblF6uDYnd9Lk/ghbjDhwPc6b6EteA== - dependencies: - phenomenon "^1.6.0" - code-block-writer@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-11.0.0.tgz#5956fb186617f6740e2c3257757fea79315dd7d4" @@ -5535,11 +4874,6 @@ code-excerpt@^3.0.0: dependencies: convert-to-spaces "^1.0.1" -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== - collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -5599,17 +4933,12 @@ comma-separated-tokens@^2.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.2.tgz#d4c25abb679b7751c880be623c1179780fe1dd98" integrity sha512-G5yTt3KQN4Yn7Yk4ed73hlZ1evrFKXeUW3086p3PRFNp7m2vIjI6Pg+Kgb+oyzhd9F2qdcoj67+y3SdxL5XWsg== -commander@6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75" - integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q== - commander@9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-9.1.0.tgz#a6b263b2327f2e188c6402c42623327909f2dbec" integrity sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w== -commander@^2.7.1, commander@^2.8.1, commander@^2.9.0: +commander@^2.8.1, commander@^2.9.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -5639,11 +4968,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== - constant-case@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-1.1.2.tgz#8ec2ca5ba343e00aa38dbf4e200fd5ac907efd63" @@ -5652,7 +4976,7 @@ constant-case@^1.1.0: snake-case "^1.1.0" upper-case "^1.1.1" -content-disposition@0.5.4, content-disposition@^0.5.2, content-disposition@^0.5.3: +content-disposition@0.5.4, content-disposition@^0.5.2: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== @@ -5953,11 +5277,6 @@ dayjs@^1, dayjs@^1.11.2: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.2.tgz#fa0f5223ef0d6724b3d8327134890cfe3d72fbe5" integrity sha512-F4LXf1OeU9hrSYRPTTj/6FbO4HTjPKXvEIC1P2kcnFurViINCVk3ZV0xAS3XVx9MkMsXbbqlK6hjseaYbgKEHw== -debounce@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" - integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== - debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -6069,11 +5388,6 @@ decompress@^4.2.1: pify "^2.3.0" strip-dirs "^2.0.0" -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== - deep-extend@0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -6153,11 +5467,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== - denque@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/denque/-/denque-2.0.1.tgz#bcef4c1b80dc32efe97515744f21a4229ab8934a" @@ -6173,7 +5482,7 @@ depd@2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -depd@^1.1.0, depd@~1.1.2: +depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -6250,11 +5559,6 @@ diff-sequences@^27.5.1: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== -diff-sequences@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.0.2.tgz#40f8d4ffa081acbd8902ba35c798458d0ff1af41" - integrity sha512-YtEoNynLDFCRznv/XDalsKGSZDoj0U5kLnXvY0JSq3nBboRrZXjD81+eSiwi+nzcZDwedMmcowcxNwwgFW23mQ== - diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -6291,13 +5595,6 @@ dlv@^1.1.3: resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== -doctrine@3.0.0, doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -6305,6 +5602,13 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dom-helpers@^5.0.1: version "5.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" @@ -6434,11 +5738,6 @@ elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.3, elliptic@^6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emittery@^0.10.2: - version "0.10.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" - integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== - emittery@^0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" @@ -6484,7 +5783,7 @@ enhanced-resolve@^5.7.0: graceful-fs "^4.2.4" tapable "^2.2.0" -env-cmd@10.1.0, env-cmd@^10.1.0: +env-cmd@10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/env-cmd/-/env-cmd-10.1.0.tgz#c7f5d3b550c9519f137fdac4dd8fb6866a8c8c4b" integrity sha512-mMdWTT9XKN7yNth/6N6g2GuKuJTsKMDHlQFUDacb/heQRRWOTIZ42t1rMHnQu4jYxU1ajdTeJM+9eEETlqToMA== @@ -6938,7 +6237,7 @@ eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@8.15.0, eslint@^8.15.0: +eslint@^8.15.0: version "8.15.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.15.0.tgz#fea1d55a7062da48d82600d2e0974c55612a11e9" integrity sha512-GG5USZ1jhCu8HJkzGgeK8/+RGnHaNYZGrGDzUtigK3BsGESW/rs2az23XqE0WVwDxy1VRvvjSSGu5nB0Bu+6SA== @@ -6979,47 +6278,6 @@ eslint@8.15.0, eslint@^8.15.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -eslint@^8.10.0: - version "8.16.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.16.0.tgz#6d936e2d524599f2a86c708483b4c372c5d3bbae" - integrity sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA== - dependencies: - "@eslint/eslintrc" "^1.3.0" - "@humanwhocodes/config-array" "^0.9.2" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.3.2" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^6.0.1" - globals "^13.15.0" - ignore "^5.2.0" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" - strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - espree@^9.3.2: version "9.3.2" resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596" @@ -7282,7 +6540,7 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -execa@^5.0.0, execa@^5.1.1: +execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -7337,17 +6595,6 @@ expect@^26.6.2: jest-message-util "^26.6.2" jest-regex-util "^26.0.0" -expect@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.0.tgz#10e8da64c0850eb8c39a480199f14537f46e8360" - integrity sha512-qFXKl8Pmxk8TBGfaFKRtcQjfXEnKAs+dmlxdwvukJZorwrAabT7M3h8oLOG01I2utEhkmUTi17CHaPBovZsKdw== - dependencies: - "@jest/expect-utils" "^28.1.0" - jest-get-type "^28.0.2" - jest-matcher-utils "^28.1.0" - jest-message-util "^28.1.0" - jest-util "^28.1.0" - express@4.17.2: version "4.17.2" resolved "https://registry.yarnpkg.com/express/-/express-4.17.2.tgz#c18369f265297319beed4e5558753cc8c1364cb3" @@ -7813,7 +7060,7 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -fresh@0.5.2, fresh@^0.5.2: +fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= @@ -7845,15 +7092,6 @@ fs-extra@^4.0.2: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^8.0.1: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-minipass@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" @@ -7911,20 +7149,6 @@ functions-have-names@^1.2.2: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg== - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - gaxios@^4.0.0: version "4.3.2" resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.3.2.tgz#845827c2dc25a0213c8ab4155c7a28910f5be83f" @@ -8076,18 +7300,6 @@ glob-parent@^6.0.1, glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -8137,13 +7349,6 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.15.0: - version "13.15.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac" - integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== - dependencies: - type-fest "^0.20.2" - globals@^13.6.0, globals@^13.9.0: version "13.13.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.13.0.tgz#ac32261060d8070e2719dd6998406e27d2b5727b" @@ -8275,11 +7480,6 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== -graceful-fs@^4.2.0: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== - gradient-string@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/gradient-string/-/gradient-string-2.0.0.tgz#0333846e88e6011bdd12fa73d0fa2a60dfd34f51" @@ -8417,11 +7617,6 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -8477,26 +7672,11 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hast-util-is-element@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-2.1.2.tgz#fc0b0dc7cef3895e839b8d66979d57b0338c68f3" - integrity sha512-thjnlGAnwP8ef/GSO1Q8BfVk2gundnc2peGQqEg2kUt/IqesiGg/5mSwN2fE7nLzy61pg88NG6xV+UrGOrx9EA== - dependencies: - "@types/hast" "^2.0.0" - "@types/unist" "^2.0.0" - hast-util-parse-selector@^2.0.0: version "2.2.5" resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== -hast-util-sanitize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-4.0.0.tgz#71a02ca2e50d04b852a5500846418070ca364f60" - integrity sha512-pw56+69jq+QSr/coADNvWTmBPDy+XsmwaF5KnUys4/wM1jt/fZdl7GPxhXXXYdXnz3Gj3qMkbUCH2uKjvX0MgQ== - dependencies: - "@types/hast" "^2.0.0" - hast-util-to-estree@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/hast-util-to-estree/-/hast-util-to-estree-2.0.2.tgz#79c5bf588915610b3f0d47ca83a74dc0269c7dc2" @@ -8517,22 +7697,6 @@ hast-util-to-estree@^2.0.0: unist-util-position "^4.0.0" zwitch "^2.0.0" -hast-util-to-html@^8.0.0: - version "8.0.3" - resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-8.0.3.tgz#4e37580872e143ea9ce0dba87918b19e4ea997e3" - integrity sha512-/D/E5ymdPYhHpPkuTHOUkSatxr4w1ZKrZsG0Zv/3C2SRVT0JFJG53VS45AMrBtYk0wp5A7ksEhiC8QaOZM95+A== - dependencies: - "@types/hast" "^2.0.0" - ccount "^2.0.0" - comma-separated-tokens "^2.0.0" - hast-util-is-element "^2.0.0" - hast-util-whitespace "^2.0.0" - html-void-elements "^2.0.0" - property-information "^6.0.0" - space-separated-tokens "^2.0.0" - stringify-entities "^4.0.2" - unist-util-is "^5.0.0" - hast-util-whitespace@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-2.0.0.tgz#4fc1086467cc1ef5ba20673cb6b03cec3a970f1c" @@ -8606,11 +7770,6 @@ html-parse-stringify@^3.0.1: dependencies: void-elements "3.1.0" -html-void-elements@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-2.0.1.tgz#29459b8b05c200b6c5ee98743c41b979d577549f" - integrity sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A== - http-cache-semantics@3.8.1: version "3.8.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" @@ -8795,11 +7954,6 @@ image-q@^4.0.0: dependencies: "@types/node" "16.9.1" -immediate@~3.0.5: - version "3.0.6" - resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" - integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== - immutable@^3.x.x: version "3.8.2" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" @@ -9176,13 +8330,6 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw== - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -9496,17 +8643,6 @@ istanbul-lib-instrument@^5.0.4: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" -istanbul-lib-instrument@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f" - integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^6.3.0" - istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -9525,7 +8661,7 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^3.0.2, istanbul-reports@^3.1.3: +istanbul-reports@^3.0.2: version "3.1.4" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== @@ -9555,39 +8691,6 @@ jest-changed-files@^26.6.2: execa "^4.0.0" throat "^5.0.0" -jest-changed-files@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.0.2.tgz#7d7810660a5bd043af9e9cfbe4d58adb05e91531" - integrity sha512-QX9u+5I2s54ZnGoMEjiM2WeBvJR2J7w/8ZUmH2um/WLAuGAYFQcsVXY9+1YL6k0H/AGUdH8pXUAv6erDqEsvIA== - dependencies: - execa "^5.0.0" - throat "^6.0.1" - -jest-circus@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.0.tgz#e229f590911bd54d60efaf076f7acd9360296dae" - integrity sha512-rNYfqfLC0L0zQKRKsg4n4J+W1A2fbyGH7Ss/kDIocp9KXD9iaL111glsLu7+Z7FHuZxwzInMDXq+N1ZIBkI/TQ== - dependencies: - "@jest/environment" "^28.1.0" - "@jest/expect" "^28.1.0" - "@jest/test-result" "^28.1.0" - "@jest/types" "^28.1.0" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - is-generator-fn "^2.0.0" - jest-each "^28.1.0" - jest-matcher-utils "^28.1.0" - jest-message-util "^28.1.0" - jest-runtime "^28.1.0" - jest-snapshot "^28.1.0" - jest-util "^28.1.0" - pretty-format "^28.1.0" - slash "^3.0.0" - stack-utils "^2.0.3" - throat "^6.0.1" - jest-cli@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" @@ -9607,24 +8710,6 @@ jest-cli@^26.6.3: prompts "^2.0.1" yargs "^15.4.1" -jest-cli@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.0.tgz#cd1d8adb9630102d5ba04a22895f63decdd7ac1f" - integrity sha512-fDJRt6WPRriHrBsvvgb93OxgajHHsJbk4jZxiPqmZbMDRcHskfJBBfTyjFko0jjfprP544hOktdSi9HVgl4VUQ== - dependencies: - "@jest/core" "^28.1.0" - "@jest/test-result" "^28.1.0" - "@jest/types" "^28.1.0" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^28.1.0" - jest-util "^28.1.0" - jest-validate "^28.1.0" - prompts "^2.0.1" - yargs "^17.3.1" - jest-config@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" @@ -9646,36 +8731,8 @@ jest-config@^26.6.3: jest-resolve "^26.6.2" jest-util "^26.6.2" jest-validate "^26.6.2" - micromatch "^4.0.2" - pretty-format "^26.6.2" - -jest-config@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.0.tgz#fca22ca0760e746fe1ce1f9406f6b307ab818501" - integrity sha512-aOV80E9LeWrmflp7hfZNn/zGA4QKv/xsn2w8QCBP0t0+YqObuCWTSgNbHJ0j9YsTuCO08ZR/wsvlxqqHX20iUA== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^28.1.0" - "@jest/types" "^28.1.0" - babel-jest "^28.1.0" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^28.1.0" - jest-environment-node "^28.1.0" - jest-get-type "^28.0.2" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.0" - jest-runner "^28.1.0" - jest-util "^28.1.0" - jest-validate "^28.1.0" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^28.1.0" - slash "^3.0.0" - strip-json-comments "^3.1.1" + micromatch "^4.0.2" + pretty-format "^26.6.2" jest-diff@^26.6.2: version "26.6.2" @@ -9697,16 +8754,6 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-diff@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.0.tgz#77686fef899ec1873dbfbf9330e37dd429703269" - integrity sha512-8eFd3U3OkIKRtlasXfiAQfbovgFgRDb0Ngcs2E+FMeBZ4rUezqIaGjuyggJBp+llosQXNEWofk/Sz4Hr5gMUhA== - dependencies: - chalk "^4.0.0" - diff-sequences "^28.0.2" - jest-get-type "^28.0.2" - pretty-format "^28.1.0" - jest-docblock@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" @@ -9714,13 +8761,6 @@ jest-docblock@^26.0.0: dependencies: detect-newline "^3.0.0" -jest-docblock@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.0.2.tgz#3cab8abea53275c9d670cdca814fc89fba1298c2" - integrity sha512-FH10WWw5NxLoeSdQlJwu+MTiv60aXV/t8KEwIRGEv74WARE1cXIqh1vGdy2CraHuWOOrnzTWj/azQKqW4fO7xg== - dependencies: - detect-newline "^3.0.0" - jest-each@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" @@ -9732,17 +8772,6 @@ jest-each@^26.6.2: jest-util "^26.6.2" pretty-format "^26.6.2" -jest-each@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.0.tgz#54ae66d6a0a5b1913e9a87588d26c2687c39458b" - integrity sha512-a/XX02xF5NTspceMpHujmOexvJ4GftpYXqr6HhhmKmExtMXsyIN/fvanQlt/BcgFoRKN4OCXxLQKth9/n6OPFg== - dependencies: - "@jest/types" "^28.1.0" - chalk "^4.0.0" - jest-get-type "^28.0.2" - jest-util "^28.1.0" - pretty-format "^28.1.0" - jest-environment-jsdom@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" @@ -9768,18 +8797,6 @@ jest-environment-node@^26.6.2: jest-mock "^26.6.2" jest-util "^26.6.2" -jest-environment-node@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.0.tgz#6ed2150aa31babba0c488c5b4f4d813a585c68e6" - integrity sha512-gBLZNiyrPw9CSMlTXF1yJhaBgWDPVvH0Pq6bOEwGMXaYNzhzhw2kA/OijNF8egbCgDS0/veRv97249x2CX+udQ== - dependencies: - "@jest/environment" "^28.1.0" - "@jest/fake-timers" "^28.1.0" - "@jest/types" "^28.1.0" - "@types/node" "*" - jest-mock "^28.1.0" - jest-util "^28.1.0" - jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" @@ -9790,11 +8807,6 @@ jest-get-type@^27.5.1: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== -jest-get-type@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" - integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== - jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" @@ -9836,25 +8848,6 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.0.tgz#6c1ee2daf1c20a3e03dbd8e5b35c4d73d2349cf0" - integrity sha512-xyZ9sXV8PtKi6NCrJlmq53PyNVHzxmcfXNVvIRHpHmh1j/HChC4pwKgyjj7Z9us19JMw8PpQTJsFWOsIfT93Dw== - dependencies: - "@jest/types" "^28.1.0" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^28.0.2" - jest-util "^28.1.0" - jest-worker "^28.1.0" - micromatch "^4.0.4" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.3.2" - jest-jasmine2@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" @@ -9887,14 +8880,6 @@ jest-leak-detector@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" -jest-leak-detector@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.0.tgz#b65167776a8787443214d6f3f54935a4c73c8a45" - integrity sha512-uIJDQbxwEL2AMMs2xjhZl2hw8s77c3wrPaQ9v6tXJLGaaQ+4QrNJH5vuw7hA7w/uGT/iJ42a83opAqxGHeyRIA== - dependencies: - jest-get-type "^28.0.2" - pretty-format "^28.1.0" - jest-matcher-utils@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" @@ -9915,16 +8900,6 @@ jest-matcher-utils@^27.0.0: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-matcher-utils@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.0.tgz#2ae398806668eeabd293c61712227cb94b250ccf" - integrity sha512-onnax0n2uTLRQFKAjC7TuaxibrPSvZgKTcSCnNUz/tOjJ9UhxNm7ZmPpoQavmTDUjXvUQ8KesWk2/VdrxIFzTQ== - dependencies: - chalk "^4.0.0" - jest-diff "^28.1.0" - jest-get-type "^28.0.2" - pretty-format "^28.1.0" - jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -9940,21 +8915,6 @@ jest-message-util@^26.6.2: slash "^3.0.0" stack-utils "^2.0.2" -jest-message-util@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.0.tgz#7e8f0b9049e948e7b94c2a52731166774ba7d0af" - integrity sha512-RpA8mpaJ/B2HphDMiDlrAZdDytkmwFqgjDZovM21F35lHGeUeCvYmm6W+sbQ0ydaLpg5bFAUuWG1cjqOl8vqrw== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^28.1.0" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^28.1.0" - slash "^3.0.0" - stack-utils "^2.0.3" - jest-mock@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" @@ -9963,14 +8923,6 @@ jest-mock@^26.6.2: "@jest/types" "^26.6.2" "@types/node" "*" -jest-mock@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.0.tgz#ccc7cc12a9b330b3182db0c651edc90d163ff73e" - integrity sha512-H7BrhggNn77WhdL7O1apG0Q/iwl0Bdd5E1ydhCJzL3oBLh/UYxAwR3EJLsBZ9XA3ZU4PA3UNw4tQjduBTCTmLw== - dependencies: - "@jest/types" "^28.1.0" - "@types/node" "*" - jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" @@ -9986,11 +8938,6 @@ jest-regex-util@^27.5.1: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-regex-util@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" - integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== - jest-resolve-dependencies@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" @@ -10000,14 +8947,6 @@ jest-resolve-dependencies@^26.6.3: jest-regex-util "^26.0.0" jest-snapshot "^26.6.2" -jest-resolve-dependencies@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.0.tgz#167becb8bee6e20b5ef4a3a728ec67aef6b0b79b" - integrity sha512-Ue1VYoSZquPwEvng7Uefw8RmZR+me/1kr30H2jMINjGeHgeO/JgrR6wxj2ofkJ7KSAA11W3cOrhNCbj5Dqqd9g== - dependencies: - jest-regex-util "^28.0.2" - jest-snapshot "^28.1.0" - jest-resolve@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" @@ -10022,21 +8961,6 @@ jest-resolve@^26.6.2: resolve "^1.18.1" slash "^3.0.0" -jest-resolve@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.0.tgz#b1f32748a6cee7d1779c7ef639c0a87078de3d35" - integrity sha512-vvfN7+tPNnnhDvISuzD1P+CRVP8cK0FHXRwPAcdDaQv4zgvwvag2n55/h5VjYcM5UJG7L4TwE5tZlzcI0X2Lhw== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.0" - jest-pnp-resolver "^1.2.2" - jest-util "^28.1.0" - jest-validate "^28.1.0" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - jest-runner@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" @@ -10063,33 +8987,6 @@ jest-runner@^26.6.3: source-map-support "^0.5.6" throat "^5.0.0" -jest-runner@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.0.tgz#aefe2a1e618a69baa0b24a50edc54fdd7e728eaa" - integrity sha512-FBpmuh1HB2dsLklAlRdOxNTTHKFR6G1Qmd80pVDvwbZXTriqjWqjei5DKFC1UlM732KjYcE6yuCdiF0WUCOS2w== - dependencies: - "@jest/console" "^28.1.0" - "@jest/environment" "^28.1.0" - "@jest/test-result" "^28.1.0" - "@jest/transform" "^28.1.0" - "@jest/types" "^28.1.0" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.10.2" - graceful-fs "^4.2.9" - jest-docblock "^28.0.2" - jest-environment-node "^28.1.0" - jest-haste-map "^28.1.0" - jest-leak-detector "^28.1.0" - jest-message-util "^28.1.0" - jest-resolve "^28.1.0" - jest-runtime "^28.1.0" - jest-util "^28.1.0" - jest-watcher "^28.1.0" - jest-worker "^28.1.0" - source-map-support "0.5.13" - throat "^6.0.1" - jest-runtime@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" @@ -10123,34 +9020,6 @@ jest-runtime@^26.6.3: strip-bom "^4.0.0" yargs "^15.4.1" -jest-runtime@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.0.tgz#4847dcb2a4eb4b0f9eaf41306897e51fb1665631" - integrity sha512-wNYDiwhdH/TV3agaIyVF0lsJ33MhyujOe+lNTUiolqKt8pchy1Hq4+tDMGbtD5P/oNLA3zYrpx73T9dMTOCAcg== - dependencies: - "@jest/environment" "^28.1.0" - "@jest/fake-timers" "^28.1.0" - "@jest/globals" "^28.1.0" - "@jest/source-map" "^28.0.2" - "@jest/test-result" "^28.1.0" - "@jest/transform" "^28.1.0" - "@jest/types" "^28.1.0" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - execa "^5.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^28.1.0" - jest-message-util "^28.1.0" - jest-mock "^28.1.0" - jest-regex-util "^28.0.2" - jest-resolve "^28.1.0" - jest-snapshot "^28.1.0" - jest-util "^28.1.0" - slash "^3.0.0" - strip-bom "^4.0.0" - jest-serializer@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" @@ -10189,35 +9058,6 @@ jest-snapshot@^26.6.2: pretty-format "^26.6.2" semver "^7.3.2" -jest-snapshot@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.0.tgz#4b74fa8816707dd10fe9d551c2c258e5a67b53b6" - integrity sha512-ex49M2ZrZsUyQLpLGxQtDbahvgBjlLPgklkqGM0hq/F7W/f8DyqZxVHjdy19QKBm4O93eDp+H5S23EiTbbUmHw== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^28.1.0" - "@jest/transform" "^28.1.0" - "@jest/types" "^28.1.0" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^28.1.0" - graceful-fs "^4.2.9" - jest-diff "^28.1.0" - jest-get-type "^28.0.2" - jest-haste-map "^28.1.0" - jest-matcher-utils "^28.1.0" - jest-message-util "^28.1.0" - jest-util "^28.1.0" - natural-compare "^1.4.0" - pretty-format "^28.1.0" - semver "^7.3.5" - jest-util@^26.1.0, jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" @@ -10242,18 +9082,6 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.0.tgz#d54eb83ad77e1dd441408738c5a5043642823be5" - integrity sha512-qYdCKD77k4Hwkose2YBEqQk7PzUf/NSE+rutzceduFveQREeH6b+89Dc9+wjX9dAwHcgdx4yedGA3FQlU/qCTA== - dependencies: - "@jest/types" "^28.1.0" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-validate@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" @@ -10266,18 +9094,6 @@ jest-validate@^26.6.2: leven "^3.1.0" pretty-format "^26.6.2" -jest-validate@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.0.tgz#8a6821f48432aba9f830c26e28226ad77b9a0e18" - integrity sha512-Lly7CJYih3vQBfjLeANGgBSBJ7pEa18cxpQfQEq2go2xyEzehnHfQTjoUia8xUv4x4J80XKFIDwJJThXtRFQXQ== - dependencies: - "@jest/types" "^28.1.0" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^28.0.2" - leven "^3.1.0" - pretty-format "^28.1.0" - jest-watcher@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" @@ -10291,20 +9107,6 @@ jest-watcher@^26.6.2: jest-util "^26.6.2" string-length "^4.0.1" -jest-watcher@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.0.tgz#aaa7b4164a4e77eeb5f7d7b25ede5e7b4e9c9aaf" - integrity sha512-tNHMtfLE8Njcr2IRS+5rXYA4BhU90gAOwI9frTGOqd+jX0P/Au/JfRSNqsf5nUTcWdbVYuLxS1KjnzILSoR5hA== - dependencies: - "@jest/test-result" "^28.1.0" - "@jest/types" "^28.1.0" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.10.2" - jest-util "^28.1.0" - string-length "^4.0.1" - jest-worker@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" @@ -10323,15 +9125,6 @@ jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.0.tgz#ced54757a035e87591e1208253a6e3aac1a855e5" - integrity sha512-ZHwM6mNwaWBR52Snff8ZvsCTqQsvhCxP/bT1I6T6DAnb6ygkshsyLQIMxFwHpYxht0HOoqt23JlC01viI7T03A== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - jest@^26.0.0: version "26.6.3" resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" @@ -10341,15 +9134,6 @@ jest@^26.0.0: import-local "^3.0.2" jest-cli "^26.6.3" -jest@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.0.tgz#f420e41c8f2395b9a30445a97189ebb57593d831" - integrity sha512-TZR+tHxopPhzw3c3560IJXZWLNHgpcz1Zh0w5A65vynLGNcg/5pZ+VildAd7+XGOu6jd58XMY/HNn0IkZIXVXg== - dependencies: - "@jest/core" "^28.1.0" - import-local "^3.0.2" - jest-cli "^28.1.0" - jimp@^0.16.1: version "0.16.1" resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.16.1.tgz#192f851a30e5ca11112a3d0aa53137659a78ca7a" @@ -10533,15 +9317,6 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" -jsonfile@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-5.0.0.tgz#e6b718f73da420d612823996fdf14a03f6ff6922" - integrity sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w== - dependencies: - universalify "^0.1.2" - optionalDependencies: - graceful-fs "^4.1.6" - jsonwebtoken@^8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" @@ -10721,13 +9496,6 @@ libphonenumber-js@^1.9.52, libphonenumber-js@^1.9.53: resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.53.tgz#f4f3321f8fb0ee62952c2a8df4711236d2626088" integrity sha512-3cuMrA2CY3TbKVC0wKye5dXYgxmVVi4g13gzotprQSguFHMqf0pIrMM2Z6ZtMsSWqvtIqi5TuQhGjMhxz0O9Mw== -lie@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" - integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw== - dependencies: - immediate "~3.0.5" - lilconfig@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" @@ -10815,13 +9583,6 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -localforage@^1.8.1: - version "1.10.0" - resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" - integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg== - dependencies: - lie "3.1.1" - locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -10902,11 +9663,6 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.mergewith@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" - integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== - lodash.once@^4.0.0, lodash.once@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" @@ -11023,11 +9779,6 @@ lru-memoizer@^2.1.2, lru-memoizer@^2.1.4: lodash.clonedeep "^4.5.0" lru-cache "~4.0.0" -lru_map@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" - integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== - luxon@^1.21.3: version "1.28.0" resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" @@ -11272,21 +10023,6 @@ mdast-util-mdxjs-esm@^1.0.0: mdast-util-from-markdown "^1.0.0" mdast-util-to-markdown "^1.0.0" -mdast-util-to-hast@^11.0.0: - version "11.3.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-11.3.0.tgz#ea9220617a710e80aa5cc3ac7cc9d4bb0440ae7a" - integrity sha512-4o3Cli3hXPmm1LhB+6rqhfsIUBjnKFlIUZvudaermXB+4/KONdd/W4saWWkC+LBLbPMqhFSSTSRgafHsT5fVJw== - dependencies: - "@types/hast" "^2.0.0" - "@types/mdast" "^3.0.0" - "@types/mdurl" "^1.0.0" - mdast-util-definitions "^5.0.0" - mdurl "^1.0.0" - unist-builder "^3.0.0" - unist-util-generated "^2.0.0" - unist-util-position "^4.0.0" - unist-util-visit "^4.0.0" - mdast-util-to-hast@^12.1.0: version "12.1.1" resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-12.1.1.tgz#89a2bb405eaf3b05eb8bf45157678f35eef5dbca" @@ -11391,7 +10127,7 @@ merge-class-names@^1.1.1: resolved "https://registry.yarnpkg.com/merge-class-names/-/merge-class-names-1.4.2.tgz#78d6d95ab259e7e647252a7988fd25a27d5a8835" integrity sha512-bOl98VzwCGi25Gcn3xKxnR5p/WrhWFQB59MS/aGENcmUc6iSm96yrFDF0XSNurX9qN4LbJm0R9kfvsQ17i8zCw== -merge-descriptors@1.0.1, merge-descriptors@^1.0.1: +merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= @@ -11416,7 +10152,7 @@ methods@^1.1.2, methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micro@9.3.4, micro@^9.3.4: +micro@^9.3.4: version "9.3.4" resolved "https://registry.yarnpkg.com/micro/-/micro-9.3.4.tgz#745a494e53c8916f64fb6a729f8cbf2a506b35ad" integrity sha512-smz9naZwTG7qaFnEZ2vn248YZq9XR+XoOH3auieZbkhDL4xLOxiE+KqG8qqnBeKfXA9c1uEFGCxPN1D+nT6N7w== @@ -11940,13 +10676,6 @@ mockdate@^3.0.5: resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-3.0.5.tgz#789be686deb3149e7df2b663d2bc4392bc3284fb" integrity sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ== -modify-response-middleware@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/modify-response-middleware/-/modify-response-middleware-1.1.0.tgz#8713182390a7c7ec32dfc79b9bc626d14060d4ce" - integrity sha512-oAyioDzMvyYwbq8tH7DvahHtGzQvDwZlN2CxL2fZQdUZNz8JJvC3PYaagiMGN58sM45Veo1GA8g/aJI7Hogpxw== - dependencies: - brotli "^1.3.2" - module-alias@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0" @@ -12094,7 +10823,7 @@ nanoid@^3.1.23, nanoid@^3.3.1: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.2.tgz#c89622fafb4381cd221421c69ec58547a1eec557" integrity sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA== -nanoid@^3.1.30, nanoid@^3.3.4: +nanoid@^3.1.30, nanoid@^3.3.3: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== @@ -12131,14 +10860,7 @@ neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -next-api-middleware@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/next-api-middleware/-/next-api-middleware-1.0.1.tgz#8dd76abeace9e10c6be29c9c9486a84cb649cc12" - integrity sha512-t8UbZ9UUPFB7nklrHdAusn7MfoVSHtnWlRV1R0hirvLRHPDnzidTnw1Mu99Kqvc1bVC0rQSUX5kf0j/4nmEtfA== - dependencies: - debug "^4.3.2" - -next-auth@^4.3.3, next-auth@^4.3.4: +next-auth@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.3.4.tgz#7b241e34e1f68632768cef8ee289e33256cb2b19" integrity sha512-8dGkNicbxY2BYsJq4uOJIEsGt39wXj5AViTBsVfbRQqtAFmZmXYHutf90VBmobm8rT2+Xl60HDUTkuVVK+x+xw== @@ -12183,25 +10905,11 @@ next-mdx-remote@^4.0.3: vfile "^5.3.0" vfile-matter "^3.0.1" -next-plausible@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/next-plausible/-/next-plausible-2.2.0.tgz#f825842f97bce0062bdaf897328c4908d7ce0a78" - integrity sha512-pIhs5MikL6ZMJvB7sxkM49xN06W1A6d6RYta5vrqwQmF2/oXoCG+IPoaPzyODZ/vo7f2/NMAOaUm5QM0dKqMdA== - next-seo@^4.26.0: version "4.29.0" resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-4.29.0.tgz#d281e95ba47914117cc99e9e468599f0547d9b9b" integrity sha512-xmwzcz4uHaYJ8glbuhs6FSBQ7z3irmdPYdJJ5saWm72Uy3o+mPKGaPCXQetTCE6/xxVnpoDV4yFtFlEjUcljSg== -next-swagger-doc@^0.3.4: - version "0.3.4" - resolved "https://registry.yarnpkg.com/next-swagger-doc/-/next-swagger-doc-0.3.4.tgz#6f4e23f23af3ed14e223285809be4a29f61cea7c" - integrity sha512-HhMzSlcos+MmvxEE09qgTz7QI5sYIQ1LRNSKcQ306k7UTiADtOB+UK/b5nRqr5Le3rxsP1fF8LfA+RHRbdUqhg== - dependencies: - cleye "1.2.0" - isarray "2.0.5" - swagger-jsdoc "6.2.1" - next-themes@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.0.8.tgz#2a1748317085afbc2509e2c32bd04af4f0f6cb7d" @@ -12228,12 +10936,7 @@ next-transpile-modules@^9.0.0: enhanced-resolve "^5.7.0" escalade "^3.1.1" -next-validations@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/next-validations/-/next-validations-0.2.0.tgz#ce3c4bc332b115beda633521fd81e587987864eb" - integrity sha512-QMF2hRNSSbjeBaCYqpt3mEM9CkXXzaMCWCvPyi5/vKTBjbgkiYtaQnUfjj5eH8dX+ZmRrBYGgN1EKqL7ZnI0wQ== - -next@12.1.6, next@^12.1.0, next@^12.1.6: +next@12.1.6, next@^12.1.6: version "12.1.6" resolved "https://registry.yarnpkg.com/next/-/next-12.1.6.tgz#eb205e64af1998651f96f9df44556d47d8bbc533" integrity sha512-cebwKxL3/DhNKfg9tPZDQmbRKjueqykHHbgaoG4VBRH3AHQJ2HO0dbKFiS1hPhe1/qgc2d/hFeadsbPicmLD+A== @@ -12305,7 +11008,7 @@ node-domexception@1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -12327,22 +11030,6 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-mocks-http@^1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/node-mocks-http/-/node-mocks-http-1.11.0.tgz#defc0febf6b935f08245397d47534a8de592996e" - integrity sha512-jS/WzSOcKbOeGrcgKbenZeNhxUNnP36Yw11+hL4TTxQXErGfqYZ+MaYNNvhaTiGIJlzNSqgQkk9j8dSu1YWSuw== - dependencies: - accepts "^1.3.7" - content-disposition "^0.5.3" - depd "^1.1.0" - fresh "^0.5.2" - merge-descriptors "^1.0.1" - methods "^1.1.2" - mime "^1.3.4" - parseurl "^1.3.3" - range-parser "^1.2.0" - type-is "^1.6.18" - node-notifier@^8.0.0: version "8.0.2" resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" @@ -12455,21 +11142,6 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npmlog@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== - number-to-bn@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" @@ -12949,7 +11621,7 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0, parse-json@^5.2.0: +parse-json@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -12976,7 +11648,7 @@ parse5@^5.1.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== -parseurl@^1.3.3, parseurl@~1.3.3: +parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -13145,11 +11817,6 @@ pgpass@1.x: dependencies: split2 "^4.1.0" -phenomenon@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/phenomenon/-/phenomenon-1.6.0.tgz#7b5b7647d0b48152cc0846994da3d92e8f6da677" - integrity sha512-7h9/fjPD3qNlgggzm88cY58l9sudZ6Ey+UmZsizfhtawO6E3srZQXywaNm2lBwT72TbpHYRPy7ytIHeBUD/G0A== - phin@^2.9.1: version "2.9.3" resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c" @@ -13304,15 +11971,6 @@ postcss@8.4.5: picocolors "^1.0.0" source-map-js "^1.0.1" -postcss@^8.3.6, postcss@^8.4.13: - version "8.4.13" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575" - integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA== - dependencies: - nanoid "^3.3.3" - picocolors "^1.0.0" - source-map-js "^1.0.2" - postcss@^8.4.12: version "8.4.12" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" @@ -13322,12 +11980,12 @@ postcss@^8.4.12: picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@^8.4.8: - version "8.4.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" - integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== +postcss@^8.4.13: + version "8.4.13" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575" + integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA== dependencies: - nanoid "^3.3.4" + nanoid "^3.3.3" picocolors "^1.0.0" source-map-js "^1.0.2" @@ -13421,16 +12079,6 @@ pretty-format@^27.0.0, pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^28.1.0: - version "28.1.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.0.tgz#8f5836c6a0dfdb834730577ec18029052191af55" - integrity sha512-79Z4wWOYCdvQkEoEuSlBhHJqWeZ8D8YRPiPctJFCtvuaClGpiwiQYSCUOE6IEKUbbFukKOTFIUAXE8N4EQTo1Q== - dependencies: - "@jest/schemas" "^28.0.2" - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^18.0.0" - pretty-format@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385" @@ -13473,11 +12121,6 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= -progress@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -13683,7 +12326,7 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@^1.2.0, range-parser@~1.2.1: +range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== @@ -13738,23 +12381,11 @@ react-calendar@^3.3.1: merge-class-names "^1.1.1" prop-types "^15.6.0" -react-chartjs-2@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/react-chartjs-2/-/react-chartjs-2-4.1.0.tgz#2a123df16d3a987c54eb4e810ed766d3c03adf8d" - integrity sha512-AsUihxEp8Jm1oBhbEovE+w50m9PVNhz1sfwEIT4hZduRC0m14gHWHd0cUaxkFDb8HNkdMIGzsNlmVqKiOpU74g== - react-colorful@^5.5.1: version "5.5.1" resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.5.1.tgz#29d9c4e496f2ca784dd2bb5053a3a4340cfaf784" integrity sha512-M1TJH2X3RXEt12sWkpa6hLc/bbYS0H6F4rIqjQZ+RxNBstpY67d9TrFXtqdZwhpmBXcCwEi7stKqFue3ZRkiOg== -react-confetti@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/react-confetti/-/react-confetti-6.1.0.tgz#03dc4340d955acd10b174dbf301f374a06e29ce6" - integrity sha512-7Ypx4vz0+g8ECVxr88W9zhcQpbeujJAVqL14ZnXJ3I23mOI9/oBVTQ3dkJhUmB0D6XOtCZEM6N0Gm9PMngkORw== - dependencies: - tween-functions "^1.2.0" - react-copy-to-clipboard@5.0.4: version "5.0.4" resolved "https://registry.yarnpkg.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.4.tgz#42ec519b03eb9413b118af92d1780c403a5f19bf" @@ -13825,17 +12456,12 @@ react-fit@^1.4.0: prop-types "^15.6.0" tiny-warning "^1.0.0" -react-hook-form@^7.16.2: - version "7.31.3" - resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.31.3.tgz#b61bafb9a7435f91695351a7a9f714d8c4df0121" - integrity sha512-NVZdCWViIWXXXlQ3jxVQH0NuNfwPf8A/0KvuCxrM9qxtP1qYosfR2ZudarziFrVOC7eTUbWbm1T4OyYCwv9oSQ== - react-hook-form@^7.31.1: version "7.31.1" resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.31.1.tgz#16c357dd366bc226172e6acbb5a1672873bbfb28" integrity sha512-QjtjZ8r8KtEBWWpcXLyQordCraTFxILtyQpaz5KLLxN2YzcC+FZ9LLtOnNGuOnzZo9gCoB+viK3ZHV9Mb2htmQ== -react-hot-toast@^2.1.0, react-hot-toast@^2.1.1: +react-hot-toast@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.2.0.tgz#ab6f4caed4214b9534f94bb8cfaaf21b051e62b9" integrity sha512-248rXw13uhf/6TNDVzagX+y7R8J183rp7MwUMNkcrBRyHj/jWOggfXTGlM8zAOuh701WyVW+eUaWG2LeSufX9g== @@ -13903,11 +12529,6 @@ react-is@^17.0.1, react-is@^17.0.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0: - version "18.1.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67" - integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg== - react-live-chat-loader@^2.7.3: version "2.7.3" resolved "https://registry.yarnpkg.com/react-live-chat-loader/-/react-live-chat-loader-2.7.3.tgz#a66a7d64eacdf0a680570b4e9a99639a88bffaae" @@ -14053,15 +12674,6 @@ react-transition-group@^4.3.0: loose-envify "^1.4.0" prop-types "^15.6.2" -react-twemoji@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/react-twemoji/-/react-twemoji-0.3.0.tgz#8c8d5aedec8dda5cc0538043639073bcdd44c3a8" - integrity sha512-y2ZQD3KvpZklETxz9c1NycRdUVF5nKsJ0bPNW3SaRJT+ReK36sMcneYwRPfv9EK2p3s9ph/NczDglnB8wbMJ0g== - dependencies: - lodash.isequal "^4.5.0" - prop-types "^15.7.2" - twemoji "^13.0.1" - react-use-intercom@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/react-use-intercom/-/react-use-intercom-1.5.1.tgz#94567a80ce3b56692962d712a54489c55fb4c54e" @@ -14123,7 +12735,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5: +readable-stream@^2.0.0, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -14251,17 +12863,6 @@ remark-gfm@^1.0.0: mdast-util-gfm "^0.1.0" micromark-extension-gfm "^0.3.0" -remark-html@^14.0.1: - version "14.0.1" - resolved "https://registry.yarnpkg.com/remark-html/-/remark-html-14.0.1.tgz#2118516604c1a6c2ea9d5914a526942554e04e30" - integrity sha512-a/x5bTlFrkwYkz43zuJIk0m0IuS5Rx8zLztGwdzmAdUj0Hsi4C4nkJ8gTQRNXY/ET/gMrqQORMMI0arRItq/aQ== - dependencies: - "@types/mdast" "^3.0.0" - hast-util-sanitize "^4.0.0" - hast-util-to-html "^8.0.0" - mdast-util-to-hast "^11.0.0" - unified "^10.0.0" - remark-mdx@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-2.1.1.tgz#14021be9ecbc9ad0310f4240980221328aa7ed55" @@ -14296,15 +12897,6 @@ remark-rehype@^10.0.0: mdast-util-to-hast "^12.1.0" unified "^10.0.0" -remark-stringify@^10.0.0: - version "10.0.2" - resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-10.0.2.tgz#50414a6983f5008eb9e72eed05f980582d1f69d7" - integrity sha512-6wV3pvbPvHkbNnWB0wdDvVFHOe1hBRAx1Q/5g/EpH4RppAII6J8Gnwe7VbHuXaoKIF6LAg6ExTel/+kNqSQ7lw== - dependencies: - "@types/mdast" "^3.0.0" - mdast-util-to-markdown "^1.0.0" - unified "^10.0.0" - remark-stringify@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-9.0.1.tgz#576d06e910548b0a7191a71f27b33f1218862894" @@ -14321,16 +12913,6 @@ remark@^13.0.0: remark-stringify "^9.0.0" unified "^9.1.0" -remark@^14.0.1: - version "14.0.2" - resolved "https://registry.yarnpkg.com/remark/-/remark-14.0.2.tgz#4a1833f7441a5c29e44b37bb1843fb820797b40f" - integrity sha512-A3ARm2V4BgiRXaUo5K0dRvJ1lbogrbXnhkJRmD0yw092/Yl0kOCZt1k9ZeElEwkZsWGsMumz6qL5MfNJH9nOBA== - dependencies: - "@types/mdast" "^3.0.0" - remark-parse "^10.0.0" - remark-stringify "^10.0.0" - unified "^10.0.0" - remarkable@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-2.0.1.tgz#280ae6627384dfb13d98ee3995627ca550a12f31" @@ -14427,11 +13009,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== - resolve@^1.10.0, resolve@^1.12.0, resolve@^1.18.1, resolve@^1.20.0, resolve@^1.22.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" @@ -14685,7 +13262,7 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +semver@^7.3.4, semver@^7.3.7: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== @@ -14751,7 +13328,7 @@ servify@^0.1.12: request "^2.79.0" xhr "^2.3.3" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -14840,7 +13417,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -15004,14 +13581,6 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@^0.5.21, source-map-support@^0.5.6: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -15132,7 +13701,7 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -stack-utils@^2.0.2, stack-utils@^2.0.3: +stack-utils@^2.0.2: version "2.0.5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== @@ -15182,16 +13751,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -15288,14 +13848,6 @@ stringify-entities@^4.0.0: character-entities-html4 "^2.0.0" character-entities-legacy "^3.0.0" -stringify-entities@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.3.tgz#cfabd7039d22ad30f3cc435b0ca2c1574fc88ef8" - integrity sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g== - dependencies: - character-entities-html4 "^2.0.0" - character-entities-legacy "^3.0.0" - stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" @@ -15312,7 +13864,7 @@ strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: +strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= @@ -15536,25 +14088,6 @@ swagger-client@^3.18.5: traverse "~0.6.6" url "~0.11.0" -swagger-jsdoc@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/swagger-jsdoc/-/swagger-jsdoc-6.2.1.tgz#2f3f67c8433ae2c09e7e318d2f8fd9e09f0166b3" - integrity sha512-l2BwFf7wzNPb11+NRRy65X+kuHUGLb3ZuGFn6A8xDXXTu73YzJmCiy+LED/6QsOgPBPgO3u3sDEz6KuOHAlCtA== - dependencies: - commander "6.2.0" - doctrine "3.0.0" - glob "7.1.6" - lodash.mergewith "^4.6.2" - swagger-parser "10.0.2" - yaml "2.0.0-1" - -swagger-parser@10.0.2: - version "10.0.2" - resolved "https://registry.yarnpkg.com/swagger-parser/-/swagger-parser-10.0.2.tgz#d7f18faa09c9c145e938977c9bd6c3435998b667" - integrity sha512-9jHkHM+QXyLGFLk1DkXBwV+4HyNm0Za3b8/zk/+mjr8jgOSiqm3FOTHBSDsBjtn9scdL+8eWcHdupp2NLM8tDw== - dependencies: - "@apidevtools/swagger-parser" "10.0.2" - swagger-ui-react@4.11.1: version "4.11.1" resolved "https://registry.yarnpkg.com/swagger-ui-react/-/swagger-ui-react-4.11.1.tgz#1de7feea2f99aa292d52cfa3d31edf665eaa6181" @@ -15619,17 +14152,12 @@ swarm-js@^0.1.40: tar "^4.0.2" xhr-request "^1.0.1" -swr@^1.2.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/swr/-/swr-1.3.0.tgz#c6531866a35b4db37b38b72c45a63171faf9f4e8" - integrity sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw== - symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -tailwindcss@^3.0.23, tailwindcss@^3.0.24: +tailwindcss@^3.0.24: version "3.0.24" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.24.tgz#22e31e801a44a78a1d9a81ecc52e13b69d85704d" integrity sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig== @@ -15687,11 +14215,6 @@ tar@^4.0.2: safe-buffer "^5.2.1" yallist "^3.1.1" -terminal-columns@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/terminal-columns/-/terminal-columns-1.4.1.tgz#f669dd878afaad0c404d4b7aa01855fb6f24f1a4" - integrity sha512-IKVL/itiMy947XWVv4IHV7a0KQXvKjj4ptbi7Ew9MPMcOLzkiQeyx3Gyvh62hKrfJ0RZc4M1nbhzjNM39Kyujw== - terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -15738,11 +14261,6 @@ throat@^5.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== -throat@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" - integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== - through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -16150,31 +14668,11 @@ turbo@1.2.9: turbo-windows-64 "1.2.9" turbo-windows-arm64 "1.2.9" -tween-functions@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tween-functions/-/tween-functions-1.2.0.tgz#1ae3a50e7c60bb3def774eac707acbca73bbc3ff" - integrity sha1-GuOlDnxguz3vd06scHrLynO7w/8= - tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -twemoji-parser@13.1.0: - version "13.1.0" - resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-13.1.0.tgz#65e7e449c59258791b22ac0b37077349127e3ea4" - integrity sha512-AQOzLJpYlpWMy8n+0ATyKKZzWlZBJN+G0C+5lhX7Ftc2PeEVdUU/7ns2Pn2vVje26AIZ/OHwFoUbdv6YYD/wGg== - -twemoji@^13.0.1: - version "13.1.1" - resolved "https://registry.yarnpkg.com/twemoji/-/twemoji-13.1.1.tgz#6e31409908bb5383cdb1d09c9c8e7856aa6e2e3b" - integrity sha512-IIIoq+n1lk1M1+evBKZD3DO0ud02fDQ4ssbgAv8rp3YBWUeNmskjlisFUPPDacQ50XS3bhrd4Kq9Q2gqhxb0dg== - dependencies: - fs-extra "^8.0.1" - jsonfile "^5.0.0" - twemoji-parser "13.1.0" - universalify "^0.1.2" - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -16229,12 +14727,7 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-flag@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/type-flag/-/type-flag-2.1.0.tgz#9d3c7c84804032d4e03b4877a866c25c122c1136" - integrity sha512-ln8u/BdNzppXeFP8JsmsofElwVC2/FYoQAIipzhx45jjFwSApFqIieBEY93TKoLpphQBAbazX8LCoIY59Vwxlg== - -type-is@^1.6.18, type-is@~1.6.18: +type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -16286,11 +14779,6 @@ typescript@4.6.4, typescript@^4.6.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== -tzdata@^1.0.30: - version "1.0.30" - resolved "https://registry.yarnpkg.com/tzdata/-/tzdata-1.0.30.tgz#d9d5a4b4b5e1ed95f6255f98c0564c4256316f52" - integrity sha512-/0yogZsIRUVhGIEGZahL+Nnl9gpMD6jtQ9MlVtPVofFwhaqa+cFTgRy1desTAKqdmIJjS6CL+i6F/mnetrLaxw== - uglify-js@^3.1.4: version "3.15.3" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.3.tgz#9aa82ca22419ba4c0137642ba0df800cb06e0471" @@ -16667,15 +15155,6 @@ v8-to-istanbul@^7.0.0: convert-source-map "^1.6.0" source-map "^0.7.3" -v8-to-istanbul@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.0.tgz#be0dae58719fc53cb97e5c7ac1d7e6d4f5b19511" - integrity sha512-HcvgY/xaRm7isYmyx+lFKA4uQmfUbN0J4M0nNItvzTvH/iQ9kW5j/t4YSR+Ge323/lrgDAWJoF46tzGQHwBHFw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.7" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -16684,11 +15163,6 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -validator@^13.6.0: - version "13.7.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" - integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== - varint@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" @@ -17199,13 +15673,6 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - widest-line@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" @@ -17256,14 +15723,6 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" - integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^3.0.7" - ws@^3.0.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" @@ -17420,11 +15879,6 @@ yallist@^3.0.0, yallist@^3.1.1: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yaml@2.0.0-1: - version "2.0.0-1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.0.0-1.tgz#8c3029b3ee2028306d5bcf396980623115ff8d18" - integrity sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ== - yaml@^1.10.2, yaml@^1.7.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" @@ -17491,19 +15945,6 @@ yargs@^17.0.1: y18n "^5.0.5" yargs-parser "^21.0.0" -yargs@^17.3.1: - version "17.5.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e" - integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.0.0" - yauzl@^2.4.2: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" @@ -17537,17 +15978,6 @@ yup@^0.32.9: property-expr "^2.0.4" toposort "^2.0.2" -z-schema@^4.2.3: - version "4.2.4" - resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-4.2.4.tgz#73102a49512179b12a8ec50b1daa676b984da6e4" - integrity sha512-YvBeW5RGNeNzKOUJs3rTL4+9rpcvHXt5I051FJbOcitV8bl40pEfcG0Q+dWSwS0/BIYrMZ/9HHoqLllMkFhD0w== - dependencies: - lodash.get "^4.4.2" - lodash.isequal "^4.5.0" - validator "^13.6.0" - optionalDependencies: - commander "^2.7.1" - zen-observable-ts@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz#2d1aa9d79b87058e9b75698b92791c1838551f83" From 799d26cdcc8053faa450eee9a40e28676e81d9fb Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Fri, 10 Jun 2022 11:21:22 +0100 Subject: [PATCH 03/33] fixed small regression due to merge --- apps/web/components/booking/pages/AvailabilityPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 0147157a0fb1e8..f336b52b621aa1 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -514,7 +514,7 @@ const AvailabilityPage = ({ profile, plan, eventTypeId, eventType, workingHours,

)} - + {timezoneDropdown}
{booking?.startTime && rescheduleUid && ( From 2d4f29ccaf4e5d42e1735691234d15e3f6224c26 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Fri, 10 Jun 2022 14:17:01 +0100 Subject: [PATCH 04/33] Fix alignment of the chevrons --- packages/ui/booker/DatePicker.tsx | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/ui/booker/DatePicker.tsx b/packages/ui/booker/DatePicker.tsx index ec18d53fc1100c..fdf6a396003872 100644 --- a/packages/ui/booker/DatePicker.tsx +++ b/packages/ui/booker/DatePicker.tsx @@ -96,26 +96,28 @@ const DatePicker = ({ weekStart = 0, className, locale, date, ...passThroughProp return (
-
+
{new Date(new Date().setMonth(month)).toLocaleString(locale, { month: "long" })} {" "} {new Date(new Date().setMonth(month)).getFullYear()} - - +
+ + +
{weekdayNames(locale, weekStart, "short").map((weekDay) => ( From a84b5cb47dea24a532f1eecc298f7a14c0695c12 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Sat, 11 Jun 2022 18:04:55 +0100 Subject: [PATCH 05/33] Added isToday dot, added onMonthChange so we can fetch this month slots --- .../web/components/booking/AvailableTimes.tsx | 10 ++-- .../booking/pages/AvailabilityPage.tsx | 19 +++---- apps/web/server/routers/viewer/slots.tsx | 8 +-- packages/ui/booker/DatePicker.tsx | 52 ++++++++++++++----- 4 files changed, 58 insertions(+), 31 deletions(-) diff --git a/apps/web/components/booking/AvailableTimes.tsx b/apps/web/components/booking/AvailableTimes.tsx index d1915b3112441e..f9f5c08cc69a71 100644 --- a/apps/web/components/booking/AvailableTimes.tsx +++ b/apps/web/components/booking/AvailableTimes.tsx @@ -30,7 +30,7 @@ type AvailableTimesProps = { }[]; schedulingType: SchedulingType | null; seatsPerTimeSlot?: number | null; - slots?: []; + slots?: { time: string }[]; }; const AvailableTimes: FC = ({ @@ -43,6 +43,8 @@ const AvailableTimes: FC = ({ schedulingType, seatsPerTimeSlot, }) => { + console.log(slots); + const { t, i18n } = useLocale(); const router = useRouter(); const { rescheduleUid } = router.query; @@ -75,7 +77,7 @@ const AvailableTimes: FC = ({ pathname: "book", query: { ...router.query, - date: slot.time.format(), + date: dayjs(slot.time).format(), type: eventTypeId, slug: eventTypeSlug, /** Treat as recurring only when a count exist and it's not a rescheduling workflow */ @@ -97,7 +99,7 @@ const AvailableTimes: FC = ({ } return ( -
+
{/* Current there is no way to disable Next.js Links */} {seatsPerTimeSlot && slot.attendees && slot.attendees >= seatsPerTimeSlot ? (
= ({ "text-primary-500 mb-2 block rounded-sm border bg-white py-4 font-medium opacity-25 dark:border-transparent dark:bg-gray-600 dark:text-neutral-200 ", brand === "#fff" || brand === "#ffffff" ? "border-brandcontrast" : "border-brand" )}> - {slot.time.format(timeFormat)} + {dayjs(slot.time).format(timeFormat)} {!!seatsPerTimeSlot &&

{t("booking_full")}

}
) : ( diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index f336b52b621aa1..f2b7a451b3fbd7 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -147,20 +147,13 @@ const SlotPicker = ({ weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6; }) => { const { selectedDate, changeDate } = useDateSelected({ timeZone }); - const startTime = - selectedDate || - (() => { - const d = new Date(); - d.setMinutes(Math.ceil(new Date().getMinutes() / 5) * 5, 0, 0); - return d; - })(); + + const [startDate, setStartDate] = useState(new Date()); const slots = useSlots({ eventTypeId: eventTypeId, - startTime: dayjs(startTime).toDate(), - endTime: dayjs(startTime) - .endOf(selectedDate ? "day" : "month") - .toDate(), + startTime: startDate, + endTime: dayjs(startDate).endOf("month").toDate(), }); return ( @@ -174,8 +167,9 @@ const SlotPicker = ({ } locale={"en"} excludedDates={Object.keys(slots).filter((k) => slots[k].length === 0)} - date={selectedDate?.toDate()} + selected={selectedDate?.toDate()} onChange={(date) => changeDate(dayjs(date))} + onMonthChange={setStartDate} weekStart={weekStart} /> @@ -183,6 +177,7 @@ const SlotPicker = ({ {selectedDate && ( = {}; + const slots: Record = {}; let time = dayjs(startTime); do { @@ -149,7 +149,9 @@ export const slotsRouter = createRouter().query("getSchedule", { workingHours, minimumBookingNotice: eventType.minimumBookingNotice, frequency: eventType.slotInterval || eventType.length, - }); + }).map((slot) => ({ + time: slot, + })); time = time.add(1, "day"); } while (time.isBefore(endTime)); diff --git a/packages/ui/booker/DatePicker.tsx b/packages/ui/booker/DatePicker.tsx index fdf6a396003872..053207a9feaa7b 100644 --- a/packages/ui/booker/DatePicker.tsx +++ b/packages/ui/booker/DatePicker.tsx @@ -1,17 +1,23 @@ import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid"; +import dayjs from "dayjs"; +import isToday from "dayjs/plugin/isToday"; import { useState } from "react"; import classNames from "@calcom/lib/classNames"; import { yyyymmdd, daysInMonth } from "@calcom/lib/date-fns"; import { weekdayNames } from "@calcom/lib/weekday"; +dayjs.extend(isToday); + export type DatePickerProps = { // which day of the week to render the calendar. Usually Sunday (=0) or Monday (=1) - default: Sunday weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6; // Fires whenever a selected date is changed. onChange: (date: Date) => void; + // Fires when the month is changed. + onMonthChange?: (date: Date) => void; // which date is currently selected (not tracked from here) - date?: Date; + selected?: Date; // defaults to current date. minDate?: Date; // Furthest date selectable in the future, default = UNLIMITED @@ -24,7 +30,11 @@ export type DatePickerProps = { className?: string; }; -const Day = ({ checked, children, ...props }: JSX.IntrinsicElements["button"] & { checked: boolean }) => { +const Day = ({ + date, + active, + ...props +}: JSX.IntrinsicElements["button"] & { active: boolean; date: Date }) => { return ( ); }; @@ -51,7 +62,7 @@ const Days = ({ excludedDates = [], browsingDate, weekStart, - date, + selected, ...props }: Omit & { browsingDate: Date; @@ -79,11 +90,11 @@ const Days = ({
) : ( props.onChange(day)} disabled={excludedDates.includes(yyyymmdd(day)) || day < minDate} - checked={date ? yyyymmdd(date) === yyyymmdd(day) : false}> - {day.getDate()} - + active={selected ? yyyymmdd(selected) === yyyymmdd(day) : false} + /> )}
))} @@ -91,8 +102,24 @@ const Days = ({ ); }; -const DatePicker = ({ weekStart = 0, className, locale, date, ...passThroughProps }: DatePickerProps) => { - const [month, setMonth] = useState(date ? date.getMonth() : new Date().getMonth()); +const DatePicker = ({ + weekStart = 0, + className, + locale, + selected, + onMonthChange, + ...passThroughProps +}: DatePickerProps) => { + const [month, setMonth] = useState(selected ? selected.getMonth() : new Date().getMonth()); + + const changeMonth = (newMonth: number) => { + setMonth(newMonth); + if (onMonthChange) { + const d = new Date(); + d.setMonth(newMonth); + onMonthChange(d); + } + }; return (
@@ -105,7 +132,7 @@ const DatePicker = ({ weekStart = 0, className, locale, date, ...passThroughProp
-
@@ -130,6 +157,7 @@ const DatePicker = ({ weekStart = 0, className, locale, date, ...passThroughProp
From 3dc0ed1d6404582c001db39c7f50e7237d7ae192 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Mon, 13 Jun 2022 18:29:30 +0100 Subject: [PATCH 06/33] Added includedDates to inverse excludedDates --- apps/web/components/booking/AvailableTimes.tsx | 2 -- packages/ui/booker/DatePicker.tsx | 18 +++++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/web/components/booking/AvailableTimes.tsx b/apps/web/components/booking/AvailableTimes.tsx index f9f5c08cc69a71..00adddca3e115c 100644 --- a/apps/web/components/booking/AvailableTimes.tsx +++ b/apps/web/components/booking/AvailableTimes.tsx @@ -43,8 +43,6 @@ const AvailableTimes: FC = ({ schedulingType, seatsPerTimeSlot, }) => { - console.log(slots); - const { t, i18n } = useLocale(); const router = useRouter(); const { rescheduleUid } = router.query; diff --git a/packages/ui/booker/DatePicker.tsx b/packages/ui/booker/DatePicker.tsx index 053207a9feaa7b..9adf69acb77e78 100644 --- a/packages/ui/booker/DatePicker.tsx +++ b/packages/ui/booker/DatePicker.tsx @@ -26,6 +26,8 @@ export type DatePickerProps = { locale: string; // Defaults to [], which dates are not bookable. Array of valid dates like: ["2022-04-23", "2022-04-24"] excludedDates?: string[]; + // defaults to all, which dates are bookable (inverse of excludedDates) + includedDates?: string[]; // allows adding classes to the container className?: string; }; @@ -60,6 +62,7 @@ const Day = ({ const Days = ({ minDate = new Date(), excludedDates = [], + includedDates = [], browsingDate, weekStart, selected, @@ -92,7 +95,11 @@ const Days = ({ props.onChange(day)} - disabled={excludedDates.includes(yyyymmdd(day)) || day < minDate} + disabled={ + !includedDates.includes(yyyymmdd(day)) || + excludedDates.includes(yyyymmdd(day)) || + day < minDate + } active={selected ? yyyymmdd(selected) === yyyymmdd(day) : false} /> )} @@ -116,7 +123,7 @@ const DatePicker = ({ setMonth(newMonth); if (onMonthChange) { const d = new Date(); - d.setMonth(newMonth); + d.setMonth(newMonth, 1); onMonthChange(d); } }; @@ -134,12 +141,13 @@ const DatePicker = ({
; export default function Type(props: AvailabilityPageProps) { - const data = { - ...props, - isDynamicGroup: false, - date: null, - eventType: { - id: 3, - title: "30min", - availability: [], - description: "", - length: 30, - price: 0, - currency: "usd", - periodType: "UNLIMITED", - periodStartDate: "Thu May 26 2022 15:23:21 GMT+0100 (British Summer Time)", - periodEndDate: "Thu May 26 2022 15:23:21 GMT+0100 (British Summer Time)", - periodDays: 30, - periodCountCalendarDays: false, - locations: [], - schedulingType: null, - recurringEvent: {}, - schedule: null, - hidden: false, - slug: "30min", - minimumBookingNotice: 120, - beforeEventBuffer: 0, - afterEventBuffer: 0, - timeZone: null, - metadata: {}, - slotInterval: null, - seatsPerTimeSlot: null, - users: [{ ...props.profile }], - }, - booking: null, - }; - const { t } = useLocale(); return props.away ? ( @@ -59,7 +29,7 @@ export default function Type(props: AvailabilityPageProps) {
- ) : data.isDynamicGroup && !data.profile.allowDynamicBooking ? ( + ) : props.isDynamicGroup && !props.profile.allowDynamicBooking ? (
@@ -75,284 +45,81 @@ export default function Type(props: AvailabilityPageProps) {
) : ( - + ); } -/*export const getServerSideProps = async (context: GetServerSidePropsContext) => { - const ssr = await ssrInit(context); +export const getStaticProps = async (context: GetStaticPropsContext) => { + const typeParam = asStringOrThrow(context.params?.type); + const userParam = asStringOrThrow(context.params?.user); - const usernameList = getUsernameList(context.query.user); - - const userParam = asStringOrNull(context.query.user); - const typeParam = asStringOrNull(context.query.type); - const dateParam = asStringOrNull(context.query.date); - const rescheduleUid = asStringOrNull(context.query.rescheduleUid); - - if (!userParam || !typeParam) { - throw new Error(`File is not named [type]/[user]`); - } - - const users = await prisma.user.findMany({ + const user = await prisma?.user.findUnique({ where: { - username: { - in: usernameList, - }, + username: userParam, }, select: { id: true, - username: true, - name: true, - email: true, - bio: true, - avatar: true, - startTime: true, - endTime: true, - timeZone: true, - weekStart: true, - availability: true, - hideBranding: true, - brandColor: true, - darkBrandColor: true, - defaultScheduleId: true, - allowDynamicBooking: true, away: true, - schedules: { - select: { - availability: true, - timeZone: true, - id: true, - }, - }, - theme: true, - plan: true, - eventTypes: { - where: { - AND: [ - { - slug: typeParam, - }, - { - teamId: null, - }, - ], - }, - select: { - ...availiblityPageEventTypeSelect, - users: { - select: { - id: false, - avatar: true, - name: true, - username: true, - hideBranding: true, - plan: true, - timeZone: true, - }, - }, - }, - }, }, }); - if (!users || !users.length) { + if (!user) { return { notFound: true, }; } - const [user] = users; //to be used when dealing with single user, not dynamic group - const isSingleUser = users.length === 1; - const isDynamicGroup = users.length > 1; - - if (isSingleUser && user.eventTypes.length !== 1) { - const eventTypeBackwardsCompat = await prisma.eventType.findFirst({ - where: { - AND: [ - { - userId: user.id, - }, - { - slug: typeParam, - }, - ], - }, - select: { - ...availiblityPageEventTypeSelect, - users: { - select: { - id: false, - avatar: true, - name: true, - username: true, - hideBranding: true, - plan: true, - timeZone: true, - }, - }, - }, - }); - if (!eventTypeBackwardsCompat) { - return { - notFound: true, - }; - } - - eventTypeBackwardsCompat.users.push({ - avatar: user.avatar, - name: user.name, - username: user.username, - hideBranding: user.hideBranding, - plan: user.plan, - timeZone: user.timeZone, - }); - - user.eventTypes.push(eventTypeBackwardsCompat); - } - - let [eventType] = user.eventTypes; - - if (isDynamicGroup) { - eventType = getDefaultEvent(typeParam); - eventType["users"] = users.map((user) => { - return { - avatar: user.avatar as string, - name: user.name as string, - username: user.username as string, - hideBranding: user.hideBranding, - plan: user.plan, - timeZone: user.timeZone as string, - }; - }); - } - // check this is the first event for free user - if (isSingleUser && user.plan === UserPlan.FREE) { - const firstEventType = await prisma.eventType.findFirst({ - where: { - OR: [ - { - userId: user.id, - }, - { - users: { - some: { - id: user.id, - }, - }, - }, - ], - }, - orderBy: [ + const eventType = await prisma?.eventType.findFirst({ + where: { + AND: [ { - position: "desc", + slug: typeParam, }, { - id: "asc", + userId: user?.id, }, ], - select: { - id: true, + }, + select: { + title: true, + recurringEvent: true, + length: true, + locations: true, + id: true, + description: true, + price: true, + currency: true, + metadata: true, + users: { + select: { + name: true, + username: true, + hideBranding: true, + plan: true, + }, }, - }); - if (firstEventType?.id !== eventType.id) { - return { - notFound: true, - } as const; - } - } - const locations = eventType.locations ? (eventType.locations as LocationObject[]) : []; - - const eventTypeObject = Object.assign({}, eventType, { - metadata: (eventType.metadata || {}) as JSONObject, - periodStartDate: eventType.periodStartDate?.toString() ?? null, - periodEndDate: eventType.periodEndDate?.toString() ?? null, - recurringEvent: parseRecurringEvent(eventType.recurringEvent), - locations: locationHiddenFilter(locations), - }); - - const schedule = eventType.schedule - ? { ...eventType.schedule } - : { - ...user.schedules.filter( - (schedule) => !user.defaultScheduleId || schedule.id === user.defaultScheduleId - )[0], - }; - - const timeZone = isDynamicGroup ? undefined : schedule.timeZone || eventType.timeZone || user.timeZone; - - const workingHours = getWorkingHours( - { - timeZone, }, - isDynamicGroup - ? eventType.availability || undefined - : schedule.availability || (eventType.availability.length ? eventType.availability : user.availability) - ); - eventTypeObject.schedule = null; - eventTypeObject.availability = []; + }); - let booking: GetBookingType | null = null; - if (rescheduleUid) { - booking = await getBooking(prisma, rescheduleUid); + if (!eventType) { + return { + notFound: true, + }; } - const dynamicNames = isDynamicGroup - ? users.map((user) => { - return user.name || ""; - }) - : []; - - const profile = isDynamicGroup - ? { - name: getGroupName(dynamicNames), - image: null, - slug: typeParam, - theme: null, - weekStart: "Sunday", - brandColor: "", - darkBrandColor: "", - allowDynamicBooking: !users.some((user) => { - return !user.allowDynamicBooking; - }), - } - : { - name: user.name || user.username, - image: user.avatar, - slug: user.username, - theme: user.theme, - weekStart: user.weekStart, - brandColor: user.brandColor, - darkBrandColor: user.darkBrandColor, - }; - - return { - props: { - away: user.away, - isDynamicGroup, - profile, - plan: user.plan, - date: dateParam, - eventType: eventTypeObject, - workingHours, - trpcState: ssr.dehydrate(), - booking, - }, - }; -};*/ - -export const getStaticProps = async () => { return { props: { + eventType: { + ...eventType, + metadata: eventType.metadata || {}, + recurringEvent: parseRecurringEvent(eventType.recurringEvent), + locations: locationHiddenFilter((eventType.locations || []) as LocationObject[]), + }, profile: { - name: "Pro Example", - username: "pro", - hideBranding: false, - plan: "PRO", - timeZone: "Europe/London", - image: "http://localhost:3000/pro/avatar.png", + ...eventType.users[0], + image: `http://localhost:3000/${eventType.users[0].username}/avatar.png`, }, - away: false, - eventTypeId: 1, + away: user?.away, }, }; }; From 2a50ac7319ad891ae5de2c31b5a4ffe30c083a95 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Mon, 13 Jun 2022 20:08:38 +0100 Subject: [PATCH 10/33] This builds the flat map so not all paths block on every new build --- apps/web/pages/[user]/[type].tsx | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index 6277f30e7f2cf8..c269f5fdfb97b9 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -5,6 +5,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent"; import { asStringOrThrow } from "@lib/asStringOrNull"; +import prisma from "@lib/prisma"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import AvailabilityPage from "@components/booking/pages/AvailabilityPage"; @@ -53,7 +54,7 @@ export const getStaticProps = async (context: GetStaticPropsContext) => { const typeParam = asStringOrThrow(context.params?.type); const userParam = asStringOrThrow(context.params?.user); - const user = await prisma?.user.findUnique({ + const user = await prisma.user.findUnique({ where: { username: userParam, }, @@ -69,7 +70,7 @@ export const getStaticProps = async (context: GetStaticPropsContext) => { }; } - const eventType = await prisma?.eventType.findFirst({ + const eventType = await prisma.eventType.findFirst({ where: { AND: [ { @@ -125,7 +126,23 @@ export const getStaticProps = async (context: GetStaticPropsContext) => { }; export const getStaticPaths = async () => { - const paths = ["/pro/30min"]; + const users = await prisma.user.findMany({ + select: { + username: true, + eventTypes: { + where: { + teamId: null, + }, + select: { + slug: true, + }, + }, + }, + }); + + const paths = users?.flatMap((user) => + user.eventTypes.flatMap((eventType) => `/${user.username}/${eventType.slug}`) + ); return { paths, fallback: "blocking" }; }; From 8cbcef98383fe88210278f13dda1759aa7cd7165 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Mon, 13 Jun 2022 20:36:48 +0100 Subject: [PATCH 11/33] Added requiresConfirmation --- apps/web/pages/[user]/[type].tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index c269f5fdfb97b9..60482638c4481c 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -90,6 +90,7 @@ export const getStaticProps = async (context: GetStaticPropsContext) => { description: true, price: true, currency: true, + requiresConfirmation: true, metadata: true, users: { select: { From 9a261c80f253470943977096335714d4e5f867e5 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Tue, 14 Jun 2022 14:54:31 +0100 Subject: [PATCH 12/33] Correctly take into account getFilteredTimes to make the calendar function --- .../web/components/booking/AvailableTimes.tsx | 12 +- .../booking/pages/AvailabilityPage.tsx | 23 ++- apps/web/pages/[user]/[type].tsx | 1 + apps/web/server/routers/viewer/slots.tsx | 144 +++++++++++++++--- 4 files changed, 143 insertions(+), 37 deletions(-) diff --git a/apps/web/components/booking/AvailableTimes.tsx b/apps/web/components/booking/AvailableTimes.tsx index 00adddca3e115c..27df8bfbfb827c 100644 --- a/apps/web/components/booking/AvailableTimes.tsx +++ b/apps/web/components/booking/AvailableTimes.tsx @@ -14,23 +14,21 @@ import { useSlots } from "@lib/hooks/useSlots"; import Loader from "@components/Loader"; +import { Slot } from "@server/routers/viewer/slots"; + type AvailableTimesProps = { timeFormat: string; - minimumBookingNotice: number; - beforeBufferTime: number; - afterBufferTime: number; eventTypeId: number; eventLength: number; recurringCount: number | undefined; eventTypeSlug: string; - slotInterval: number | null; date: Dayjs; users: { username: string | null; }[]; schedulingType: SchedulingType | null; seatsPerTimeSlot?: number | null; - slots?: { time: string }[]; + slots?: Slot[]; }; const AvailableTimes: FC = ({ @@ -105,7 +103,7 @@ const AvailableTimes: FC = ({ "text-primary-500 mb-2 block rounded-sm border bg-white py-4 font-medium opacity-25 dark:border-transparent dark:bg-gray-600 dark:text-neutral-200 ", brand === "#fff" || brand === "#ffffff" ? "border-brandcontrast" : "border-brand" )}> - {dayjs(slot.time).format(timeFormat)} + {dayjs(slot.time).tz(timeZone()).format(timeFormat)} {!!seatsPerTimeSlot &&

{t("booking_full")}

}
) : ( @@ -116,7 +114,7 @@ const AvailableTimes: FC = ({ brand === "#fff" || brand === "#ffffff" ? "border-brandcontrast" : "border-brand" )} data-testid="time"> - {dayjs.tz(slot.time, timeZone()).format(timeFormat)} + {dayjs(slot.time).tz(timeZone()).format(timeFormat)} {!!seatsPerTimeSlot && (

{ const { selectedDate, setSelectedDate } = useDateSelected({ timeZone }); - const [startDate, setStartDate] = useState( - selectedDate ? dayjs(selectedDate).startOf("month").toDate() : new Date() - ); + const [startDate, setStartDate] = useState(new Date()); + + useEffect(() => { + if (dayjs(selectedDate).startOf("month").isAfter(dayjs())) { + setStartDate(dayjs(selectedDate).startOf("month").toDate()); + } + }, [selectedDate]); const slots = useSlots({ eventTypeId: eventTypeId, @@ -160,7 +170,7 @@ const SlotPicker = ({ endTime: dayjs(startDate).endOf("month").toDate(), }); - const [times, setTimes] = useState<{ time: Dayjs }[]>([]); + const [times, setTimes] = useState([]); useEffect(() => { if (selectedDate && slots[yyyymmdd(selectedDate)]) { @@ -194,7 +204,8 @@ const SlotPicker = ({ timeFormat={timeFormat} eventTypeId={eventTypeId} eventTypeSlug={""} - recurringCount={0} + seatsPerTimeSlot={seatsPerTimeSlot} + recurringCount={recurringEventCount} users={[]} /> )} @@ -663,6 +674,8 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, booking }: P timezoneDropdown={timezoneDropdown} timeZone={timeZone} timeFormat={timeFormat} + seatsPerTimeSlot={eventType.seatsPerTimeSlot} + recurringEventCount={recurringEventCount} />

diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index 60482638c4481c..f024b367718b2d 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -92,6 +92,7 @@ export const getStaticProps = async (context: GetStaticPropsContext) => { currency: true, requiresConfirmation: true, metadata: true, + seatsPerTimeSlot: true, users: { select: { name: true, diff --git a/apps/web/server/routers/viewer/slots.tsx b/apps/web/server/routers/viewer/slots.tsx index 179ae82b3905ea..bf723eac0edad7 100644 --- a/apps/web/server/routers/viewer/slots.tsx +++ b/apps/web/server/routers/viewer/slots.tsx @@ -2,7 +2,6 @@ import dayjs, { Dayjs } from "dayjs"; import { z } from "zod"; import getBusyTimes from "@calcom/core/getBusyTimes"; -import { getFilteredTimes } from "@calcom/core/hooks/useSlots"; import { getWorkingHours } from "@calcom/lib/availability"; import { yyyymmdd } from "@calcom/lib/date-fns"; @@ -22,6 +21,81 @@ const getScheduleSchema = z.object({ additionalOrganizers: z.array(z.string()).optional(), }); +export type Slot = { + time: Dayjs; + attendees?: number; + bookingUid?: string; +}; + +type getFilteredTimesProps = { + times: dayjs.Dayjs[]; + busy: TimeRange[]; + eventLength: number; + beforeBufferTime: number; + afterBufferTime: number; + currentSeats?: CurrentSeats[]; +}; + +export const getFilteredTimes = (props: getFilteredTimesProps) => { + const { times, busy, eventLength, beforeBufferTime, afterBufferTime, currentSeats } = props; + const finalizationTime = times[times.length - 1]?.add(eventLength, "minutes"); + // Check for conflicts + for (let i = times.length - 1; i >= 0; i -= 1) { + // const totalSlotLength = eventLength + beforeBufferTime + afterBufferTime; + // Check if the slot surpasses the user's availability end time + const slotEndTimeWithAfterBuffer = times[i].add(eventLength + afterBufferTime, "minutes"); + if (slotEndTimeWithAfterBuffer.isAfter(finalizationTime, "minute")) { + times.splice(i, 1); + } else { + const slotStartTime = times[i]; + const slotEndTime = times[i].add(eventLength, "minutes"); + const slotStartTimeWithBeforeBuffer = times[i].subtract(beforeBufferTime, "minutes"); + // If the event has seats then see if there is already a booking (want to show full bookings as well) + if (currentSeats?.some((booking) => booking.startTime === slotStartTime.toISOString())) { + break; + } + busy.every((busyTime): boolean => { + const startTime = dayjs(busyTime.start); + const endTime = dayjs(busyTime.end); + // Check if start times are the same + if (slotStartTime.isBetween(startTime, endTime, null, "[)")) { + times.splice(i, 1); + } + // Check if slot end time is between start and end time + else if (slotEndTime.isBetween(startTime, endTime)) { + times.splice(i, 1); + } + // Check if startTime is between slot + else if (startTime.isBetween(slotStartTime, slotEndTime)) { + times.splice(i, 1); + } + // Check if timeslot has before buffer time space free + else if ( + slotStartTimeWithBeforeBuffer.isBetween( + startTime.subtract(beforeBufferTime, "minutes"), + endTime.add(afterBufferTime, "minutes") + ) + ) { + times.splice(i, 1); + } + // Check if timeslot has after buffer time space free + else if ( + slotEndTimeWithAfterBuffer.isBetween( + startTime.subtract(beforeBufferTime, "minutes"), + endTime.add(afterBufferTime, "minutes") + ) + ) { + times.splice(i, 1); + } else { + return true; + } + return false; + }); + } + } + return times; +}; + export const slotsRouter = createRouter().query("getSchedule", { input: getScheduleSchema, async resolve({ input, ctx }) { @@ -35,6 +109,8 @@ export const slotsRouter = createRouter().query("getSchedule", { seatsPerTimeSlot: true, timeZone: true, slotInterval: true, + beforeEventBuffer: true, + afterEventBuffer: true, schedule: { select: { availability: true, @@ -95,8 +171,6 @@ export const slotsRouter = createRouter().query("getSchedule", { end: dayjs(a.end).add(currentUser.bufferTime, "minute"), })); - console.log(bufferedBusyTimes); - const schedule = eventType?.schedule ? { ...eventType?.schedule } : { @@ -117,40 +191,60 @@ export const slotsRouter = createRouter().query("getSchedule", { /* Current logic is if a booking is in a time slot mark it as busy, but seats can have more than one attendee so grab current bookings with a seats event type and display them on the calendar, even if they are full */ - let currentSeats; - if (eventType?.seatsPerTimeSlot) { - currentSeats = await ctx.prisma.booking.findMany({ - where: { - eventTypeId: input.eventTypeId, - startTime: { - gte: startTime.format(), - lte: endTime.format(), + const currentSeats = eventType?.seatsPerTimeSlot + ? await ctx.prisma.booking.findMany({ + where: { + eventTypeId: input.eventTypeId, + startTime: { + gte: startTime.format(), + lte: endTime.format(), + }, }, - }, - select: { - uid: true, - startTime: true, - _count: { - select: { - attendees: true, + select: { + uid: true, + startTime: true, + _count: { + select: { + attendees: true, + }, }, }, - }, - }); - } + }) + : []; - const slots: Record = {}; + const slots: Record = {}; let time = dayjs(startTime); do { - slots[yyyymmdd(time.toDate())] = getSlots({ + const times = getSlots({ inviteeDate: time, eventLength: eventType.length, workingHours, minimumBookingNotice: eventType.minimumBookingNotice, frequency: eventType.slotInterval || eventType.length, - }).map((slot) => ({ - time: slot, + }); + const filterTimeProps = { + times, + busy: bufferedBusyTimes, + eventLength: eventType.length, + beforeBufferTime: eventType.beforeEventBuffer, + afterBufferTime: eventType.afterEventBuffer, + currentSeats, + }; + slots[yyyymmdd(time.toDate())] = getFilteredTimes(filterTimeProps).map((time) => ({ + time, + users: eventType.users, + // Conditionally add the attendees and booking id to slots object if there is already a booking during that time + ...(currentSeats?.some((booking) => booking.startTime.toISOString() === time.toISOString()) && { + attendees: + currentSeats[ + currentSeats.findIndex((booking) => booking.startTime.toISOString() === time.toISOString()) + ]._count.attendees, + bookingUid: + currentSeats[ + currentSeats.findIndex((booking) => booking.startTime.toISOString() === time.toISOString()) + ].uid, + }), })); time = time.add(1, "day"); } while (time.isBefore(endTime)); From 772561410bc610a92bc10655bc4f8401ce23f75d Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 00:23:39 +0100 Subject: [PATCH 13/33] Rewritten team availability, seems to work --- apps/web/pages/[user]/[type].tsx | 114 ++++++++++- apps/web/server/routers/viewer/slots.tsx | 239 +++++++++++++---------- 2 files changed, 249 insertions(+), 104 deletions(-) diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index f024b367718b2d..1106ce90a825a8 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -1,8 +1,10 @@ import { GetStaticPropsContext } from "next"; import { locationHiddenFilter, LocationObject } from "@calcom/app-store/locations"; +import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent"; +import { availiblityPageEventTypeSelect } from "@calcom/prisma/selects"; import { asStringOrThrow } from "@lib/asStringOrNull"; import prisma from "@lib/prisma"; @@ -50,13 +52,10 @@ export default function Type(props: AvailabilityPageProps) { ); } -export const getStaticProps = async (context: GetStaticPropsContext) => { - const typeParam = asStringOrThrow(context.params?.type); - const userParam = asStringOrThrow(context.params?.user); - +async function getUserPageProps({ username, slug }: { username: string; slug: string }) { const user = await prisma.user.findUnique({ where: { - username: userParam, + username, }, select: { id: true, @@ -74,7 +73,7 @@ export const getStaticProps = async (context: GetStaticPropsContext) => { where: { AND: [ { - slug: typeParam, + slug, }, { userId: user?.id, @@ -125,6 +124,109 @@ export const getStaticProps = async (context: GetStaticPropsContext) => { away: user?.away, }, }; +} + +async function getDynamicGroupPageProps({ + usernameList, + length, +}: { + usernameList: string[]; + length: number; +}) { + const eventType = getDefaultEvent("" + length); + + const users = await prisma.user.findMany({ + where: { + username: { + in: usernameList, + }, + }, + select: { + id: true, + username: true, + name: true, + email: true, + bio: true, + avatar: true, + startTime: true, + endTime: true, + timeZone: true, + weekStart: true, + availability: true, + hideBranding: true, + brandColor: true, + darkBrandColor: true, + defaultScheduleId: true, + allowDynamicBooking: true, + away: true, + schedules: { + select: { + availability: true, + timeZone: true, + id: true, + }, + }, + theme: true, + plan: true, + }, + }); + + if (!users || !users.length) { + return { + notFound: true, + }; + } + + eventType.users = users.map((user) => { + return { + image: `http://localhost:3000/${user.username}/avatar.png`, + name: user.name as string, + username: user.username as string, + hideBranding: user.hideBranding, + plan: user.plan, + timeZone: user.timeZone as string, + }; + }); + + const dynamicNames = users.map((user) => { + return user.name || ""; + }); + + const profile = { + name: getGroupName(dynamicNames), + image: null, + slug: length, + theme: null, + weekStart: "Sunday", + brandColor: "", + darkBrandColor: "", + allowDynamicBooking: !users.some((user) => { + return !user.allowDynamicBooking; + }), + }; + + return { + props: { + eventType, + profile, + }, + }; +} + +export const getStaticProps = async (context: GetStaticPropsContext) => { + const typeParam = asStringOrThrow(context.params?.type); + const userParam = asStringOrThrow(context.params?.user); + + // dynamic groups are not generated at build time, but otherwise are probably cached until infinity. + const isDynamicGroup = userParam.includes("+"); + if (isDynamicGroup) { + return await getDynamicGroupPageProps({ + usernameList: getUsernameList(userParam), + length: parseInt(typeParam), + }); + } else { + return await getUserPageProps({ username: userParam, slug: typeParam }); + } }; export const getStaticPaths = async () => { diff --git a/apps/web/server/routers/viewer/slots.tsx b/apps/web/server/routers/viewer/slots.tsx index bf723eac0edad7..96991b2770b2a0 100644 --- a/apps/web/server/routers/viewer/slots.tsx +++ b/apps/web/server/routers/viewer/slots.tsx @@ -1,11 +1,14 @@ +import { SchedulingType } from "@prisma/client"; import dayjs, { Dayjs } from "dayjs"; import { z } from "zod"; import getBusyTimes from "@calcom/core/getBusyTimes"; import { getWorkingHours } from "@calcom/lib/availability"; import { yyyymmdd } from "@calcom/lib/date-fns"; +import { TimeRange, WorkingHours } from "@calcom/types/schedule"; import getSlots from "@lib/slots"; +import { CurrentSeats } from "@lib/types/schedule"; import { createRouter } from "@server/createRouter"; import { TRPCError } from "@trpc/server"; @@ -16,84 +19,99 @@ const getScheduleSchema = z.object({ // endTime ISOString endTime: z.string(), // Event type ID - eventTypeId: z.number(), - // Additional organizers for dynamic events. - additionalOrganizers: z.array(z.string()).optional(), + eventTypeId: z.number().optional(), + // or list of users (for dynamic events) + usernameList: z.array(z.string()).optional(), }); export type Slot = { - time: Dayjs; + time: string; attendees?: number; bookingUid?: string; + users?: string; }; -type getFilteredTimesProps = { - times: dayjs.Dayjs[]; +const checkForAvailability = ({ + time, + busy, + workingHours, + eventLength, + beforeBufferTime, + afterBufferTime, + currentSeats, +}: { + time: Dayjs; busy: TimeRange[]; + workingHours: WorkingHours[]; eventLength: number; beforeBufferTime: number; afterBufferTime: number; currentSeats?: CurrentSeats[]; -}; - -export const getFilteredTimes = (props: getFilteredTimesProps) => { - const { times, busy, eventLength, beforeBufferTime, afterBufferTime, currentSeats } = props; - const finalizationTime = times[times.length - 1]?.add(eventLength, "minutes"); - // Check for conflicts - for (let i = times.length - 1; i >= 0; i -= 1) { - // const totalSlotLength = eventLength + beforeBufferTime + afterBufferTime; - // Check if the slot surpasses the user's availability end time - const slotEndTimeWithAfterBuffer = times[i].add(eventLength + afterBufferTime, "minutes"); - if (slotEndTimeWithAfterBuffer.isAfter(finalizationTime, "minute")) { - times.splice(i, 1); - } else { - const slotStartTime = times[i]; - const slotEndTime = times[i].add(eventLength, "minutes"); - const slotStartTimeWithBeforeBuffer = times[i].subtract(beforeBufferTime, "minutes"); - // If the event has seats then see if there is already a booking (want to show full bookings as well) - if (currentSeats?.some((booking) => booking.startTime === slotStartTime.toISOString())) { - break; +}) => { + if ( + !workingHours.every((workingHour) => { + if (!workingHour.days.includes(time.day())) { + return false; } - busy.every((busyTime): boolean => { - const startTime = dayjs(busyTime.start); - const endTime = dayjs(busyTime.end); - // Check if start times are the same - if (slotStartTime.isBetween(startTime, endTime, null, "[)")) { - times.splice(i, 1); - } - // Check if slot end time is between start and end time - else if (slotEndTime.isBetween(startTime, endTime)) { - times.splice(i, 1); - } - // Check if startTime is between slot - else if (startTime.isBetween(slotStartTime, slotEndTime)) { - times.splice(i, 1); - } - // Check if timeslot has before buffer time space free - else if ( - slotStartTimeWithBeforeBuffer.isBetween( - startTime.subtract(beforeBufferTime, "minutes"), - endTime.add(afterBufferTime, "minutes") - ) - ) { - times.splice(i, 1); - } - // Check if timeslot has after buffer time space free - else if ( - slotEndTimeWithAfterBuffer.isBetween( - startTime.subtract(beforeBufferTime, "minutes"), - endTime.add(afterBufferTime, "minutes") - ) - ) { - times.splice(i, 1); - } else { - return true; - } + if ( + !time.isBetween( + time.utc().startOf("day").add(workingHour.startTime, "minutes"), + time.utc().startOf("day").add(workingHour.endTime, "minutes"), + null, + "[)" + ) + ) { return false; - }); - } + } + return true; + }) + ) { + return false; + } + + if (currentSeats?.some((booking) => booking.startTime.toISOString() === time.toISOString())) { + return true; } - return times; + + const slotEndTime = time.add(eventLength, "minutes"); + const slotStartTimeWithBeforeBuffer = time.subtract(beforeBufferTime, "minutes"); + const slotEndTimeWithAfterBuffer = time.add(eventLength + afterBufferTime, "minutes"); + + return busy.every((busyTime): boolean => { + const startTime = dayjs(busyTime.start); + const endTime = dayjs(busyTime.end); + // Check if start times are the same + if (time.isBetween(startTime, endTime, null, "[)")) { + return false; + } + // Check if slot end time is between start and end time + else if (slotEndTime.isBetween(startTime, endTime)) { + return false; + } + // Check if startTime is between slot + else if (startTime.isBetween(time, slotEndTime)) { + return false; + } + // Check if timeslot has before buffer time space free + else if ( + slotStartTimeWithBeforeBuffer.isBetween( + startTime.subtract(beforeBufferTime, "minutes"), + endTime.add(afterBufferTime, "minutes") + ) + ) { + return false; + } + // Check if timeslot has after buffer time space free + else if ( + slotEndTimeWithAfterBuffer.isBetween( + startTime.subtract(beforeBufferTime, "minutes"), + endTime.add(afterBufferTime, "minutes") + ) + ) { + return false; + } + return true; + }); }; export const slotsRouter = createRouter().query("getSchedule", { @@ -111,6 +129,7 @@ export const slotsRouter = createRouter().query("getSchedule", { slotInterval: true, beforeEventBuffer: true, afterEventBuffer: true, + schedulingType: true, schedule: { select: { availability: true, @@ -127,6 +146,7 @@ export const slotsRouter = createRouter().query("getSchedule", { users: { select: { id: true, + username: true, bufferTime: true, schedules: { select: { @@ -149,48 +169,56 @@ export const slotsRouter = createRouter().query("getSchedule", { throw new TRPCError({ code: "NOT_FOUND" }); } - const currentUser = eventType.users[0]; - const startTime = dayjs(input.startTime); const endTime = dayjs(input.endTime); if (!startTime.isValid() || !endTime.isValid()) { throw new TRPCError({ message: "Invalid time range given.", code: "BAD_REQUEST" }); } - const busyTimes = await getBusyTimes({ - credentials: currentUser.credentials, - startTime: startTime.format(), - endTime: endTime.format(), - eventTypeId: input.eventTypeId, - userId: currentUser.id, - selectedCalendars: currentUser.selectedCalendars, - }); + const userSchedules = await Promise.all( + eventType.users.map(async (currentUser) => { + const schedule = currentUser.schedules.filter( + (schedule) => !currentUser.defaultScheduleId || schedule.id === currentUser.defaultScheduleId + )[0]; + + const workingHours = getWorkingHours( + { + timeZone: currentUser.timeZone, + }, + schedule.availability || currentUser.availability + ); - const bufferedBusyTimes = busyTimes.map((a) => ({ - start: dayjs(a.start).subtract(currentUser.bufferTime, "minute"), - end: dayjs(a.end).add(currentUser.bufferTime, "minute"), - })); + const busy = await getBusyTimes({ + credentials: currentUser.credentials, + startTime: startTime.format(), + endTime: endTime.format(), + eventTypeId: input.eventTypeId, + userId: currentUser.id, + selectedCalendars: currentUser.selectedCalendars, + }); - const schedule = eventType?.schedule - ? { ...eventType?.schedule } - : { - ...currentUser.schedules.filter( - (schedule) => !currentUser.defaultScheduleId || schedule.id === currentUser.defaultScheduleId - )[0], + return { + workingHours, + busy: busy.map((a) => ({ + start: dayjs(a.start).subtract(currentUser.bufferTime, "minute").toDate(), + end: dayjs(a.end).add(currentUser.bufferTime, "minute").toDate(), + })), }; + }) + ); - const timeZone = schedule.timeZone || eventType?.timeZone || currentUser.timeZone; + const schedule = eventType?.schedule; + const timeZone = schedule?.timeZone || eventType?.timeZone || undefined; const workingHours = getWorkingHours( { timeZone, }, - schedule.availability || - (eventType?.availability.length ? eventType.availability : currentUser.availability) + schedule?.availability || (eventType?.availability.length ? eventType.availability : []) ); /* Current logic is if a booking is in a time slot mark it as busy, but seats can have more than one attendee so grab - current bookings with a seats event type and display them on the calendar, even if they are full */ + current bookings with a seats event type and display them on the calendar, even if they are full */ const currentSeats = eventType?.seatsPerTimeSlot ? await ctx.prisma.booking.findMany({ where: { @@ -214,8 +242,16 @@ export const slotsRouter = createRouter().query("getSchedule", { const slots: Record = {}; + const availabilityCheckProps = { + eventLength: eventType.length, + beforeBufferTime: eventType.beforeEventBuffer, + afterBufferTime: eventType.afterEventBuffer, + currentSeats, + }; + let time = dayjs(startTime); do { + // get slots retrieves the available times for a given day const times = getSlots({ inviteeDate: time, eventLength: eventType.length, @@ -223,17 +259,24 @@ export const slotsRouter = createRouter().query("getSchedule", { minimumBookingNotice: eventType.minimumBookingNotice, frequency: eventType.slotInterval || eventType.length, }); - const filterTimeProps = { - times, - busy: bufferedBusyTimes, - eventLength: eventType.length, - beforeBufferTime: eventType.beforeEventBuffer, - afterBufferTime: eventType.afterEventBuffer, - currentSeats, - }; - slots[yyyymmdd(time.toDate())] = getFilteredTimes(filterTimeProps).map((time) => ({ - time, - users: eventType.users, + + // if ROUND_ROBIN - slots stay available on some() - if normal / COLLECTIVE - slots only stay available on every() + const filteredTimes = + !eventType.schedulingType || eventType.schedulingType === SchedulingType.COLLECTIVE + ? times.filter((time) => + userSchedules.every((schedule) => + checkForAvailability({ time, ...schedule, ...availabilityCheckProps }) + ) + ) + : times.filter((time) => + userSchedules.some((schedule) => + checkForAvailability({ time, ...schedule, ...availabilityCheckProps }) + ) + ); + + slots[yyyymmdd(time.toDate())] = filteredTimes.map((time) => ({ + time: time.toISOString(), + users: eventType.users.map((user) => user.username || ""), // Conditionally add the attendees and booking id to slots object if there is already a booking during that time ...(currentSeats?.some((booking) => booking.startTime.toISOString() === time.toISOString()) && { attendees: From 2f4df1837dc530cea7fe07b139b2e11964070110 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 10:20:10 +0100 Subject: [PATCH 14/33] Circumvent i18n flicker by showing the loader instead --- apps/web/components/booking/pages/AvailabilityPage.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index de2930ce3342e5..d5e360b45c0bd8 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -38,6 +38,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import { getRecurringFreq } from "@calcom/lib/recurringStrings"; import { localStorage } from "@calcom/lib/webstorage"; import { Frequency } from "@calcom/prisma/zod-utils"; +import Loader from "@calcom/ui/Loader"; import DatePicker from "@calcom/ui/booker/DatePicker"; import { asStringOrNull } from "@lib/asStringOrNull"; @@ -362,6 +363,10 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, booking }: P ? "max-w-4xl" : "max-w-3xl"; + if (Object.keys(i18n).length === 0) { + return ; + } + const timezoneDropdown = ( ); From 33c888f616b2875c271e968fae3cc9f7ed3ba727 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 10:30:49 +0100 Subject: [PATCH 15/33] 'You can remove this code. Its not being used now' - Hariom --- apps/web/components/booking/pages/AvailabilityPage.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index d5e360b45c0bd8..6608ff105864c8 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -337,10 +337,6 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, booking }: P } }, [contracts, eventType.metadata.smartContractAddress, eventType.users, router]); - if (isAvailableTimesVisible) { - // Let iframe take the width available due to increase in max-width - sdkActionManager?.fire("__refreshWidth", {}); - } const [recurringEventCount, setRecurringEventCount] = useState(eventType.recurringEvent?.count); const telemetry = useTelemetry(); From 33abde4f1f1eb4862c0355126d8fc27a608a7dcb Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 11:03:54 +0100 Subject: [PATCH 16/33] Nailed a persistent little bug, new Date() caused the current day to flicker on and off --- packages/ui/booker/DatePicker.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/ui/booker/DatePicker.tsx b/packages/ui/booker/DatePicker.tsx index 9adf69acb77e78..ca753058de7874 100644 --- a/packages/ui/booker/DatePicker.tsx +++ b/packages/ui/booker/DatePicker.tsx @@ -1,7 +1,7 @@ import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid"; import dayjs from "dayjs"; import isToday from "dayjs/plugin/isToday"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import classNames from "@calcom/lib/classNames"; import { yyyymmdd, daysInMonth } from "@calcom/lib/date-fns"; @@ -60,7 +60,7 @@ const Day = ({ }; const Days = ({ - minDate = new Date(), + minDate, excludedDates = [], includedDates = [], browsingDate, @@ -73,6 +73,10 @@ const Days = ({ }) => { // Create placeholder elements for empty days in first week const weekdayOfFirst = new Date(new Date(browsingDate).setDate(1)).getDay(); + // memoize to prevent a flicker on redraw on the current day + const minDateValueOf = useMemo(() => { + return minDate?.valueOf() || new Date().valueOf(); + }, [minDate]); const days: (Date | null)[] = Array((weekdayOfFirst - weekStart + 7) % 7).fill(null); for (let day = 1, dayCount = daysInMonth(browsingDate); day <= dayCount; day++) { @@ -98,7 +102,7 @@ const Days = ({ disabled={ !includedDates.includes(yyyymmdd(day)) || excludedDates.includes(yyyymmdd(day)) || - day < minDate + day.valueOf() < minDateValueOf } active={selected ? yyyymmdd(selected) === yyyymmdd(day) : false} /> From a0e4e3519bd8ea3f6d5c6d699602607cf0678e9e Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 11:04:43 +0100 Subject: [PATCH 17/33] TS fixes --- apps/web/components/booking/pages/AvailabilityPage.tsx | 4 ++-- apps/web/pages/[user]/[type].tsx | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 6608ff105864c8..9a74bee74639d9 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -654,7 +654,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, booking }: P {timezoneDropdown} - + {booking?.startTime && rescheduleUid && (
@@ -675,7 +675,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, booking }: P timezoneDropdown={timezoneDropdown} timeZone={timeZone} timeFormat={timeFormat} - seatsPerTimeSlot={eventType.seatsPerTimeSlot} + seatsPerTimeSlot={eventType.seatsPerTimeSlot || undefined} recurringEventCount={recurringEventCount} />
diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index 1106ce90a825a8..79ab5c54ff526a 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -4,7 +4,6 @@ import { locationHiddenFilter, LocationObject } from "@calcom/app-store/location import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent"; -import { availiblityPageEventTypeSelect } from "@calcom/prisma/selects"; import { asStringOrThrow } from "@lib/asStringOrNull"; import prisma from "@lib/prisma"; @@ -82,6 +81,7 @@ async function getUserPageProps({ username, slug }: { username: string; slug: st }, select: { title: true, + slug: true, recurringEvent: true, length: true, locations: true, @@ -119,6 +119,7 @@ async function getUserPageProps({ username, slug }: { username: string; slug: st }, profile: { ...eventType.users[0], + slug: `${eventType.users[0].username}/${eventType.slug}`, image: `http://localhost:3000/${eventType.users[0].username}/avatar.png`, }, away: user?.away, @@ -195,7 +196,7 @@ async function getDynamicGroupPageProps({ const profile = { name: getGroupName(dynamicNames), image: null, - slug: length, + slug: "" + length, theme: null, weekStart: "Sunday", brandColor: "", From 459cfd21bea94504d06e138683fe26c8ec070789 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 11:37:48 +0100 Subject: [PATCH 18/33] Fix some eventType details in AvailableTimes --- apps/web/components/booking/AvailableTimes.tsx | 5 ----- .../components/booking/pages/AvailabilityPage.tsx | 14 ++++++++------ apps/web/pages/[user]/[type].tsx | 1 + 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/apps/web/components/booking/AvailableTimes.tsx b/apps/web/components/booking/AvailableTimes.tsx index 27df8bfbfb827c..e218104be0917c 100644 --- a/apps/web/components/booking/AvailableTimes.tsx +++ b/apps/web/components/booking/AvailableTimes.tsx @@ -1,4 +1,3 @@ -import { ExclamationIcon } from "@heroicons/react/solid"; import { SchedulingType } from "@prisma/client"; import dayjs, { Dayjs } from "dayjs"; import Link from "next/link"; @@ -10,16 +9,12 @@ import { nameOfDay } from "@calcom/lib/weekday"; import classNames from "@lib/classNames"; import { timeZone } from "@lib/clock"; import { useLocale } from "@lib/hooks/useLocale"; -import { useSlots } from "@lib/hooks/useSlots"; - -import Loader from "@components/Loader"; import { Slot } from "@server/routers/viewer/slots"; type AvailableTimesProps = { timeFormat: string; eventTypeId: number; - eventLength: number; recurringCount: number | undefined; eventTypeSlug: string; date: Dayjs; diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 9a74bee74639d9..a144a5aca16e4f 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -13,6 +13,7 @@ import { RefreshIcon, VideoCameraIcon, } from "@heroicons/react/solid"; +import { EventType } from "@prisma/client"; import * as Collapsible from "@radix-ui/react-collapsible"; import { useContracts } from "contexts/contractsContext"; import dayjs, { Dayjs } from "dayjs"; @@ -139,7 +140,7 @@ const useSlots = ({ }; const SlotPicker = ({ - eventTypeId, + eventType, timezoneDropdown, timeFormat, timeZone, @@ -147,7 +148,7 @@ const SlotPicker = ({ seatsPerTimeSlot, weekStart = 0, }: { - eventTypeId: number; + eventType: Pick; timezoneDropdown: JSX.Element; timeFormat: string; timeZone?: string; @@ -166,7 +167,7 @@ const SlotPicker = ({ }, [selectedDate]); const slots = useSlots({ - eventTypeId: eventTypeId, + eventTypeId: eventType.id, startTime: startDate, endTime: dayjs(startDate).endOf("month").toDate(), }); @@ -203,10 +204,11 @@ const SlotPicker = ({ slots={times} date={dayjs(selectedDate)} timeFormat={timeFormat} - eventTypeId={eventTypeId} - eventTypeSlug={""} + eventTypeId={eventType.id} + eventTypeSlug={eventType.slug} seatsPerTimeSlot={seatsPerTimeSlot} recurringCount={recurringEventCount} + schedulingType={eventType.schedulingType} users={[]} /> )} @@ -671,7 +673,7 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, booking }: P )} Date: Wed, 15 Jun 2022 12:00:25 +0100 Subject: [PATCH 19/33] '5 / 6 Seats Available' instead of '6 / Seats Available' --- apps/web/components/booking/AvailableTimes.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/components/booking/AvailableTimes.tsx b/apps/web/components/booking/AvailableTimes.tsx index e218104be0917c..a64cd4048a85a6 100644 --- a/apps/web/components/booking/AvailableTimes.tsx +++ b/apps/web/components/booking/AvailableTimes.tsx @@ -120,7 +120,7 @@ const AvailableTimes: FC = ({ : "text-emerald-400" } text-sm`}> {slot.attendees ? seatsPerTimeSlot - slot.attendees : seatsPerTimeSlot} /{" "} - {!!seatsPerTimeSlot} {t("seats_available")} + {seatsPerTimeSlot} {t("seats_available")}

)} From 488ffcf19494921665f92caad13f1e6b6480db29 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 13:00:39 +0100 Subject: [PATCH 20/33] More type fixes --- apps/web/components/booking/pages/AvailabilityPage.tsx | 7 +++++-- apps/web/pages/[user]/[type].tsx | 9 ++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index a144a5aca16e4f..4c21ebeedd0d2d 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -310,7 +310,7 @@ const useDateSelected = ({ timeZone }: { timeZone?: string }) => { return { selectedDate, setSelectedDate }; }; -const AvailabilityPage = ({ profile, plan, eventType, workingHours, booking }: Props) => { +const AvailabilityPage = ({ profile, eventType, booking }: Props) => { const router = useRouter(); const isEmbed = useIsEmbed(); const { rescheduleUid } = router.query; @@ -330,7 +330,10 @@ const AvailabilityPage = ({ profile, plan, eventType, workingHours, booking }: P setIsAvailableTimesVisible(!!router.query.date); }, [router.query.date]); - useExposePlanGlobally(plan); + // TODO: Improve this; + useExposePlanGlobally(eventType.users.length === 1 ? eventType.users[0].plan : "PRO"); + + // TODO: this needs to be extracted elsewhere useEffect(() => { if (eventType.metadata.smartContractAddress) { const eventOwner = eventType.users[0]; diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index ac393cff0f1f60..46e71478949940 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -31,7 +31,7 @@ export default function Type(props: AvailabilityPageProps) { - ) : props.isDynamicGroup && !props.profile.allowDynamicBooking ? ( + ) : props.isDynamic && !props.profile.allowDynamicBooking ? (
@@ -98,7 +98,11 @@ async function getUserPageProps({ username, slug }: { username: string; slug: st name: true, username: true, hideBranding: true, + brandColor: true, + darkBrandColor: true, + theme: true, plan: true, + allowDynamicBooking: true, }, }, }, @@ -124,6 +128,7 @@ async function getUserPageProps({ username, slug }: { username: string; slug: st image: `http://localhost:3000/${eventType.users[0].username}/avatar.png`, }, away: user?.away, + isDynamic: false, }, }; } @@ -211,6 +216,8 @@ async function getDynamicGroupPageProps({ props: { eventType, profile, + isDynamic: true, + away: false, }, }; } From a9e1114466fbee5a28a5a07caa1799fb65ce40a0 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 13:11:19 +0100 Subject: [PATCH 21/33] Removed unrelated merge artifact --- packages/ui/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ui/package.json b/packages/ui/package.json index a9e10bb0943eb9..ca5e6daf6ad626 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -5,7 +5,6 @@ "types": "./index.tsx", "license": "MIT", "dependencies": { - "@heroicons/react": "^1.0.6", "@radix-ui/react-dialog": "^0.1.0", "next": "^12.1.6" }, From caabf3a5fb838d5f38e264e5f5b406fd169191e6 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 15:13:16 +0100 Subject: [PATCH 22/33] Use WEBAPP_URL instead of hardcoded --- apps/web/pages/[user]/[type].tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index 851b9d5de814a8..221464af5cd9f9 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -1,6 +1,7 @@ import { GetStaticPropsContext } from "next"; import { locationHiddenFilter, LocationObject } from "@calcom/app-store/locations"; +import { 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"; @@ -135,7 +136,7 @@ async function getUserPageProps({ username, slug }: { username: string; slug: st profile: { ...eventType.users[0], slug: `${eventType.users[0].username}/${eventType.slug}`, - image: `http://localhost:3000/${eventType.users[0].username}/avatar.png`, + image: `${WEBAPP_URL}/${eventType.users[0].username}/avatar.png`, }, away: user?.away, isDynamic: false, @@ -196,7 +197,7 @@ async function getDynamicGroupPageProps({ eventType.users = users.map((user) => { return { - image: `http://localhost:3000/${user.username}/avatar.png`, + image: `${WEBAPP_URL}/${user.username}/avatar.png`, name: user.name as string, username: user.username as string, hideBranding: user.hideBranding, From a989f53eadf918550da88d470fc28f147d305b2f Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 16:06:23 +0100 Subject: [PATCH 23/33] Next round of TS fixes --- .../booking/pages/AvailabilityPage.tsx | 19 ++----- apps/web/pages/[user]/[type].tsx | 53 +++++++++++++------ apps/web/server/routers/viewer/slots.tsx | 6 +-- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 4c21ebeedd0d2d..0d19513a9d17c3 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -298,19 +298,10 @@ const useDateSelected = ({ timeZone }: { timeZone?: string }) => { _setSelectedDate(newDate); }; - /*useEffect(() => { - if ( - selectedDate != null && - selectedDate?.utcOffset() !== selectedDate.clone().utcOffset(0).tz(timeZone).utcOffset() - ) { - changeDate(selectedDate.tz(timeZone, true)); - } - }, [selectedDate, changeDate, timeZone]);*/ - return { selectedDate, setSelectedDate }; }; -const AvailabilityPage = ({ profile, eventType, booking }: Props) => { +const AvailabilityPage = ({ profile, eventType }: Props) => { const router = useRouter(); const isEmbed = useIsEmbed(); const { rescheduleUid } = router.query; @@ -516,7 +507,7 @@ const AvailabilityPage = ({ profile, eventType, booking }: Props) => { {timezoneDropdown}
- {booking?.startTime && rescheduleUid && ( + {/* Temp disabled booking?.startTime && rescheduleUid && (

{ parseDate(dayjs(booking.startTime), i18n)}

- )} + )*/}
@@ -661,7 +652,7 @@ const AvailabilityPage = ({ profile, eventType, booking }: Props) => { - {booking?.startTime && rescheduleUid && ( + {/* Temporarily disabled - booking?.startTime && rescheduleUid && (

{ {typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}

- )} + )*/} { + return { + name: user.name, + username: user.username, + hideBranding: user.hideBranding, + plan: user.plan, + timeZone: user.timeZone, + }; + }), + }); + return { props: { - eventType: { - ...eventType, - metadata: eventType.metadata || {}, - recurringEvent: parseRecurringEvent(eventType.recurringEvent), - locations: locationHiddenFilter((eventType.locations || []) as LocationObject[]), - }, + eventType: eventTypeObject, profile: { ...eventType.users[0], slug: `${eventType.users[0].username}/${eventType.slug}`, @@ -195,15 +210,21 @@ async function getDynamicGroupPageProps({ }; } - eventType.users = users.map((user) => { - return { - image: `${WEBAPP_URL}/${user.username}/avatar.png`, - name: user.name as string, - username: user.username as string, - hideBranding: user.hideBranding, - plan: user.plan, - timeZone: user.timeZone as string, - }; + const locations = eventType.locations ? (eventType.locations as LocationObject[]) : []; + + const eventTypeObject = Object.assign({}, eventType, { + metadata: (eventType.metadata || {}) as JSONObject, + recurringEvent: parseRecurringEvent(eventType.recurringEvent), + locations: locationHiddenFilter(locations), + users: users.map((user) => { + return { + name: user.name, + username: user.username, + hideBranding: user.hideBranding, + plan: user.plan, + timeZone: user.timeZone, + }; + }), }); const dynamicNames = users.map((user) => { @@ -225,7 +246,7 @@ async function getDynamicGroupPageProps({ return { props: { - eventType, + eventType: eventTypeObject, profile, isDynamic: true, away: false, diff --git a/apps/web/server/routers/viewer/slots.tsx b/apps/web/server/routers/viewer/slots.tsx index 96991b2770b2a0..23eae7d23d3408 100644 --- a/apps/web/server/routers/viewer/slots.tsx +++ b/apps/web/server/routers/viewer/slots.tsx @@ -19,16 +19,16 @@ const getScheduleSchema = z.object({ // endTime ISOString endTime: z.string(), // Event type ID - eventTypeId: z.number().optional(), + eventTypeId: z.number(), // or list of users (for dynamic events) - usernameList: z.array(z.string()).optional(), + // usernameList: z.array(z.string()).optional(), }); export type Slot = { time: string; attendees?: number; bookingUid?: string; - users?: string; + users?: string[]; }; const checkForAvailability = ({ From 671f530040274f3995446f9847c7836227c6e8d0 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 16:19:28 +0100 Subject: [PATCH 24/33] I believe this was mistyped --- apps/web/lib/types/schedule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/lib/types/schedule.ts b/apps/web/lib/types/schedule.ts index 43537957f3f246..3a04a85cd31fed 100644 --- a/apps/web/lib/types/schedule.ts +++ b/apps/web/lib/types/schedule.ts @@ -19,7 +19,7 @@ export type WorkingHours = { export type CurrentSeats = { uid: string; - startTime: string; + startTime: Date; _count: { attendees: number; }; From 79575a1a736c654938d77edc9bd9f32c96b91778 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 16:22:58 +0100 Subject: [PATCH 25/33] Temporarily disabled rescheduling 'this is when you originally scheduled', so removed dep --- apps/web/components/booking/pages/AvailabilityPage.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 0d19513a9d17c3..76ded51bf81506 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -47,7 +47,6 @@ import { timeZone } from "@lib/clock"; import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally"; import useTheme from "@lib/hooks/useTheme"; import { isBrandingHidden } from "@lib/isBrandingHidden"; -import { parseDate } from "@lib/parseDate"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@lib/telemetry"; import { detectBrowserTimeFormat } from "@lib/timeFormat"; import { trpc } from "@lib/trpc"; From cba7e0e8b683c440bc2282681bba4a9f8dc22e10 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 16:29:11 +0100 Subject: [PATCH 26/33] Sorting some dead code --- apps/web/lib/hooks/useSlots.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/lib/hooks/useSlots.ts b/apps/web/lib/hooks/useSlots.ts index d2ea3d0bc54f8c..5e5cd63c5d28ce 100644 --- a/apps/web/lib/hooks/useSlots.ts +++ b/apps/web/lib/hooks/useSlots.ts @@ -61,7 +61,7 @@ export const getFilteredTimes = (props: getFilteredTimesProps) => { const slotEndTime = times[i].add(eventLength, "minutes"); const slotStartTimeWithBeforeBuffer = times[i].subtract(beforeBufferTime, "minutes"); // If the event has seats then see if there is already a booking (want to show full bookings as well) - if (currentSeats?.some((booking) => booking.startTime === slotStartTime.toISOString())) { + if (currentSeats?.some((booking) => booking.startTime === slotStartTime.toDate())) { break; } busy.every((busyTime): boolean => { @@ -155,12 +155,12 @@ export const useSlots = (props: UseSlotsProps) => { time, users: [user], // Conditionally add the attendees and booking id to slots object if there is already a booking during that time - ...(currentSeats?.some((booking) => booking.startTime === time.toISOString()) && { + ...(currentSeats?.some((booking) => booking.startTime === time.toDate()) && { attendees: - currentSeats[currentSeats.findIndex((booking) => booking.startTime === time.toISOString())]._count + currentSeats[currentSeats.findIndex((booking) => booking.startTime === time.toDate())]._count .attendees, bookingUid: - currentSeats[currentSeats.findIndex((booking) => booking.startTime === time.toISOString())].uid, + currentSeats[currentSeats.findIndex((booking) => booking.startTime === time.toDate())].uid, }), })); }; From 41bf12931738e848c7df39324298262635fc6023 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 16:54:09 +0100 Subject: [PATCH 27/33] This page has a lot of red, not all related to this PR --- apps/web/pages/d/[link]/[slug].tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/pages/d/[link]/[slug].tsx b/apps/web/pages/d/[link]/[slug].tsx index f5591695e3831a..d6401163c61b8d 100644 --- a/apps/web/pages/d/[link]/[slug].tsx +++ b/apps/web/pages/d/[link]/[slug].tsx @@ -1,4 +1,3 @@ -import { Prisma } from "@prisma/client"; import { GetServerSidePropsContext } from "next"; import { JSONObject } from "superjson/dist/types"; @@ -19,6 +18,8 @@ import { ssrInit } from "@server/lib/ssr"; export type AvailabilityPageProps = inferSSRProps; export default function Type(props: AvailabilityPageProps) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore return ; } From 9d6bf9fa1ebece233e20977384550904933f4b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20L=C3=B3pez?= Date: Wed, 15 Jun 2022 13:33:18 -0600 Subject: [PATCH 28/33] A PR to your PR (#3067) * Cleanup * Cleanup * Uses zod to parse params * Type fixes * Fixes ISR * E2E fixes * Disabled dynamic bookings until post v1.7 * More test fixes --- .../web/components/booking/AvailableTimes.tsx | 6 +- .../booking/pages/AvailabilityPage.tsx | 22 ++- apps/web/lib/hooks/useSlots.ts | 8 +- apps/web/lib/slots.ts | 12 +- apps/web/lib/types/schedule.ts | 8 -- apps/web/pages/[user].tsx | 118 ++++++++--------- apps/web/pages/[user]/[type].tsx | 111 ++++++++-------- apps/web/pages/api/book/event.ts | 1 + apps/web/pages/d/[link]/[slug].tsx | 20 +-- apps/web/pages/team/[slug]/[type].tsx | 2 +- apps/web/pages/team/[slug]/book.tsx | 2 +- .../playwright/dynamic-booking-pages.test.ts | 1 + apps/web/playwright/reschedule.test.ts | 1 + apps/web/server/routers/viewer/slots.tsx | 125 ++++++------------ packages/core/getUserAvailability.ts | 60 +++++---- 15 files changed, 222 insertions(+), 275 deletions(-) diff --git a/apps/web/components/booking/AvailableTimes.tsx b/apps/web/components/booking/AvailableTimes.tsx index a64cd4048a85a6..68a0bfa73471cb 100644 --- a/apps/web/components/booking/AvailableTimes.tsx +++ b/apps/web/components/booking/AvailableTimes.tsx @@ -2,15 +2,15 @@ import { SchedulingType } from "@prisma/client"; import dayjs, { Dayjs } from "dayjs"; import Link from "next/link"; import { useRouter } from "next/router"; -import React, { FC, useEffect, useState } from "react"; +import { FC, useEffect, useState } from "react"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { nameOfDay } from "@calcom/lib/weekday"; import classNames from "@lib/classNames"; import { timeZone } from "@lib/clock"; -import { useLocale } from "@lib/hooks/useLocale"; -import { Slot } from "@server/routers/viewer/slots"; +import type { Slot } from "@server/routers/viewer/slots"; type AvailableTimesProps = { timeFormat: string; diff --git a/apps/web/components/booking/pages/AvailabilityPage.tsx b/apps/web/components/booking/pages/AvailabilityPage.tsx index 76ded51bf81506..14865ada5f4db2 100644 --- a/apps/web/components/booking/pages/AvailabilityPage.tsx +++ b/apps/web/components/booking/pages/AvailabilityPage.tsx @@ -1,36 +1,34 @@ // Get router variables import { ArrowLeftIcon, - CalendarIcon, ChevronDownIcon, ChevronUpIcon, + ClipboardCheckIcon, ClockIcon, CreditCardIcon, GlobeIcon, InformationCircleIcon, LocationMarkerIcon, - ClipboardCheckIcon, RefreshIcon, VideoCameraIcon, } from "@heroicons/react/solid"; import { EventType } from "@prisma/client"; import * as Collapsible from "@radix-ui/react-collapsible"; import { useContracts } from "contexts/contractsContext"; -import dayjs, { Dayjs } from "dayjs"; +import dayjs from "dayjs"; import customParseFormat from "dayjs/plugin/customParseFormat"; import utc from "dayjs/plugin/utc"; import { TFunction } from "next-i18next"; import { useRouter } from "next/router"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useEffect, useState } from "react"; import { FormattedNumber, IntlProvider } from "react-intl"; import { AppStoreLocationType, LocationObject, LocationType } from "@calcom/app-store/locations"; import { + useEmbedNonStylesConfig, useEmbedStyles, - useIsEmbed, useIsBackgroundTransparent, - sdkActionManager, - useEmbedNonStylesConfig, + useIsEmbed, } from "@calcom/embed-core/embed-iframe"; import classNames from "@calcom/lib/classNames"; import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants"; @@ -38,7 +36,6 @@ import { yyyymmdd } from "@calcom/lib/date-fns"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { getRecurringFreq } from "@calcom/lib/recurringStrings"; import { localStorage } from "@calcom/lib/webstorage"; -import { Frequency } from "@calcom/prisma/zod-utils"; import Loader from "@calcom/ui/Loader"; import DatePicker from "@calcom/ui/booker/DatePicker"; @@ -58,15 +55,16 @@ import { HeadSeo } from "@components/seo/head-seo"; import AvatarGroup from "@components/ui/AvatarGroup"; import PoweredByCal from "@components/ui/PoweredByCal"; -import { Slot } from "@server/routers/viewer/slots"; +import type { Slot } from "@server/routers/viewer/slots"; -import { AvailabilityPageProps } from "../../../pages/[user]/[type]"; -import { AvailabilityTeamPageProps } from "../../../pages/team/[slug]/[type]"; +import type { AvailabilityPageProps } from "../../../pages/[user]/[type]"; +import type { DynamicAvailabilityPageProps } from "../../../pages/d/[link]/[slug]"; +import type { AvailabilityTeamPageProps } from "../../../pages/team/[slug]/[type]"; dayjs.extend(utc); dayjs.extend(customParseFormat); -type Props = AvailabilityTeamPageProps | AvailabilityPageProps; +type Props = AvailabilityTeamPageProps | AvailabilityPageProps | DynamicAvailabilityPageProps; export const locationKeyToString = (location: LocationObject, t: TFunction) => { switch (location.type) { diff --git a/apps/web/lib/hooks/useSlots.ts b/apps/web/lib/hooks/useSlots.ts index 5e5cd63c5d28ce..9119ba1a3925b4 100644 --- a/apps/web/lib/hooks/useSlots.ts +++ b/apps/web/lib/hooks/useSlots.ts @@ -5,8 +5,10 @@ import utc from "dayjs/plugin/utc"; import { stringify } from "querystring"; import { useEffect, useState } from "react"; +import type { CurrentSeats } from "@calcom/core/getUserAvailability"; + import getSlots from "@lib/slots"; -import { CurrentSeats, TimeRange, WorkingHours } from "@lib/types/schedule"; +import type { TimeRange, WorkingHours } from "@lib/types/schedule"; dayjs.extend(isBetween); dayjs.extend(utc); @@ -15,7 +17,7 @@ type AvailabilityUserResponse = { busy: TimeRange[]; timeZone: string; workingHours: WorkingHours[]; - currentSeats?: CurrentSeats[]; + currentSeats?: CurrentSeats; }; type Slot = { @@ -43,7 +45,7 @@ type getFilteredTimesProps = { eventLength: number; beforeBufferTime: number; afterBufferTime: number; - currentSeats?: CurrentSeats[]; + currentSeats?: CurrentSeats; }; export const getFilteredTimes = (props: getFilteredTimesProps) => { diff --git a/apps/web/lib/slots.ts b/apps/web/lib/slots.ts index fb8ac14bedcbda..f7011edf47b57e 100644 --- a/apps/web/lib/slots.ts +++ b/apps/web/lib/slots.ts @@ -4,7 +4,7 @@ import isToday from "dayjs/plugin/isToday"; import utc from "dayjs/plugin/utc"; import { getWorkingHours } from "./availability"; -import { WorkingHours, CurrentSeats } from "./types/schedule"; +import { WorkingHours } from "./types/schedule"; dayjs.extend(isToday); dayjs.extend(utc); @@ -16,7 +16,6 @@ export type GetSlots = { workingHours: WorkingHours[]; minimumBookingNotice: number; eventLength: number; - currentSeats?: CurrentSeats[]; }; export type WorkingHoursTimeFrame = { startTime: number; endTime: number }; @@ -43,14 +42,7 @@ const splitAvailableTime = ( return result; }; -const getSlots = ({ - inviteeDate, - frequency, - minimumBookingNotice, - workingHours, - eventLength, - currentSeats, -}: GetSlots) => { +const getSlots = ({ inviteeDate, frequency, minimumBookingNotice, workingHours, eventLength }: GetSlots) => { // current date in invitee tz const startDate = dayjs().add(minimumBookingNotice, "minute"); const startOfDay = dayjs.utc().startOf("day"); diff --git a/apps/web/lib/types/schedule.ts b/apps/web/lib/types/schedule.ts index 3a04a85cd31fed..ba5e74b45e41df 100644 --- a/apps/web/lib/types/schedule.ts +++ b/apps/web/lib/types/schedule.ts @@ -16,11 +16,3 @@ export type WorkingHours = { startTime: number; endTime: number; }; - -export type CurrentSeats = { - uid: string; - startTime: Date; - _count: { - attendees: number; - }; -}; diff --git a/apps/web/pages/[user].tsx b/apps/web/pages/[user].tsx index 3572bbc1f53366..424b8b728265df 100644 --- a/apps/web/pages/[user].tsx +++ b/apps/web/pages/[user].tsx @@ -46,71 +46,53 @@ interface EvtsToVerify { } export default function User(props: inferSSRProps) { - const { users, profile } = props; + const { users, profile, eventTypes, isDynamicGroup, dynamicNames, dynamicUsernames, isSingleUser } = props; const [user] = users; //To be used when we only have a single user, not dynamic group const { Theme } = useTheme(user.theme); const { t } = useLocale(); const router = useRouter(); - const isSingleUser = props.users.length === 1; - const isDynamicGroup = props.users.length > 1; - const dynamicNames = isDynamicGroup - ? props.users.map((user) => { - return user.name || ""; - }) - : []; - const dynamicUsernames = isDynamicGroup - ? props.users.map((user) => { - return user.username || ""; - }) - : []; - const eventTypes = isDynamicGroup - ? defaultEvents.map((event) => { - event.description = getDynamicEventDescription(dynamicUsernames, event.slug); - return event; - }) - : props.eventTypes; - const groupEventTypes = props.users.some((user) => { - return !user.allowDynamicBooking; - }) ? ( -
-
-
-

{" " + t("unavailable")}

-

{t("user_dynamic_booking_disabled") as string}

+ + const groupEventTypes = + /* props.users.some((user) => !user.allowDynamicBooking) TODO: Re-enable after v1.7 launch */ true ? ( +
+
+
+

{" " + t("unavailable")}

+

{t("user_dynamic_booking_disabled") as string}

+
-
- ) : ( - - ); + ) : ( + + ); const isEmbed = useIsEmbed(); const eventTypeListItemEmbedStyles = useEmbedStyles("eventTypeListItem"); const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left"; @@ -376,6 +358,13 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => : false, })); + const isSingleUser = users.length === 1; + const dynamicUsernames = isDynamicGroup + ? users.map((user) => { + return user.username || ""; + }) + : []; + return { props: { users, @@ -383,8 +372,17 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => user: { emailMd5: crypto.createHash("md5").update(user.email).digest("hex"), }, - eventTypes, + eventTypes: isDynamicGroup + ? defaultEvents.map((event) => { + event.description = getDynamicEventDescription(dynamicUsernames, event.slug); + return event; + }) + : eventTypes, trpcState: ssr.dehydrate(), + isDynamicGroup, + dynamicNames, + dynamicUsernames, + isSingleUser, }, }; }; diff --git a/apps/web/pages/[user]/[type].tsx b/apps/web/pages/[user]/[type].tsx index f9c811603dba30..cccd187d7ea9e5 100644 --- a/apps/web/pages/[user]/[type].tsx +++ b/apps/web/pages/[user]/[type].tsx @@ -1,6 +1,7 @@ -import { EventType, User, UserPlan } from "@prisma/client"; +import { UserPlan } from "@prisma/client"; import { GetStaticPropsContext } from "next"; import { JSONObject } from "superjson/dist/types"; +import { z } from "zod"; import { locationHiddenFilter, LocationObject } from "@calcom/app-store/locations"; import { WEBAPP_URL } from "@calcom/lib/constants"; @@ -8,7 +9,6 @@ import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defa import { useLocale } from "@calcom/lib/hooks/useLocale"; import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent"; -import { asStringOrThrow } from "@lib/asStringOrNull"; import prisma from "@lib/prisma"; import { inferSSRProps } from "@lib/types/inferSSRProps"; @@ -34,7 +34,7 @@ export default function Type(props: AvailabilityPageProps) {
- ) : props.isDynamic && !props.profile.allowDynamicBooking ? ( + ) : props.isDynamic /* && !props.profile.allowDynamicBooking TODO: Re-enable after v1.7 launch */ ? (
@@ -62,6 +62,47 @@ async function getUserPageProps({ username, slug }: { username: string; slug: st select: { id: true, away: true, + plan: true, + eventTypes: { + // Order is important to ensure that given a slug if there are duplicates, we choose the same event type consistently when showing in event-types list UI(in terms of ordering and disabled event types) + // TODO: If we can ensure that there are no duplicates for a [slug, userId] combination in existing data, this requirement might be avoided. + orderBy: [ + { + position: "desc", + }, + { + id: "asc", + }, + ], + select: { + title: true, + slug: true, + recurringEvent: true, + length: true, + locations: true, + id: true, + description: true, + price: true, + currency: true, + requiresConfirmation: true, + schedulingType: true, + metadata: true, + seatsPerTimeSlot: true, + users: { + select: { + name: true, + username: true, + hideBranding: true, + brandColor: true, + darkBrandColor: true, + theme: true, + plan: true, + allowDynamicBooking: true, + timeZone: true, + }, + }, + }, + }, }, }); @@ -71,56 +112,9 @@ async function getUserPageProps({ username, slug }: { username: string; slug: st }; } - const eventType = await prisma.eventType.findFirst({ - where: { - AND: [ - { - slug, - }, - { - userId: user?.id, - }, - ], - }, - // Order is important to ensure that given a slug if there are duplicates, we choose the same event type consistently when showing in event-types list UI(in terms of ordering and disabled event types) - // TODO: If we can ensure that there are no duplicates for a [slug, userId] combination in existing data, this requirement might be avoided. - orderBy: [ - { - position: "desc", - }, - { - id: "asc", - }, - ], - select: { - title: true, - slug: true, - recurringEvent: true, - length: true, - locations: true, - id: true, - description: true, - price: true, - currency: true, - requiresConfirmation: true, - schedulingType: true, - metadata: true, - seatsPerTimeSlot: true, - users: { - select: { - name: true, - username: true, - hideBranding: true, - brandColor: true, - darkBrandColor: true, - theme: true, - plan: true, - allowDynamicBooking: true, - timeZone: true, - }, - }, - }, - }); + const eventType = user.eventTypes.find((et, i) => + user.plan === UserPlan.FREE ? i === 0 && et.slug === slug : et.slug === slug + ); if (!eventType) { return { @@ -156,6 +150,7 @@ async function getUserPageProps({ username, slug }: { username: string; slug: st away: user?.away, isDynamic: false, }, + revalidate: 10, // seconds }; } @@ -204,7 +199,7 @@ async function getDynamicGroupPageProps({ }, }); - if (!users || !users.length) { + if (!users.length) { return { notFound: true, }; @@ -235,7 +230,7 @@ async function getDynamicGroupPageProps({ name: getGroupName(dynamicNames), image: null, slug: "" + length, - theme: null, + theme: null as string | null, weekStart: "Sunday", brandColor: "", darkBrandColor: "", @@ -251,12 +246,14 @@ async function getDynamicGroupPageProps({ isDynamic: true, away: false, }, + revalidate: 10, // seconds }; } +const paramsSchema = z.object({ type: z.string(), user: z.string() }); + export const getStaticProps = async (context: GetStaticPropsContext) => { - const typeParam = asStringOrThrow(context.params?.type); - const userParam = asStringOrThrow(context.params?.user); + const { type: typeParam, user: userParam } = paramsSchema.parse(context.params); // dynamic groups are not generated at build time, but otherwise are probably cached until infinity. const isDynamicGroup = userParam.includes("+"); diff --git a/apps/web/pages/api/book/event.ts b/apps/web/pages/api/book/event.ts index ba2f2828a05af6..ec22a0e0dbe33f 100644 --- a/apps/web/pages/api/book/event.ts +++ b/apps/web/pages/api/book/event.ts @@ -159,6 +159,7 @@ const getEventTypesFromDB = async (eventTypeId: number) => { id: eventTypeId, }, select: { + id: true, users: userSelect, team: { select: { diff --git a/apps/web/pages/d/[link]/[slug].tsx b/apps/web/pages/d/[link]/[slug].tsx index d6401163c61b8d..9ddd5d942ea344 100644 --- a/apps/web/pages/d/[link]/[slug].tsx +++ b/apps/web/pages/d/[link]/[slug].tsx @@ -1,10 +1,10 @@ import { GetServerSidePropsContext } from "next"; import { JSONObject } from "superjson/dist/types"; +import { z } from "zod"; import { parseRecurringEvent } from "@calcom/lib"; import { availiblityPageEventTypeSelect } from "@calcom/prisma"; -import { asStringOrNull } from "@lib/asStringOrNull"; import { getWorkingHours } from "@lib/availability"; import { GetBookingType } from "@lib/getBooking"; import { locationHiddenFilter, LocationObject } from "@lib/location"; @@ -15,19 +15,21 @@ import AvailabilityPage from "@components/booking/pages/AvailabilityPage"; import { ssrInit } from "@server/lib/ssr"; -export type AvailabilityPageProps = inferSSRProps; +export type DynamicAvailabilityPageProps = inferSSRProps; -export default function Type(props: AvailabilityPageProps) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore +export default function Type(props: DynamicAvailabilityPageProps) { return ; } +const querySchema = z.object({ + link: z.string().optional().default(""), + slug: z.string().optional().default(""), + date: z.union([z.string(), z.null()]).optional().default(null), +}); + export const getServerSideProps = async (context: GetServerSidePropsContext) => { const ssr = await ssrInit(context); - const link = asStringOrNull(context.query.link) || ""; - const slug = asStringOrNull(context.query.slug) || ""; - const dateParam = asStringOrNull(context.query.date); + const { link, slug, date } = querySchema.parse(context.query); const hashedLink = await prisma.hashedLink.findUnique({ where: { @@ -141,7 +143,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => isDynamicGroup: false, profile, plan: user.plan, - date: dateParam, + date, eventType: eventTypeObject, workingHours, trpcState: ssr.dehydrate(), diff --git a/apps/web/pages/team/[slug]/[type].tsx b/apps/web/pages/team/[slug]/[type].tsx index 4fae6c83eadcbd..d1e0e2d566e756 100644 --- a/apps/web/pages/team/[slug]/[type].tsx +++ b/apps/web/pages/team/[slug]/[type].tsx @@ -135,7 +135,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => name: team.name || team.slug, slug: team.slug, image: team.logo, - theme: null, + theme: null as string | null, weekStart: "Sunday", brandColor: "" /* TODO: Add a way to set a brand color for Teams */, darkBrandColor: "" /* TODO: Add a way to set a brand color for Teams */, diff --git a/apps/web/pages/team/[slug]/book.tsx b/apps/web/pages/team/[slug]/book.tsx index d09e26bfde218a..5ea220205a77fc 100644 --- a/apps/web/pages/team/[slug]/book.tsx +++ b/apps/web/pages/team/[slug]/book.tsx @@ -109,7 +109,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { // FIXME: This slug is used as username on success page which is wrong. This is correctly set as username for user booking. slug: "team/" + eventTypeObject.slug, image: eventTypeObject.team?.logo || null, - theme: null /* Teams don't have a theme, and `BookingPage` uses it */, + theme: null as string | null /* Teams don't have a theme, and `BookingPage` uses it */, brandColor: null /* Teams don't have a brandColor, and `BookingPage` uses it */, darkBrandColor: null /* Teams don't have a darkBrandColor, and `BookingPage` uses it */, eventName: null, diff --git a/apps/web/playwright/dynamic-booking-pages.test.ts b/apps/web/playwright/dynamic-booking-pages.test.ts index b57b5cb3b970e6..8dc7861582f42e 100644 --- a/apps/web/playwright/dynamic-booking-pages.test.ts +++ b/apps/web/playwright/dynamic-booking-pages.test.ts @@ -11,6 +11,7 @@ import { test.describe.configure({ mode: "parallel" }); test.describe("dynamic booking", () => { + test.skip(true, "TODO: Re-enable after v1.7 launch"); test.beforeEach(async ({ page, users }) => { const pro = await users.create(); await pro.login(); diff --git a/apps/web/playwright/reschedule.test.ts b/apps/web/playwright/reschedule.test.ts index 15ef383890811c..17aacec459ec82 100644 --- a/apps/web/playwright/reschedule.test.ts +++ b/apps/web/playwright/reschedule.test.ts @@ -46,6 +46,7 @@ test.describe("Reschedule Tests", async () => { }); test("Should display former time when rescheduling availability", async ({ page, users, bookings }) => { + test.skip(true, "TODO: Re-enable after v1.7 launch"); const user = await users.create(); const booking = await bookings.create(user.id, user.username, user.eventTypes[0].id!, { status: BookingStatus.CANCELLED, diff --git a/apps/web/server/routers/viewer/slots.tsx b/apps/web/server/routers/viewer/slots.tsx index 23eae7d23d3408..e9469d713a8702 100644 --- a/apps/web/server/routers/viewer/slots.tsx +++ b/apps/web/server/routers/viewer/slots.tsx @@ -2,27 +2,33 @@ import { SchedulingType } from "@prisma/client"; import dayjs, { Dayjs } from "dayjs"; import { z } from "zod"; -import getBusyTimes from "@calcom/core/getBusyTimes"; -import { getWorkingHours } from "@calcom/lib/availability"; +import type { CurrentSeats } from "@calcom/core/getUserAvailability"; +import { getUserAvailability } from "@calcom/core/getUserAvailability"; import { yyyymmdd } from "@calcom/lib/date-fns"; +import { availabilityUserSelect } from "@calcom/prisma"; +import { stringToDayjs } from "@calcom/prisma/zod-utils"; import { TimeRange, WorkingHours } from "@calcom/types/schedule"; import getSlots from "@lib/slots"; -import { CurrentSeats } from "@lib/types/schedule"; import { createRouter } from "@server/createRouter"; import { TRPCError } from "@trpc/server"; -const getScheduleSchema = z.object({ - // startTime ISOString - startTime: z.string(), - // endTime ISOString - endTime: z.string(), - // Event type ID - eventTypeId: z.number(), - // or list of users (for dynamic events) - // usernameList: z.array(z.string()).optional(), -}); +const getScheduleSchema = z + .object({ + // startTime ISOString + startTime: stringToDayjs, + // endTime ISOString + endTime: stringToDayjs, + // Event type ID + eventTypeId: z.number().optional(), + // or list of users (for dynamic events) + usernameList: z.array(z.string()).optional(), + }) + .refine( + (data) => !!data.eventTypeId || !!data.usernameList, + "Either usernameList or eventTypeId should be filled in." + ); export type Slot = { time: string; @@ -41,12 +47,12 @@ const checkForAvailability = ({ currentSeats, }: { time: Dayjs; - busy: TimeRange[]; + busy: (TimeRange | { start: string; end: string })[]; workingHours: WorkingHours[]; eventLength: number; beforeBufferTime: number; afterBufferTime: number; - currentSeats?: CurrentSeats[]; + currentSeats?: CurrentSeats; }) => { if ( !workingHours.every((workingHour) => { @@ -122,6 +128,7 @@ export const slotsRouter = createRouter().query("getSchedule", { id: input.eventTypeId, }, select: { + id: true, minimumBookingNotice: true, length: true, seatsPerTimeSlot: true, @@ -145,21 +152,8 @@ export const slotsRouter = createRouter().query("getSchedule", { }, users: { select: { - id: true, username: true, - bufferTime: true, - schedules: { - select: { - id: true, - availability: true, - timeZone: true, - }, - }, - selectedCalendars: true, - credentials: true, - defaultScheduleId: true, - availability: true, - timeZone: true, + ...availabilityUserSelect, }, }, }, @@ -169,76 +163,39 @@ export const slotsRouter = createRouter().query("getSchedule", { throw new TRPCError({ code: "NOT_FOUND" }); } - const startTime = dayjs(input.startTime); - const endTime = dayjs(input.endTime); + const { startTime, endTime } = input; if (!startTime.isValid() || !endTime.isValid()) { throw new TRPCError({ message: "Invalid time range given.", code: "BAD_REQUEST" }); } + let currentSeats: CurrentSeats | undefined = undefined; + const userSchedules = await Promise.all( eventType.users.map(async (currentUser) => { - const schedule = currentUser.schedules.filter( - (schedule) => !currentUser.defaultScheduleId || schedule.id === currentUser.defaultScheduleId - )[0]; - - const workingHours = getWorkingHours( + const { + busy, + workingHours, + currentSeats: _currentSeats, + } = await getUserAvailability( { - timeZone: currentUser.timeZone, + userId: currentUser.id, + dateFrom: startTime.format(), + dateTo: endTime.format(), + eventTypeId: input.eventTypeId, }, - schedule.availability || currentUser.availability + { user: currentUser, eventType, currentSeats } ); - - const busy = await getBusyTimes({ - credentials: currentUser.credentials, - startTime: startTime.format(), - endTime: endTime.format(), - eventTypeId: input.eventTypeId, - userId: currentUser.id, - selectedCalendars: currentUser.selectedCalendars, - }); + if (!currentSeats && _currentSeats) currentSeats = _currentSeats; return { workingHours, - busy: busy.map((a) => ({ - start: dayjs(a.start).subtract(currentUser.bufferTime, "minute").toDate(), - end: dayjs(a.end).add(currentUser.bufferTime, "minute").toDate(), - })), + busy, }; }) ); - - const schedule = eventType?.schedule; - const timeZone = schedule?.timeZone || eventType?.timeZone || undefined; - - const workingHours = getWorkingHours( - { - timeZone, - }, - schedule?.availability || (eventType?.availability.length ? eventType.availability : []) - ); - - /* Current logic is if a booking is in a time slot mark it as busy, but seats can have more than one attendee so grab - current bookings with a seats event type and display them on the calendar, even if they are full */ - const currentSeats = eventType?.seatsPerTimeSlot - ? await ctx.prisma.booking.findMany({ - where: { - eventTypeId: input.eventTypeId, - startTime: { - gte: startTime.format(), - lte: endTime.format(), - }, - }, - select: { - uid: true, - startTime: true, - _count: { - select: { - attendees: true, - }, - }, - }, - }) - : []; + const workingHours = userSchedules.flatMap((s) => s.workingHours); + console.log("workingHours", workingHours); + console.log("currentSeats", currentSeats); const slots: Record = {}; diff --git a/packages/core/getUserAvailability.ts b/packages/core/getUserAvailability.ts index 8513b6ada4a82d..fa185fac44371a 100644 --- a/packages/core/getUserAvailability.ts +++ b/packages/core/getUserAvailability.ts @@ -1,5 +1,5 @@ import { Prisma } from "@prisma/client"; -import dayjs from "dayjs"; +import dayjs, { Dayjs } from "dayjs"; import { z } from "zod"; import { getWorkingHours } from "@calcom/lib/availability"; @@ -24,6 +24,7 @@ const getEventType = (id: number) => prisma.eventType.findUnique({ where: { id }, select: { + id: true, seatsPerTimeSlot: true, timeZone: true, schedule: { @@ -52,6 +53,28 @@ const getUser = (where: Prisma.UserWhereUniqueInput) => type User = Awaited>; +export const getCurrentSeats = (eventTypeId: number, dateFrom: Dayjs, dateTo: Dayjs) => + prisma.booking.findMany({ + where: { + eventTypeId, + startTime: { + gte: dateFrom.format(), + lte: dateTo.format(), + }, + }, + select: { + uid: true, + startTime: true, + _count: { + select: { + attendees: true, + }, + }, + }, + }); + +export type CurrentSeats = Awaited>; + export async function getUserAvailability( query: { username?: string; @@ -64,6 +87,7 @@ export async function getUserAvailability( initialData?: { user?: User; eventType?: EventType; + currentSeats?: CurrentSeats; } ) { const { username, userId, dateFrom, dateTo, eventTypeId, timezone } = availabilitySchema.parse(query); @@ -82,6 +106,12 @@ export async function getUserAvailability( let eventType: EventType | null = initialData?.eventType || null; if (!eventType && eventTypeId) eventType = await getEventType(eventTypeId); + /* Current logic is if a booking is in a time slot mark it as busy, but seats can have more than one attendee so grab + current bookings with a seats event type and display them on the calendar, even if they are full */ + let currentSeats: CurrentSeats | null = initialData?.currentSeats || null; + if (!currentSeats && eventType?.seatsPerTimeSlot) + currentSeats = await getCurrentSeats(eventType.id, dateFrom, dateTo); + const { selectedCalendars, ...currentUser } = user; const busyTimes = await getBusyTimes({ @@ -98,8 +128,6 @@ export async function getUserAvailability( end: dayjs(a.end).add(currentUser.bufferTime, "minute").toISOString(), })); - const timeZone = timezone || eventType?.timeZone || currentUser.timeZone; - const schedule = eventType?.schedule ? { ...eventType?.schedule } : { @@ -108,36 +136,14 @@ export async function getUserAvailability( )[0], }; + const timeZone = timezone || schedule?.timeZone || eventType?.timeZone || currentUser.timeZone; + const workingHours = getWorkingHours( { timeZone }, schedule.availability || (eventType?.availability.length ? eventType.availability : currentUser.availability) ); - /* Current logic is if a booking is in a time slot mark it as busy, but seats can have more than one attendee so grab - current bookings with a seats event type and display them on the calendar, even if they are full */ - let currentSeats; - if (eventType?.seatsPerTimeSlot) { - currentSeats = await prisma.booking.findMany({ - where: { - eventTypeId: eventTypeId, - startTime: { - gte: dateFrom.format(), - lte: dateTo.format(), - }, - }, - select: { - uid: true, - startTime: true, - _count: { - select: { - attendees: true, - }, - }, - }, - }); - } - return { busy: bufferedBusyTimes, timeZone, From 264d156c3a41c4f811e519f50f9cf70eb8e94898 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Wed, 15 Jun 2022 20:55:35 +0100 Subject: [PATCH 29/33] Fixed border position (transparent border) to prevent dot from jumping - and possibly fix spacing --- packages/ui/booker/DatePicker.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/ui/booker/DatePicker.tsx b/packages/ui/booker/DatePicker.tsx index ca753058de7874..213226da7a1f5c 100644 --- a/packages/ui/booker/DatePicker.tsx +++ b/packages/ui/booker/DatePicker.tsx @@ -41,9 +41,10 @@ const Day = ({