From ff6fa64b638c58aaa3f4f3bb83924ccd10d3c1f0 Mon Sep 17 00:00:00 2001 From: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Date: Wed, 28 Jan 2026 11:13:07 +0100 Subject: [PATCH] fix: UI improvements for team-wide limits (#27133) * add badge * allow undefined * fix ci type errors --------- Co-authored-by: CarinaWolli --- .../components/tabs/limits/EventLimitsTab.tsx | 83 +++++++++++++++---- apps/web/public/static/locales/en/common.json | 2 + .../repositories/eventTypeRepository.ts | 18 ++++ 3 files changed, 88 insertions(+), 15 deletions(-) diff --git a/apps/web/modules/event-types/components/tabs/limits/EventLimitsTab.tsx b/apps/web/modules/event-types/components/tabs/limits/EventLimitsTab.tsx index bf29cd3b72..d0a0a8815d 100644 --- a/apps/web/modules/event-types/components/tabs/limits/EventLimitsTab.tsx +++ b/apps/web/modules/event-types/components/tabs/limits/EventLimitsTab.tsx @@ -15,7 +15,7 @@ import findDurationType from "@calcom/lib/findDurationType"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { ascendingLimitKeys, intervalLimitKeyToUnit } from "@calcom/lib/intervalLimits/intervalLimit"; import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema"; -import { PeriodType } from "@calcom/prisma/enums"; +import { PeriodType, SchedulingType } from "@calcom/prisma/enums"; import classNames from "@calcom/ui/classNames"; import { Button } from "@calcom/ui/components/button"; import { @@ -28,6 +28,7 @@ import { } from "@calcom/ui/components/form"; import { Icon } from "@calcom/ui/components/icon"; import { Tooltip } from "@calcom/ui/components/tooltip"; +import { Badge } from "@calcom/ui/components/badge"; import { LearnMoreLink } from "@calcom/web/modules/event-types/components/LearnMoreLink"; import { useAutoAnimate } from "@formkit/auto-animate/react"; import * as RadioGroup from "@radix-ui/react-radio-group"; @@ -36,6 +37,7 @@ import React, { useEffect, useState } from "react"; import type { UseFormRegisterReturn, UseFormReturn } from "react-hook-form"; import { Controller, useFormContext } from "react-hook-form"; import type { SingleValue } from "react-select"; +import Link from "next/link"; import MaxActiveBookingsPerBookerController from "./MaxActiveBookingsPerBookerController"; @@ -135,13 +137,13 @@ function RangeLimitRadioItem({ "text-default mb-2 flex flex-col items-start text-sm sm:flex-row sm:items-center", customClassNames?.wrapper )}> -
+
{!isDisabled && ( - + className="flex items-center w-4 h-4 rounded-full border cursor-pointer bg-default border-default min-w-4 focus:border-2 focus:outline-none ltr:mr-2 rtl:ml-2"> + )} {t("within_date_range")} @@ -223,8 +225,8 @@ function RollingLimitRadioItem({ - + className="flex items-center w-4 h-4 rounded-full border cursor-pointer bg-default border-default min-w-4 focus:border-2 focus:outline-none ltr:mr-2 rtl:ml-2"> + )} @@ -259,7 +261,7 @@ function RollingLimitRadioItem({ />  {t("into_the_future")}
-
+
@@ -350,7 +352,7 @@ const MinimumBookingNoticeInput = React.forwardRef< }, [minimumBookingNoticeDisplayValues, setValue, passThroughProps.name]); return ( -
+
(); - const isRecurringEvent = !!formMethods.getValues("recurringEvent"); + + const hasTeamLimits = () => { + const team = eventType.team as { bookingLimits?: IntervalLimit | null; includeManagedEventsInLimits?: boolean } | null | undefined; + const parentTeam = (eventType.parent as { team?: { bookingLimits?: IntervalLimit | null; includeManagedEventsInLimits?: boolean } } | null | undefined)?.team; + + const teamHasLimits = + !!team?.bookingLimits && Object.keys(team.bookingLimits).length > 0; + + const parentTeamHasLimits = + !!parentTeam?.bookingLimits && + Object.keys(parentTeam.bookingLimits).length > 0; + + const includeManaged = + !!parentTeam?.includeManagedEventsInLimits || + !!team?.includeManagedEventsInLimits; + + if (teamHasLimits) { + if(eventType.schedulingType === SchedulingType.MANAGED) return includeManaged; + return true; + } + + return parentTeamHasLimits && includeManaged; + }; + + const TeamLimitsBadge = ({ isManagedChild, teamId }: { isManagedChild: boolean, teamId?: number | null }) => { + const badge = ( + + {t("team_limits_apply")} + + ); + + if (isManagedChild) { + return {badge}; + } + + if (teamId) { + return ( + + {badge} + + ); + } + return null; + }; const { shouldLockIndicator, shouldLockDisableProps } = useLockedFieldsManager({ eventType, @@ -432,7 +481,7 @@ export const EventLimitsTab = ({ eventType, customClassNames }: EventLimitsTabPr "border-subtle stack-y-6 rounded-lg border p-6", customClassNames?.bufferAndNoticeSection?.container )}> -
+
-
+
-
+
-
+
{ @@ -821,7 +874,7 @@ export const EventLimitsTab = ({ eventType, customClassNames }: EventLimitsTabPr formMethods.setValue("offsetStart", 0, { shouldDirty: true }); } }}> -
+