* fix manage test error * fix manage test error * update useBookerContext * wrap store provider around booker * replace useStore with useBookerStoreContext * replace useBookerStore with useBookerStoreContext * add initializer function for booker store provider * update props * fixup: pass more props * export StoreInitializeType type * replace useBookerStore with useBookerStoreContext * fixup: dont wrap BookerStoreProvider around booker directly * wrap BookerStoreProvider around booker web wrapper and fix local storage import * fix: wrap test components with BookerStoreProvider to fix failing unit tests - Create reusable test utility in test-utils.tsx with comprehensive mock store - Update Booker.test.tsx to use context-based testing approach - Fix DatePicker tests in both bookings and calendars packages - Simulate auto-advance behavior for month navigation tests - All 14 previously failing tests now pass Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com> * add changesets * fix: complete migration from useBookerStore to useBookerStoreContext - Updated all remaining files to use useBookerStoreContext instead of useBookerStore - Fixed BookingFields.test.tsx by wrapping test component with BookerStoreProvider - Ensures all Booker components work within the new context-based store pattern - Resolves failing unit tests in CI by completing the store migration Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com> * fix: add missing useBookerStoreContext import in BookerWebWrapper.tsx - Fixes TypeScript errors in CI where useBookerStoreContext was used but not imported - Completes the migration from useBookerStore to useBookerStoreContext pattern - All tests pass locally after this fix Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com> * Revert "fix: add missing useBookerStoreContext import in BookerWebWrapper.tsx" This reverts commit 12947ca63f46c42e5aee74c78176ef230e2bf2cf. * Revert "fix: complete migration from useBookerStore to useBookerStoreContext" This reverts commit 87d837f2d30e4f9259d2be786049d21856f4eef0. * fixup: useBookerStoreContext and useInitializeBookerStoreContext for booker web wrapper * fixup: failing E2E tests * fixup: email embed breaking * fixup: troubleshooter crashing * replace useBookerstore with useBookerStoreContext --------- Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in> Co-authored-by: Ryukemeister <sahalrajiv6900@gmail.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Benny Joo <sldisek783@gmail.com>
115 lines
3.5 KiB
TypeScript
115 lines
3.5 KiB
TypeScript
import { useEffect } from "react";
|
|
import { shallow } from "zustand/shallow";
|
|
|
|
import dayjs from "@calcom/dayjs";
|
|
import { useBookerStoreContext } from "@calcom/features/bookings/Booker/BookerStoreProvider";
|
|
import { useSlotReservationId } from "@calcom/features/bookings/Booker/useSlotReservationId";
|
|
import type { BookerEvent } from "@calcom/features/bookings/types";
|
|
import { MINUTES_TO_BOOK } from "@calcom/lib/constants";
|
|
import type {
|
|
ApiErrorResponse,
|
|
ApiSuccessResponse,
|
|
ApiSuccessResponseWithoutData,
|
|
} from "@calcom/platform-types";
|
|
|
|
import { useDeleteSelectedSlot } from "./useDeleteSelectedSlot";
|
|
import { useReserveSlot } from "./useReserveSlot";
|
|
|
|
type UseSlotsCallbacks = {
|
|
onReserveSlotSuccess?: (data: ApiSuccessResponse<string>) => void;
|
|
onReserveSlotError?: (err: ApiErrorResponse) => void;
|
|
onDeleteSlotSuccess?: (data: ApiSuccessResponseWithoutData) => void;
|
|
onDeleteSlotError?: (err: ApiErrorResponse) => void;
|
|
handleSlotReservation?: (timeslot: string) => void;
|
|
};
|
|
|
|
export type UseSlotsReturnType = ReturnType<typeof useSlots>;
|
|
|
|
export const useSlots = (
|
|
event: { data?: Pick<BookerEvent, "id" | "length"> | null },
|
|
{
|
|
onReserveSlotSuccess,
|
|
onReserveSlotError,
|
|
onDeleteSlotSuccess,
|
|
onDeleteSlotError,
|
|
isBookingDryRun,
|
|
handleSlotReservation,
|
|
}: UseSlotsCallbacks & { isBookingDryRun?: boolean } = {}
|
|
) => {
|
|
const selectedDuration = useBookerStoreContext((state) => state.selectedDuration);
|
|
const [selectedTimeslot, setSelectedTimeslot] = useBookerStoreContext(
|
|
(state) => [state.selectedTimeslot, state.setSelectedTimeslot],
|
|
shallow
|
|
);
|
|
|
|
const [slotReservationId, setSlotReservationId] = useSlotReservationId();
|
|
|
|
const reserveSlotMutation = useReserveSlot({
|
|
onSuccess: (res) => {
|
|
setSlotReservationId(res.data);
|
|
onReserveSlotSuccess?.(res);
|
|
},
|
|
onError: onReserveSlotError,
|
|
});
|
|
|
|
const seatedEventData = useBookerStoreContext((state) => state.seatedEventData);
|
|
|
|
const removeSelectedSlot = useDeleteSelectedSlot({
|
|
onSuccess: onDeleteSlotSuccess,
|
|
onError: onDeleteSlotError,
|
|
});
|
|
|
|
const handleRemoveSlot = () => {
|
|
if (event?.data) {
|
|
removeSelectedSlot.mutate({ uid: slotReservationId ?? undefined });
|
|
}
|
|
};
|
|
|
|
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,
|
|
slotUtcEndDate: dayjs(selectedTimeslot)
|
|
.utc()
|
|
.add(selectedDuration || event.data.length, "minutes")
|
|
.format(),
|
|
_isDryRun: isBookingDryRun,
|
|
bookingUid: seatedEventData.bookingUid || undefined,
|
|
});
|
|
}
|
|
};
|
|
|
|
const timeslot = useBookerStoreContext((state) => state.selectedTimeslot);
|
|
|
|
useEffect(() => {
|
|
handleReserveSlot();
|
|
|
|
const interval = setInterval(() => {
|
|
handleReserveSlot();
|
|
}, parseInt(MINUTES_TO_BOOK) * 60 * 1000 - 2000);
|
|
|
|
return () => {
|
|
handleRemoveSlot();
|
|
clearInterval(interval);
|
|
};
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [event?.data?.id, timeslot]);
|
|
|
|
return {
|
|
selectedTimeslot,
|
|
setSelectedTimeslot,
|
|
setSlotReservationId,
|
|
slotReservationId,
|
|
handleReserveSlot,
|
|
handleRemoveSlot,
|
|
// TODO: implement slot no longer available feature
|
|
allSelectedTimeslots: [],
|
|
};
|
|
};
|