-
Notifications
You must be signed in to change notification settings - Fork 12k
feat: Implement global booking limits for users #14243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
78 commits
Select commit
Hold shift + click to select a range
0762d3f
Setup UI for global booking limits
asadath1395 1c81a92
Refactor code to reuse existing code block
asadath1395 5be79a2
Add new table GlobalSettings, setup API to save the values from UI to DB
asadath1395 b2aee6f
Added modal to sync event settings with global settings and its relat…
asadath1395 0d8d828
Add falling back to global settings for booking limits when its not s…
asadath1395 dc3109e
Add ability to sync future booking with global settings
asadath1395 5f9157b
Fix all future booking event types returned even when global setting …
asadath1395 f0014ca
Revert global settings taken into consideration when limits where not…
asadath1395 646b6f0
Add ability to disable global limit settings
asadath1395 a98e1fb
Fix type errors
asadath1395 de1fe21
Fix type errors
asadath1395 60d98ee
Merge branch 'booking-global-limits' of https://github.com/asadath139…
asadath1395 a55fd2c
Merge with upstream
asadath1395 95df261
Fix failing to sync events with global settings
asadath1395 cec6253
Fix type check
asadath1395 3ebc5e0
Fix values not getting synced with global limits in individual event …
asadath1395 79aa566
Merge branch 'main' into booking-global-limits
asadath1395 1885e6a
Remove global future bookings related code
asadath1395 543f960
Merge branch 'booking-global-limits' of https://github.com/asadath139…
asadath1395 a099a39
Add global booking limit check while booking a event type
asadath1395 d9e6963
Improve UI to match design
asadath1395 8b16e30
Remove global settings and move bookingLimits to user table
asadath1395 b361282
Merge branch 'main' into booking-global-limits
asadath1395 021dd76
Fix type error
asadath1395 a7ee1da
Merge with upstream
asadath1395 d13ab8b
Fix availablity showing slots as open even when global booking limits…
asadath1395 aa5a782
Fix global booking limits to be applied to only individual and manage…
asadath1395 0aa2883
Merge with upstream
asadath1395 f78862b
Remove yarn.lock changes
asadath1395 820d316
Merge with upstream
asadath1395 8d8635b
Address PR comments
asadath1395 bb6749a
Add tests
asadath1395 6cf0a97
Merge with upstream
asadath1395 ad4d4ed
Merge branch 'main' into booking-global-limits
asadath1395 760a797
Discard changes to apps/web/modules/signup-view.tsx
zomars 2bc097d
Merge branch 'main' into pr/14243
zomars ed5e17d
Rename bookingLimits.tsx to booking-limits.tsx
zomars 214dd52
Fixes
zomars c37daf6
Refactoring
zomars f7f9abe
type fix
zomars bc11784
Fix available slots shown when global booking limits is set and user …
asadath1395 1393a86
Merge branch 'main' into booking-global-limits
asadath1395 d8936e9
Merge branch 'main' into booking-global-limits
asadath1395 3f21f51
feat: Add last login to user table and display that in orgs members view
asadath1395 34de56e
Merge branch 'main' into booking-global-limits
asadath1395 11ac965
Merge with upstream and resolve merge conflicts
asadath1395 24b8b76
Merge branch 'booking-global-limits' of https://github.com/asadath139…
asadath1395 66ab828
Merge branch 'main' into booking-global-limits
anikdhabal 3b607cf
Merge with upstream and resolve conflicts
asadath1395 a4fb4c1
Merge branch 'booking-global-limits' of https://github.com/asadath139…
asadath1395 991b174
Merge branch 'main' into booking-global-limits
asadath1395 753e025
Merge branch 'main' into booking-global-limits
asadath1395 a655d7c
Merge branch 'main' into booking-global-limits
asadath1395 990a8af
Merge branch 'main' into booking-global-limits
asadath1395 b7e9cfe
Merge branch 'main' of https://github.com/calcom/cal.com into booking…
asadath1395 139e3c3
Merge branch 'main' into booking-global-limits
asadath1395 d4e195e
Merge branch 'booking-global-limits' of https://github.com/asadath139…
asadath1395 7697e72
Merge with upstream
asadath1395 63c2fb8
Merge with upstream and fix merge conflicts
asadath1395 fa40128
Merge branch 'main' into booking-global-limits
asadath1395 628d73f
Merge branch 'main' of https://github.com/calcom/cal.com into booking…
asadath1395 9cce11e
Revert unintended changes
asadath1395 ffd2dc0
Merge branch 'main' into booking-global-limits
asadath1395 620cd5b
Merge branch 'main' into booking-global-limits
asadath1395 03a7391
Fix type error
asadath1395 e930229
Merge branch 'booking-global-limits' of https://github.com/asadath139…
asadath1395 b88a3e5
Merge branch 'main' of https://github.com/calcom/cal.com into booking…
asadath1395 0e13b39
Merge branch 'main' of https://github.com/calcom/cal.com into booking…
asadath1395 c4406b9
Merge branch 'main' of https://github.com/calcom/cal.com into booking…
asadath1395 63e561e
Address PR review comments
asadath1395 404d39c
Fix tests
asadath1395 7652dbb
Merge branch 'main' into booking-global-limits
asadath1395 568f704
Merge branch 'main' of https://github.com/calcom/cal.com into booking…
asadath1395 4bc552c
Merge branch 'booking-global-limits' of https://github.com/asadath139…
asadath1395 1c06a4c
Merge with upstream
asadath1395 8405d52
Address cubic-dev-ai review comments
asadath1395 9fac49d
Merge with upstream
asadath1395 04fdafb
Revert unintended changes
asadath1395 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
99 changes: 99 additions & 0 deletions
99
apps/web/components/settings/GlobalBookingLimitsController.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| "use client"; | ||
|
|
||
| import { useForm, Controller } from "react-hook-form"; | ||
|
|
||
| import { IntervalLimitsManager } from "@calcom/features/eventtypes/components/tabs/limits/EventLimitsTab"; | ||
| import SectionBottomActions from "@calcom/features/settings/SectionBottomActions"; | ||
| import { useLocale } from "@calcom/lib/hooks/useLocale"; | ||
| import { parseBookingLimit } from "@calcom/lib/intervalLimits/isBookingLimits"; | ||
| import { validateIntervalLimitOrder } from "@calcom/lib/intervalLimits/validateIntervalLimitOrder"; | ||
| import { trpc } from "@calcom/trpc/react"; | ||
| import type { IntervalLimit } from "@calcom/types/Calendar"; | ||
| import classNames from "@calcom/ui/classNames"; | ||
| import { Button } from "@calcom/ui/components/button"; | ||
| import { Form, SettingsToggle } from "@calcom/ui/components/form"; | ||
| import { showToast } from "@calcom/ui/components/toast"; | ||
|
|
||
| const GlobalBookingLimitsController = ({ | ||
| bookingLimits, | ||
| }: { | ||
| bookingLimits: IntervalLimit | null | undefined; | ||
| }) => { | ||
| const { t } = useLocale(); | ||
| const safeBookingLimits = bookingLimits ?? {}; | ||
| const bookingsLimitFormMethods = useForm({ | ||
| defaultValues: { | ||
| bookingLimits: safeBookingLimits, | ||
| }, | ||
| }); | ||
|
|
||
| const utils = trpc.useUtils(); | ||
| const updateProfileMutation = trpc.viewer.me.updateProfile.useMutation({ | ||
| onSuccess: async () => { | ||
| await utils.viewer.me.invalidate(); | ||
| bookingsLimitFormMethods.reset(bookingsLimitFormMethods.getValues()); | ||
| showToast(t("booking_limits_updated_successfully"), "success"); | ||
| }, | ||
| onError: () => { | ||
| showToast(t("failed_to_save_global_settings"), "error"); | ||
| }, | ||
| }); | ||
|
|
||
| const handleSubmit = async (values: { bookingLimits: IntervalLimit }) => { | ||
| const { bookingLimits } = values; | ||
| const parsedBookingLimits = parseBookingLimit(bookingLimits) || {}; | ||
| if (bookingLimits) { | ||
| const isValid = validateIntervalLimitOrder(parsedBookingLimits); | ||
| if (!isValid) throw new Error(t("event_setup_booking_limits_error")); | ||
| } | ||
| updateProfileMutation.mutate({ ...values, bookingLimits: parsedBookingLimits }); | ||
| }; | ||
|
|
||
| return ( | ||
| <Form form={bookingsLimitFormMethods} handleSubmit={handleSubmit}> | ||
| <Controller | ||
| name="bookingLimits" | ||
| render={({ field: { value } }) => { | ||
| const isChecked = Object.keys(value ?? {}).length > 0; | ||
| return ( | ||
| <SettingsToggle | ||
| toggleSwitchAtTheEnd={true} | ||
| title={t("limit_booking_frequency")} | ||
| description={t("global_limit_booking_frequency_description")} | ||
| checked={isChecked} | ||
| onCheckedChange={(active) => { | ||
| if (active) { | ||
| bookingsLimitFormMethods.setValue("bookingLimits", { | ||
| PER_DAY: 1, | ||
| }); | ||
| } else { | ||
| bookingsLimitFormMethods.setValue("bookingLimits", {}); | ||
| } | ||
| handleSubmit(bookingsLimitFormMethods.getValues()); | ||
| }} | ||
| switchContainerClassName={classNames( | ||
| "border-subtle mt-6 rounded-lg border py-6 px-4 sm:px-6", | ||
| isChecked && "rounded-b-none" | ||
| )} | ||
| childrenClassName="lg:ml-0"> | ||
| <div className="border-subtle border border-y-0 p-6"> | ||
| <IntervalLimitsManager propertyName="bookingLimits" defaultLimit={1} step={1} /> | ||
| </div> | ||
| <SectionBottomActions align="end"> | ||
| <Button | ||
| color="primary" | ||
| type="submit" | ||
| loading={updateProfileMutation.isPending} | ||
| disabled={!bookingsLimitFormMethods.formState.dirtyFields.bookingLimits}> | ||
| {t("update")} | ||
| </Button> | ||
| </SectionBottomActions> | ||
| </SettingsToggle> | ||
| ); | ||
| }} | ||
| /> | ||
| </Form> | ||
| ); | ||
| }; | ||
|
|
||
| export default GlobalBookingLimitsController; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,10 +3,14 @@ import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSche | |
| import { checkBookingLimits } from "@calcom/lib/intervalLimits/server/checkBookingLimits"; | ||
| import { checkDurationLimits } from "@calcom/lib/intervalLimits/server/checkDurationLimits"; | ||
| import { withReporting } from "@calcom/lib/sentryWrapper"; | ||
| import prisma from "@calcom/prisma"; | ||
|
|
||
| import type { NewBookingEventType } from "./getEventTypesFromDB"; | ||
|
|
||
| type EventType = Pick<NewBookingEventType, "bookingLimits" | "durationLimits" | "id" | "schedule">; | ||
| type EventType = Pick< | ||
| NewBookingEventType, | ||
| "bookingLimits" | "durationLimits" | "id" | "schedule" | "userId" | "schedulingType" | ||
| >; | ||
|
|
||
| type InputProps = { | ||
| eventType: EventType; | ||
|
|
@@ -19,11 +23,11 @@ const _checkBookingAndDurationLimits = async ({ | |
| reqBodyStart, | ||
| reqBodyRescheduleUid, | ||
| }: InputProps) => { | ||
| const startAsDate = dayjs(reqBodyStart).toDate(); | ||
| if ( | ||
| Object.prototype.hasOwnProperty.call(eventType, "bookingLimits") || | ||
| Object.prototype.hasOwnProperty.call(eventType, "durationLimits") | ||
| ) { | ||
| const startAsDate = dayjs(reqBodyStart).toDate(); | ||
| if (eventType.bookingLimits && Object.keys(eventType.bookingLimits).length > 0) { | ||
| await checkBookingLimits( | ||
| eventType.bookingLimits as IntervalLimit, | ||
|
|
@@ -42,6 +46,31 @@ const _checkBookingAndDurationLimits = async ({ | |
| ); | ||
| } | ||
| } | ||
|
|
||
| // We are only interested in global booking limits for individual and managed events for which schedulingType is null | ||
| if (eventType.userId && !eventType.schedulingType) { | ||
| const eventTypeUser = await prisma.user.findUnique({ | ||
| where: { | ||
| id: eventType.userId, | ||
| }, | ||
| select: { | ||
| id: true, | ||
| email: true, | ||
| bookingLimits: true, | ||
| }, | ||
| }); | ||
| if (eventTypeUser?.bookingLimits && Object.keys(eventTypeUser.bookingLimits).length > 0) { | ||
| await checkBookingLimits( | ||
| eventTypeUser.bookingLimits as IntervalLimit, | ||
| startAsDate, | ||
| eventType.id, | ||
| reqBodyRescheduleUid, | ||
| eventType.schedule?.timeZone, | ||
| { id: eventTypeUser.id, email: eventTypeUser.email }, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| /* isGlobalBookingLimits */ true | ||
| ); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| export const checkBookingAndDurationLimits = withReporting( | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition lacks clarity on why booking limits are only checked for users when schedulingType is falsy. Consider adding a comment explaining this logic.