diff --git a/apps/api/lib/validations/event-type.ts b/apps/api/lib/validations/event-type.ts index 70823213bf0922..cbabad6dc492ae 100644 --- a/apps/api/lib/validations/event-type.ts +++ b/apps/api/lib/validations/event-type.ts @@ -60,6 +60,7 @@ export const schemaEventTypeBaseBodyParams = EventType.pick({ successRedirectUrl: true, locations: true, bookingLimits: true, + onlyShowFirstAvailableSlot: true, durationLimits: true, }) .merge( @@ -147,6 +148,7 @@ export const schemaEventTypeReadPublic = EventType.pick({ seatsShowAvailabilityCount: true, bookingFields: true, bookingLimits: true, + onlyShowFirstAvailableSlot: true, durationLimits: true, }).merge( z.object({ diff --git a/apps/web/components/eventtype/EventLimitsTab.tsx b/apps/web/components/eventtype/EventLimitsTab.tsx index 24502bb2b872fe..da68c260a9b3c6 100644 --- a/apps/web/components/eventtype/EventLimitsTab.tsx +++ b/apps/web/components/eventtype/EventLimitsTab.tsx @@ -298,6 +298,29 @@ export const EventLimitsTab = ({ eventType }: Pick + { + const isChecked = value; + return ( + { + formMethods.setValue("onlyShowFirstAvailableSlot", active ?? false); + }} + switchContainerClassName={classNames( + "border-subtle mt-6 rounded-lg border py-6 px-4 sm:px-6", + isChecked && "rounded-b-none" + )} + /> + ); + }} + /> ; @@ -250,6 +251,7 @@ const EventTypePage = (props: EventTypeSetupProps) => { description: eventType.description ?? undefined, schedule: eventType.schedule || undefined, bookingLimits: eventType.bookingLimits || undefined, + onlyShowFirstAvailableSlot: eventType.onlyShowFirstAvailableSlot || undefined, durationLimits: eventType.durationLimits || undefined, length: eventType.length, hidden: eventType.hidden, @@ -429,6 +431,7 @@ const EventTypePage = (props: EventTypeSetupProps) => { seatsShowAttendees, seatsShowAvailabilityCount, bookingLimits, + onlyShowFirstAvailableSlot, durationLimits, recurringEvent, locations, @@ -491,6 +494,7 @@ const EventTypePage = (props: EventTypeSetupProps) => { beforeEventBuffer: beforeBufferTime, afterEventBuffer: afterBufferTime, bookingLimits, + onlyShowFirstAvailableSlot, durationLimits, seatsPerTimeSlot, seatsShowAttendees, @@ -532,6 +536,7 @@ const EventTypePage = (props: EventTypeSetupProps) => { seatsShowAttendees, seatsShowAvailabilityCount, bookingLimits, + onlyShowFirstAvailableSlot, durationLimits, recurringEvent, locations, @@ -584,6 +589,7 @@ const EventTypePage = (props: EventTypeSetupProps) => { beforeEventBuffer: beforeBufferTime, afterEventBuffer: afterBufferTime, bookingLimits, + onlyShowFirstAvailableSlot, durationLimits, seatsPerTimeSlot, seatsShowAttendees, diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index a29287a20b1893..39e5906ea2466a 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -1481,6 +1481,8 @@ "report_app": "Report app", "limit_booking_frequency": "Limit booking frequency", "limit_booking_frequency_description": "Limit how many times this event can be booked", + "limit_booking_only_first_slot": "Limit booking only first slot", + "limit_booking_only_first_slot_description": "Allow only the first slot of every day to be booked", "limit_total_booking_duration": "Limit total booking duration", "limit_total_booking_duration_description": "Limit total amount of time that this event can be booked", "add_limit": "Add Limit", diff --git a/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts b/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts index 585981796e39b2..043f717d82d7f7 100644 --- a/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts +++ b/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts @@ -186,6 +186,7 @@ export default async function handleChildrenEventTypes({ metadata: (managedEventTypeValues.metadata as Prisma.InputJsonValue) ?? undefined, bookingFields: (managedEventTypeValues.bookingFields as Prisma.InputJsonValue) ?? undefined, durationLimits: (managedEventTypeValues.durationLimits as Prisma.InputJsonValue) ?? undefined, + onlyShowFirstAvailableSlot: managedEventTypeValues.onlyShowFirstAvailableSlot ?? false, userId, users: { connect: [{ id: userId }], @@ -235,6 +236,7 @@ export default async function handleChildrenEventTypes({ hidden: children?.find((ch) => ch.owner.id === userId)?.hidden ?? false, bookingLimits: (managedEventTypeValues.bookingLimits as unknown as Prisma.InputJsonObject) ?? undefined, + onlyShowFirstAvailableSlot: managedEventTypeValues.onlyShowFirstAvailableSlot ?? false, recurringEvent: (managedEventTypeValues.recurringEvent as unknown as Prisma.InputJsonValue) ?? undefined, metadata: (managedEventTypeValues.metadata as Prisma.InputJsonValue) ?? undefined, diff --git a/packages/lib/defaultEvents.ts b/packages/lib/defaultEvents.ts index 5243b63ebdfda7..ad0857f3d100a5 100644 --- a/packages/lib/defaultEvents.ts +++ b/packages/lib/defaultEvents.ts @@ -81,6 +81,7 @@ const commons = { seatsPerTimeSlot: null, seatsShowAttendees: null, seatsShowAvailabilityCount: null, + onlyShowFirstAvailableSlot: false, id: 0, hideCalendarNotes: false, recurringEvent: null, diff --git a/packages/lib/getEventTypeById.ts b/packages/lib/getEventTypeById.ts index be26508d9fb913..9606d35108e3f3 100644 --- a/packages/lib/getEventTypeById.ts +++ b/packages/lib/getEventTypeById.ts @@ -101,6 +101,7 @@ export default async function getEventTypeById({ slotInterval: true, hashedLink: true, bookingLimits: true, + onlyShowFirstAvailableSlot: true, durationLimits: true, successRedirectUrl: true, currency: true, diff --git a/packages/lib/test/builder.ts b/packages/lib/test/builder.ts index 4a2f79c891ef40..f766d74578c3ac 100644 --- a/packages/lib/test/builder.ts +++ b/packages/lib/test/builder.ts @@ -92,6 +92,7 @@ export const buildEventType = (eventType?: Partial): EventType => { minimumBookingNotice: 120, beforeEventBuffer: 0, afterEventBuffer: 0, + onlyShowFirstAvailableSlot: false, seatsPerTimeSlot: null, seatsShowAttendees: null, seatsShowAvailabilityCount: null, diff --git a/packages/prisma/migrations/20231202181233_adding_show_first_available_timeslot/migration.sql b/packages/prisma/migrations/20231202181233_adding_show_first_available_timeslot/migration.sql new file mode 100644 index 00000000000000..9cb3a0d629fe33 --- /dev/null +++ b/packages/prisma/migrations/20231202181233_adding_show_first_available_timeslot/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "EventType" ADD COLUMN "onlyShowFirstAvailableSlot" BOOLEAN NOT NULL DEFAULT false; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 8cc61111fa89ed..ef3e541070705a 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -98,6 +98,7 @@ model EventType { beforeEventBuffer Int @default(0) afterEventBuffer Int @default(0) seatsPerTimeSlot Int? + onlyShowFirstAvailableSlot Boolean @default(false) seatsShowAttendees Boolean? @default(false) seatsShowAvailabilityCount Boolean? @default(true) schedulingType SchedulingType? diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index dba23604b7ff9c..b5a2296a6f43d9 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -585,6 +585,7 @@ export const allManagedEventTypeProps: { [k in keyof Omit 0) { + return r; + } r[dateString].push({ ...passThroughProps, time: time.toISOString(),