feat: Add handleSlotSelect prop for custom timeslot selection/reservation handling (#21133)

* feat: Add `handleSlotSelect` prop for custom timeslot selection/reservation handling

* Update custom-slot-select-flow.mdx

* Update custom-slot-select-flow.mdx

* refactor: rename handleSlotSelect to handleSlotReservation for clearer intent

* docs: add API reference link for slot reservation api
This commit is contained in:
Somay Chauhan
2025-05-08 15:13:58 +05:30
committed by GitHub
parent 78a34038d2
commit 024913592b
8 changed files with 5676 additions and 58 deletions
@@ -0,0 +1,20 @@
## Using `handleSlotReservation` for Custom Slot Selection Flows
If you want to trigger a custom action immediately when a user selects a timeslot (before the booking form appears), you can use the `handleSlotReservation` prop. This prop allows you to intercept the slot selection event, enabling you to run custom logic—such as calling the reserve slot api's https://cal.com/docs/api-reference/v2/slots/reserve-a-slot.
**Example usage:**
```tsx
<Booker
handleSlotReservation={(timeslot) => {
// Your custom logic here
console.log("User selected timeslot:", timeslot);
// For example, call the reserve slot api's etc.
}}
// ...other props
/>
```
- When a user selects a slot, the `handleSlotReservation` function will be called with the selected timeslot as its argument.
- If you provide this prop, the default slot reservation flow will be interrupted until your custom logic is complete.
- This is useful for introducing custom slot reservation flows.
@@ -25,6 +25,7 @@ export const BookerEmbed = (
hostsLimit?: BookerPlatformWrapperAtomPropsForTeam["hostsLimit"];
metadata?: BookerPlatformWrapperAtomPropsForTeam["metadata"];
handleCreateBooking?: BookerPlatformWrapperAtomPropsForTeam["handleCreateBooking"];
handleSlotReservation?: BookerPlatformWrapperAtomPropsForTeam["handleSlotReservation"];
preventEventTypeRedirect?: BookerPlatformWrapperAtomPropsForTeam["preventEventTypeRedirect"];
}
| (BookerPlatformWrapperAtomPropsForIndividual & {
@@ -60,6 +60,7 @@ export const BookerPlatformWrapper = (
allowUpdatingUrlParams = false,
confirmButtonDisabled,
isBookingDryRun,
handleSlotReservation,
} = props;
const layout = BookerLayouts[view];
@@ -400,6 +401,7 @@ export const BookerPlatformWrapper = (
onDeleteSlotSuccess: props.onDeleteSlotSuccess,
onDeleteSlotError: props.onDeleteSlotError,
isBookingDryRun: routingParams?.isBookingDryRun,
handleSlotReservation,
});
const { data: connectedCalendars, isPending: fetchingConnectedCalendars } = useConnectedCalendars({
+1
View File
@@ -66,6 +66,7 @@ export type BookerPlatformWrapperAtomProps = Omit<
onDeleteSlotSuccess?: (data: ApiSuccessResponseWithoutData) => void;
onDeleteSlotError?: (data: ApiErrorResponse) => void;
onBookerStateChange?: (state: BookerStoreValues) => void;
handleSlotReservation?: (timeslot: string) => void;
locationUrl?: string;
view?: VIEW_TYPE;
metadata?: Record<string, string>;
@@ -20,6 +20,7 @@ type UseSlotsCallbacks = {
onReserveSlotError?: (err: ApiErrorResponse) => void;
onDeleteSlotSuccess?: (data: ApiSuccessResponseWithoutData) => void;
onDeleteSlotError?: (err: ApiErrorResponse) => void;
handleSlotReservation?: (timeslot: string) => void;
};
export type UseSlotsReturnType = ReturnType<typeof useSlots>;
@@ -32,6 +33,7 @@ export const useSlots = (
onDeleteSlotSuccess,
onDeleteSlotError,
isBookingDryRun,
handleSlotReservation,
}: UseSlotsCallbacks & { isBookingDryRun?: boolean } = {}
) => {
const selectedDuration = useBookerStore((state) => state.selectedDuration);
@@ -63,6 +65,11 @@ export const useSlots = (
const handleReserveSlot = () => {
if (event?.data?.id && selectedTimeslot && (selectedDuration || event?.data?.length)) {
if (handleSlotReservation) {
handleSlotReservation(selectedTimeslot);
return;
}
reserveSlotMutation.mutate({
slotUtcStartDate: dayjs(selectedTimeslot).utc().format(),
eventTypeId: event.data.id,
@@ -57,6 +57,7 @@ export const Router = React.memo(
| "hostsLimit"
| "metadata"
| "handleCreateBooking"
| "handleSlotReservation"
| "preventEventTypeRedirect"
>;
renderLoader?: (isLoading?: boolean) => ReactElement | ReactElement[];
@@ -131,8 +131,10 @@ export default function Bookings(props: { calUsername: string; calEmail: string
? { isTeamEvent: true, teamId: teams?.[0]?.id || 0 }
: { username: props.calUsername })}
hostsLimit={3}
selectedDate={new Date("2025-03-25")}
allowUpdatingUrlParams={true}
handleSlotReservation={(timeslot) => {
console.log("Selected timeslot:", timeslot);
}}
/>
</>
)}
+5641 -57
View File
File diff suppressed because it is too large Load Diff