feat: Allow only first slot to be booked (#12636)
Co-authored-by: Morgan Vernay <morgan@cal.com>
This commit is contained in:
1 parent
8e1ce633fb
commit
2f4b1818d0
12 files changed
+47
No files matched your search
@@ -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({
|
||||
|
||||
@@ -298,6 +298,29 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Controller
|
||||
name="onlyShowFirstAvailableSlot"
|
||||
control={formMethods.control}
|
||||
render={({ field: { value } }) => {
|
||||
const isChecked = value;
|
||||
return (
|
||||
<SettingsToggle
|
||||
toggleSwitchAtTheEnd={true}
|
||||
labelClassName="text-sm"
|
||||
title={t("limit_booking_only_first_slot")}
|
||||
description={t("limit_booking_only_first_slot_description")}
|
||||
checked={isChecked}
|
||||
onCheckedChange={(active) => {
|
||||
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"
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Controller
|
||||
name="durationLimits"
|
||||
control={formMethods.control}
|
||||
|
||||
@@ -131,6 +131,7 @@ export type FormValues = {
|
||||
successRedirectUrl: string;
|
||||
durationLimits?: IntervalLimit;
|
||||
bookingLimits?: IntervalLimit;
|
||||
onlyShowFirstAvailableSlot: boolean;
|
||||
children: ChildrenEventType[];
|
||||
hosts: { userId: number; isFixed: boolean }[];
|
||||
bookingFields: z.infer<typeof eventTypeBookingFields>;
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -81,6 +81,7 @@ const commons = {
|
||||
seatsPerTimeSlot: null,
|
||||
seatsShowAttendees: null,
|
||||
seatsShowAvailabilityCount: null,
|
||||
onlyShowFirstAvailableSlot: false,
|
||||
id: 0,
|
||||
hideCalendarNotes: false,
|
||||
recurringEvent: null,
|
||||
|
||||
@@ -101,6 +101,7 @@ export default async function getEventTypeById({
|
||||
slotInterval: true,
|
||||
hashedLink: true,
|
||||
bookingLimits: true,
|
||||
onlyShowFirstAvailableSlot: true,
|
||||
durationLimits: true,
|
||||
successRedirectUrl: true,
|
||||
currency: true,
|
||||
|
||||
@@ -92,6 +92,7 @@ export const buildEventType = (eventType?: Partial<EventType>): EventType => {
|
||||
minimumBookingNotice: 120,
|
||||
beforeEventBuffer: 0,
|
||||
afterEventBuffer: 0,
|
||||
onlyShowFirstAvailableSlot: false,
|
||||
seatsPerTimeSlot: null,
|
||||
seatsShowAttendees: null,
|
||||
seatsShowAvailabilityCount: null,
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "EventType" ADD COLUMN "onlyShowFirstAvailableSlot" BOOLEAN NOT NULL DEFAULT false;
|
||||
@@ -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?
|
||||
|
||||
@@ -585,6 +585,7 @@ export const allManagedEventTypeProps: { [k in keyof Omit<Prisma.EventTypeSelect
|
||||
destinationCalendar: true,
|
||||
periodCountCalendarDays: true,
|
||||
bookingLimits: true,
|
||||
onlyShowFirstAvailableSlot: true,
|
||||
slotInterval: true,
|
||||
scheduleId: true,
|
||||
workflows: true,
|
||||
|
||||
@@ -157,6 +157,7 @@ export async function getEventType(
|
||||
periodType: true,
|
||||
periodStartDate: true,
|
||||
periodEndDate: true,
|
||||
onlyShowFirstAvailableSlot: true,
|
||||
periodCountCalendarDays: true,
|
||||
periodDays: true,
|
||||
metadata: true,
|
||||
@@ -368,6 +369,7 @@ export async function getAvailableSlots({ input, ctx }: GetScheduleOptions) {
|
||||
eventType: {
|
||||
select: {
|
||||
id: true,
|
||||
onlyShowFirstAvailableSlot: true,
|
||||
afterEventBuffer: true,
|
||||
beforeEventBuffer: true,
|
||||
seatsPerTimeSlot: true,
|
||||
@@ -577,6 +579,9 @@ export async function getAvailableSlots({ input, ctx }: GetScheduleOptions) {
|
||||
const dateString = formatter.format(time.toDate());
|
||||
|
||||
r[dateString] = r[dateString] || [];
|
||||
if (eventType.onlyShowFirstAvailableSlot && r[dateString].length > 0) {
|
||||
return r;
|
||||
}
|
||||
r[dateString].push({
|
||||
...passThroughProps,
|
||||
time: time.toISOString(),
|
||||
|
||||
Reference in new issue
Block a user