From 8d59c2fc162b6749f90185a8bb10ad6409fb31c7 Mon Sep 17 00:00:00 2001 From: Tushar Bhatt <95581504+TusharBhatt1@users.noreply.github.com> Date: Fri, 4 Apr 2025 00:59:57 +0530 Subject: [PATCH] feat: max-seats-per-time-slot (#20213) * feat: max-seats-per-time-slot * fix : test * enhancements * enhancements * fix:test * fix:test --- apps/api/v1/lib/validations/event-type.ts | 3 ++- .../inputs/update-event-type.input.ts | 12 +++++++++--- .../tabs/advanced/EventAdvancedTab.tsx | 17 +++++++++++++---- packages/lib/constants.ts | 1 + packages/platform/constants/api.ts | 1 + .../inputs/seats.input.ts | 5 ++++- .../server/routers/viewer/eventTypes/types.ts | 2 ++ 7 files changed, 32 insertions(+), 9 deletions(-) diff --git a/apps/api/v1/lib/validations/event-type.ts b/apps/api/v1/lib/validations/event-type.ts index 08ab2790de..81cfb1a0f0 100644 --- a/apps/api/v1/lib/validations/event-type.ts +++ b/apps/api/v1/lib/validations/event-type.ts @@ -1,5 +1,6 @@ import { z } from "zod"; +import { MAX_SEATS_PER_TIME_SLOT } from "@calcom/lib/constants"; import slugify from "@calcom/lib/slugify"; import { _EventTypeModel as EventType, _HostModel } from "@calcom/prisma/zod"; import { customInputSchema, eventTypeBookingFields } from "@calcom/prisma/zod-utils"; @@ -103,7 +104,7 @@ const schemaEventTypeEditParams = z .transform((s) => slugify(s)) .optional(), length: z.number().int().optional(), - seatsPerTimeSlot: z.number().optional(), + seatsPerTimeSlot: z.number().min(1).max(MAX_SEATS_PER_TIME_SLOT).nullable().optional(), seatsShowAttendees: z.boolean().optional(), seatsShowAvailabilityCount: z.boolean().optional(), bookingFields: eventTypeBookingFields.optional(), diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/inputs/update-event-type.input.ts b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/inputs/update-event-type.input.ts index 3867d1beda..0419c37935 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/inputs/update-event-type.input.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/inputs/update-event-type.input.ts @@ -10,6 +10,7 @@ import { IsOptional, ValidateNested, Min, + Max, IsInt, IsEnum, IsArray, @@ -17,6 +18,8 @@ import { IsNumber, } from "class-validator"; +import { MAX_SEATS_PER_TIME_SLOT } from "@calcom/platform-constants"; + // note(Lauris): We will gradually expose more properties if any customer needs them. // Just uncomment any below when requested. Go to bottom of file to see UpdateEventTypeInput. @@ -414,9 +417,12 @@ export class UpdateEventTypeInput_2024_04_15 { @ApiPropertyOptional() afterEventBuffer?: number; - // @IsInt() - // @IsOptional() - // seatsPerTimeSlot?: number; + @IsInt() + @Min(1) + @Max(MAX_SEATS_PER_TIME_SLOT) + @IsOptional() + @ApiPropertyOptional() + seatsPerTimeSlot?: number; // @IsBoolean() // @IsOptional() diff --git a/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx b/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx index 56cfbc8f5e..877d1aab60 100644 --- a/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx +++ b/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx @@ -30,7 +30,12 @@ import { FormBuilder } from "@calcom/features/form-builder/FormBuilder"; import type { fieldSchema } from "@calcom/features/form-builder/schema"; import type { EditableSchema } from "@calcom/features/form-builder/schema"; import { BookerLayoutSelector } from "@calcom/features/settings/BookerLayoutSelector"; -import { DEFAULT_LIGHT_BRAND_COLOR, DEFAULT_DARK_BRAND_COLOR, APP_NAME } from "@calcom/lib/constants"; +import { + DEFAULT_LIGHT_BRAND_COLOR, + DEFAULT_DARK_BRAND_COLOR, + APP_NAME, + MAX_SEATS_PER_TIME_SLOT, +} from "@calcom/lib/constants"; import type { EventNameObjectType } from "@calcom/lib/event"; import { getEventName } from "@calcom/lib/event"; import { generateHashedLink } from "@calcom/lib/generateHashedLink"; @@ -831,8 +836,10 @@ export const EventAdvancedTab = ({ label={t("number_of_seats")} type="number" disabled={seatsLocked.disabled} - defaultValue={value} + //For old events if value > MAX_SEATS_PER_TIME_SLOT + value={value > MAX_SEATS_PER_TIME_SLOT ? MAX_SEATS_PER_TIME_SLOT : value ?? 1} min={1} + max={MAX_SEATS_PER_TIME_SLOT} containerClassName={classNames( "max-w-80", customClassNames?.seatsOptions?.seatsInput.container @@ -840,9 +847,11 @@ export const EventAdvancedTab = ({ addOnClassname={customClassNames?.seatsOptions?.seatsInput.addOn} className={customClassNames?.seatsOptions?.seatsInput?.input} labelClassName={customClassNames?.seatsOptions?.seatsInput?.label} - addOnSuffix={<>{t("seats")}} + addOnSuffix={t("seats")} onChange={(e) => { - onChange(Math.abs(Number(e.target.value))); + let enteredValue = Number(e.target.value); + if (enteredValue < 1) enteredValue = 1; + onChange(Math.min(enteredValue, MAX_SEATS_PER_TIME_SLOT)); }} data-testid="seats-per-time-slot" /> diff --git a/packages/lib/constants.ts b/packages/lib/constants.ts index f85bba7a45..4ab69b6714 100644 --- a/packages/lib/constants.ts +++ b/packages/lib/constants.ts @@ -58,6 +58,7 @@ export const IS_SELF_HOSTED = !( ); export const EMBED_LIB_URL = process.env.NEXT_PUBLIC_EMBED_LIB_URL || `${WEBAPP_URL}/embed/embed.js`; export const TRIAL_LIMIT_DAYS = 14; +export const MAX_SEATS_PER_TIME_SLOT = 1000; export const HOSTED_CAL_FEATURES = process.env.NEXT_PUBLIC_HOSTED_CAL_FEATURES || !IS_SELF_HOSTED; diff --git a/packages/platform/constants/api.ts b/packages/platform/constants/api.ts index f3514c8973..36d98810f6 100644 --- a/packages/platform/constants/api.ts +++ b/packages/platform/constants/api.ts @@ -70,3 +70,4 @@ export const API_VERSIONS = [ export type API_VERSIONS_ENUM = (typeof API_VERSIONS)[number]; export type API_VERSIONS_TYPE = typeof API_VERSIONS; export const CAL_API_VERSION_HEADER = "cal-api-version"; +export const MAX_SEATS_PER_TIME_SLOT = 1000; diff --git a/packages/platform/types/event-types/event-types_2024_06_14/inputs/seats.input.ts b/packages/platform/types/event-types/event-types_2024_06_14/inputs/seats.input.ts index d7a6cffe52..3841632858 100644 --- a/packages/platform/types/event-types/event-types_2024_06_14/inputs/seats.input.ts +++ b/packages/platform/types/event-types/event-types_2024_06_14/inputs/seats.input.ts @@ -1,10 +1,13 @@ import { ApiProperty } from "@nestjs/swagger"; -import { IsBoolean, IsInt, IsOptional, Min } from "class-validator"; +import { IsBoolean, IsInt, IsOptional, Min, Max } from "class-validator"; + +import { MAX_SEATS_PER_TIME_SLOT } from "@calcom/platform-constants"; // Class representing the seat options export class Seats_2024_06_14 { @IsInt() @Min(1) + @Max(MAX_SEATS_PER_TIME_SLOT) @ApiProperty({ description: "Number of seats available per time slot", example: 4, diff --git a/packages/trpc/server/routers/viewer/eventTypes/types.ts b/packages/trpc/server/routers/viewer/eventTypes/types.ts index 47448de810..978913a294 100644 --- a/packages/trpc/server/routers/viewer/eventTypes/types.ts +++ b/packages/trpc/server/routers/viewer/eventTypes/types.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { templateTypeEnum } from "@calcom/features/ee/cal-ai-phone/zod-utils"; +import { MAX_SEATS_PER_TIME_SLOT } from "@calcom/lib/constants"; import { _DestinationCalendarModel, _EventTypeModel } from "@calcom/prisma/zod"; import { customInputSchema, @@ -71,6 +72,7 @@ const BaseEventTypeUpdateInput = _EventTypeModel assignRRMembersUsingSegment: z.boolean().optional(), rrSegmentQueryValue: rrSegmentQueryValueSchema.optional(), useEventLevelSelectedCalendars: z.boolean().optional(), + seatsPerTimeSlot: z.number().min(1).max(MAX_SEATS_PER_TIME_SLOT).nullable().optional(), }) .partial() .extend(_EventTypeModel.pick({ id: true }).shape);