From 5293db29d9716eee56be9a78da1a1b07964e4c97 Mon Sep 17 00:00:00 2001 From: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Date: Wed, 12 Oct 2022 06:29:04 +0100 Subject: [PATCH] Feat Booking Limits (#4759) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add db relevant stuff * Basic UI there still buggy * This UI is hard - some progress * Fix awful state mangament * Fix re-ordering * Working UI logic! * Partical working minMax function * Fix min max * bookingLImits api + tests * Moved checkBookingLimits to backend only code * Fix httperror import * Return busy times * Remove avaliablity calc * Working for everything but year * Remove redundant + fix async forloop * Add compatible type * Future proof with evenTypeId filter * Fix commonjson * Sorting + validation + tests + passing * Add empty test * Move validation check to backend * Add bookinglimits in trpc * Add test for undefined * Apply suggestions from code review Co-authored-by: Jeroen Reumkens * Update apps/web/components/v2/eventtype/EventLimitsTab.tsx Co-authored-by: Jeroen Reumkens * Rename value for eligiability * Rename keyof type * status code * Fix toggle not toggling off * Update apps/web/pages/v2/event-types/[type]/index.tsx Co-authored-by: Omar López * Update apps/web/pages/v2/event-types/[type]/index.tsx Co-authored-by: Omar López * Change back to undefined as it is working for sean. See if it fails on testapp * Fixing test builder Co-authored-by: Peer Richelsen Co-authored-by: Alex van Andel Co-authored-by: Jeroen Reumkens Co-authored-by: Hariom Balhara Co-authored-by: Omar López Co-authored-by: Leo Giovanetti --- .../v2/eventtype/EventLimitsTab.tsx | 177 +++++++++++++++++- apps/web/pages/api/book/event.ts | 11 +- apps/web/pages/availability/troubleshoot.tsx | 4 +- .../pages/v2/availability/troubleshoot.tsx | 12 +- .../web/pages/v2/event-types/[type]/index.tsx | 15 +- apps/web/public/static/locales/en/common.json | 3 + apps/web/test/lib/checkBookingLimits.test.ts | 119 ++++++++++++ packages/core/getBusyTimes.ts | 2 +- packages/core/getUserAvailability.ts | 63 ++++++- packages/lib/defaultEvents.ts | 1 + packages/lib/index.ts | 1 + packages/lib/isBookingLimits.ts | 29 +++ packages/lib/server/checkBookingLimits.ts | 94 ++++++++++ packages/lib/server/index.ts | 2 + packages/lib/test/builder.ts | 1 + .../migration.sql | 2 + packages/prisma/schema.prisma | 2 + packages/prisma/zod-utils.ts | 9 + .../trpc/server/routers/viewer/eventTypes.tsx | 9 + packages/trpc/server/routers/viewer/slots.tsx | 4 +- packages/types/BufferedBusyTime.d.ts | 4 +- packages/types/Calendar.d.ts | 7 + 22 files changed, 551 insertions(+), 20 deletions(-) create mode 100644 apps/web/test/lib/checkBookingLimits.test.ts create mode 100644 packages/lib/isBookingLimits.ts create mode 100644 packages/lib/server/checkBookingLimits.ts create mode 100644 packages/prisma/migrations/20220926105434_add_booking_limit/migration.sql diff --git a/apps/web/components/v2/eventtype/EventLimitsTab.tsx b/apps/web/components/v2/eventtype/EventLimitsTab.tsx index 72881669b1..f02874fa44 100644 --- a/apps/web/components/v2/eventtype/EventLimitsTab.tsx +++ b/apps/web/components/v2/eventtype/EventLimitsTab.tsx @@ -1,19 +1,21 @@ +import autoAnimate from "@formkit/auto-animate"; import * as RadioGroup from "@radix-ui/react-radio-group"; import { EventTypeSetupInfered, FormValues } from "pages/v2/event-types/[type]"; -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useFormContext, Controller, useWatch } from "react-hook-form"; import { classNames } from "@calcom/lib"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { PeriodType } from "@calcom/prisma/client"; -import { Select, Switch, Label, Input, MinutesField } from "@calcom/ui/v2"; +import type { BookingLimit } from "@calcom/types/Calendar"; +import { Icon } from "@calcom/ui"; +import { Select, Switch, Label, Input, MinutesField, Button } from "@calcom/ui/v2"; import DateRangePicker from "@calcom/ui/v2/core/form/date-range-picker/DateRangePicker"; export const EventLimitsTab = (props: Pick) => { const { t } = useLocale(); const formMethods = useFormContext(); const { eventType } = props; - const PERIOD_TYPES = [ { type: "ROLLING" as const, @@ -154,7 +156,49 @@ export const EventLimitsTab = (props: Pick) /> +
+ +
+
+
+ ( + 0} + onCheckedChange={(active) => { + if (active) { + formMethods.setValue("bookingLimits", { + PER_DAY: 1, + }); + } else { + formMethods.setValue("bookingLimits", undefined); + } + }} + /> + )} + /> + +
+ +

+ {t("limit_booking_frequency_description")} +

+
+
+
+ +
+
+
+ +
+
@@ -261,3 +305,130 @@ export const EventLimitsTab = (props: Pick)
); }; + +const validationOrderKeys = ["PER_DAY", "PER_WEEK", "PER_MONTH", "PER_YEAR"]; +type BookingLimitsKey = keyof BookingLimit; +const BookingLimits = () => { + const { watch, setValue, control } = useFormContext(); + const watchBookingLimits = watch("bookingLimits"); + const animateRef = useRef(null); + const { t } = useLocale(); + + useEffect(() => { + animateRef.current && autoAnimate(animateRef.current); + }, [animateRef]); + + const BOOKING_LIMIT_OPTIONS: { + value: keyof BookingLimit; + label: string; + }[] = [ + { + value: "PER_DAY", + label: "Per Day", + }, + { + value: "PER_WEEK", + label: "Per Week", + }, + { + value: "PER_MONTH", + label: "Per Month", + }, + { + value: "PER_YEAR", + label: "Per Year", + }, + ]; + + return ( + { + const currentBookingLimits = value; + return ( +
    + {currentBookingLimits && + watchBookingLimits && + Object.entries(currentBookingLimits) + .sort(([limitkeyA], [limitKeyB]) => { + return ( + validationOrderKeys.indexOf(limitkeyA as BookingLimitsKey) - + validationOrderKeys.indexOf(limitKeyB as BookingLimitsKey) + ); + }) + .map(([key, bookingAmount]) => { + const bookingLimitKey = key as BookingLimitsKey; + return ( +
    + { + const val = e.target.value; + setValue(`bookingLimits.${bookingLimitKey}`, parseInt(val)); + }} + /> +