@wix/headless-bookings 0.0.24 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/cjs/dist/__mocks__/services.d.ts +75 -0
  2. package/cjs/dist/__mocks__/services.js +308 -0
  3. package/cjs/dist/api/fetch-availability/index.d.ts +5 -5
  4. package/cjs/dist/api/fetch-availability/index.js +9 -6
  5. package/cjs/dist/react/booking/Book.d.ts +49 -0
  6. package/cjs/dist/react/booking/Book.js +46 -0
  7. package/cjs/dist/react/booking/Booking.d.ts +18 -0
  8. package/cjs/dist/react/booking/Booking.js +19 -0
  9. package/cjs/dist/react/core/booking/Book.d.ts +41 -0
  10. package/cjs/dist/react/core/booking/Book.js +59 -0
  11. package/cjs/dist/react/core/time-slot-list/TimeSlot.d.ts +4 -4
  12. package/cjs/dist/react/core/time-slot-list/TimeSlot.js +3 -10
  13. package/cjs/dist/react/time-slot-list/TimeSlot.d.ts +3 -3
  14. package/cjs/dist/react/time-slot-list/TimeSlot.js +3 -3
  15. package/cjs/dist/react/time-slot-list/TimeSlotList.d.ts +5 -3
  16. package/cjs/dist/services/booking/book-action/bookAction.d.ts +21 -0
  17. package/cjs/dist/services/booking/book-action/bookAction.js +59 -0
  18. package/cjs/dist/services/booking/book-action/buildBookingRequest.d.ts +22 -0
  19. package/cjs/dist/services/booking/book-action/buildBookingRequest.js +81 -0
  20. package/cjs/dist/services/booking/book-action/buildCheckoutRequest.d.ts +26 -0
  21. package/cjs/dist/services/booking/book-action/buildCheckoutRequest.js +62 -0
  22. package/cjs/dist/services/booking/book-action/canBook.d.ts +23 -0
  23. package/cjs/dist/services/booking/book-action/canBook.js +19 -0
  24. package/cjs/dist/services/booking/book-action/index.d.ts +8 -0
  25. package/cjs/dist/services/booking/book-action/index.js +7 -0
  26. package/cjs/dist/services/booking/book-action/types.d.ts +50 -0
  27. package/cjs/dist/services/booking/book-action/types.js +4 -0
  28. package/cjs/dist/services/booking/booking.d.ts +2 -0
  29. package/cjs/dist/services/booking/booking.js +16 -0
  30. package/cjs/dist/services/index.d.ts +1 -0
  31. package/cjs/dist/services/index.js +1 -0
  32. package/cjs/dist/services/time-slot-list/time-slot-list.def.d.ts +10 -2
  33. package/cjs/dist/services/time-slot-list/time-slot-list.js +9 -29
  34. package/dist/__mocks__/services.d.ts +75 -0
  35. package/dist/__mocks__/services.js +308 -0
  36. package/dist/api/fetch-availability/index.d.ts +5 -5
  37. package/dist/api/fetch-availability/index.js +9 -6
  38. package/dist/react/booking/Book.d.ts +49 -0
  39. package/dist/react/booking/Book.js +46 -0
  40. package/dist/react/booking/Booking.d.ts +18 -0
  41. package/dist/react/booking/Booking.js +19 -0
  42. package/dist/react/core/booking/Book.d.ts +41 -0
  43. package/dist/react/core/booking/Book.js +59 -0
  44. package/dist/react/core/time-slot-list/TimeSlot.d.ts +4 -4
  45. package/dist/react/core/time-slot-list/TimeSlot.js +3 -10
  46. package/dist/react/time-slot-list/TimeSlot.d.ts +3 -3
  47. package/dist/react/time-slot-list/TimeSlot.js +3 -3
  48. package/dist/react/time-slot-list/TimeSlotList.d.ts +5 -3
  49. package/dist/services/booking/book-action/bookAction.d.ts +21 -0
  50. package/dist/services/booking/book-action/bookAction.js +59 -0
  51. package/dist/services/booking/book-action/buildBookingRequest.d.ts +22 -0
  52. package/dist/services/booking/book-action/buildBookingRequest.js +81 -0
  53. package/dist/services/booking/book-action/buildCheckoutRequest.d.ts +26 -0
  54. package/dist/services/booking/book-action/buildCheckoutRequest.js +62 -0
  55. package/dist/services/booking/book-action/canBook.d.ts +23 -0
  56. package/dist/services/booking/book-action/canBook.js +19 -0
  57. package/dist/services/booking/book-action/index.d.ts +8 -0
  58. package/dist/services/booking/book-action/index.js +7 -0
  59. package/dist/services/booking/book-action/types.d.ts +50 -0
  60. package/dist/services/booking/book-action/types.js +4 -0
  61. package/dist/services/booking/booking.d.ts +2 -0
  62. package/dist/services/booking/booking.js +16 -0
  63. package/dist/services/index.d.ts +1 -0
  64. package/dist/services/index.js +1 -0
  65. package/dist/services/time-slot-list/time-slot-list.def.d.ts +10 -2
  66. package/dist/services/time-slot-list/time-slot-list.js +9 -29
  67. package/package.json +4 -3
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as CoreBooking from '../core/booking/Booking.js';
3
+ import { Book } from './Book.js';
3
4
  /**
4
5
  * Root component that provides booking context to the entire app.
5
6
  *
@@ -23,3 +24,21 @@ export const Root = (props) => {
23
24
  return (_jsx(CoreBooking.Root, { bookingServiceConfig: bookingServiceConfig, children: children }));
24
25
  };
25
26
  Root.displayName = 'Booking.Root';
27
+ /**
28
+ * Actions namespace containing action components for booking
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * // Simple usage
33
+ * <Booking.Actions.Book />
34
+ *
35
+ * // With callbacks
36
+ * <Booking.Actions.Book
37
+ * onCheckoutRequired={({ checkoutId }) => router.push(`/checkout?id=${checkoutId}`)}
38
+ * onError={(error) => toast.error(error)}
39
+ * />
40
+ * ```
41
+ */
42
+ export const Actions = {
43
+ Book,
44
+ };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Core Book - Provides book action data via render props
3
+ * NO AsChildSlot, NO asChild - that belongs in high-level component
4
+ */
5
+ import React from 'react';
6
+ import type { BookResult, BookingError } from '../../../services/booking/book-action/types.js';
7
+ /**
8
+ * Data exposed by Book component via render props
9
+ */
10
+ export interface BookData {
11
+ isLoading: boolean;
12
+ error: BookingError | null;
13
+ canBook: boolean;
14
+ onClick: () => Promise<void>;
15
+ disabled: boolean;
16
+ }
17
+ /**
18
+ * Props for core Book component
19
+ */
20
+ export interface BookProps {
21
+ children: (data: BookData) => React.ReactNode;
22
+ onCheckoutRequired?: (result: BookResult) => void;
23
+ onError?: (error: BookingError) => void;
24
+ disabled?: boolean;
25
+ }
26
+ /**
27
+ * Core Book component - provides book action data via render props.
28
+ * Used internally by higher-level Booking.Actions.Book component.
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * <Book onCheckoutRequired={handleCheckout}>
33
+ * {({ isLoading, error, canBook, onClick, disabled }) => (
34
+ * <button onClick={onClick} disabled={disabled}>
35
+ * {isLoading ? 'Booking...' : 'Book Now'}
36
+ * </button>
37
+ * )}
38
+ * </Book>
39
+ * ```
40
+ */
41
+ export declare function Book(props: BookProps): React.ReactNode;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Core Book - Provides book action data via render props
3
+ * NO AsChildSlot, NO asChild - that belongs in high-level component
4
+ */
5
+ import { useState } from 'react';
6
+ import { useService } from '@wix/services-manager-react';
7
+ import { BookingServiceDefinition } from '../../../services/booking/booking.js';
8
+ import { canBook } from '../../../services/booking/book-action/canBook.js';
9
+ /**
10
+ * Core Book component - provides book action data via render props.
11
+ * Used internally by higher-level Booking.Actions.Book component.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * <Book onCheckoutRequired={handleCheckout}>
16
+ * {({ isLoading, error, canBook, onClick, disabled }) => (
17
+ * <button onClick={onClick} disabled={disabled}>
18
+ * {isLoading ? 'Booking...' : 'Book Now'}
19
+ * </button>
20
+ * )}
21
+ * </Book>
22
+ * ```
23
+ */
24
+ export function Book(props) {
25
+ const { children, onCheckoutRequired, onError, disabled } = props;
26
+ const [isLoading, setIsLoading] = useState(false);
27
+ const [error, setError] = useState(null);
28
+ const bookingService = useService(BookingServiceDefinition);
29
+ const serviceSelections = bookingService.serviceSelections.get();
30
+ const formSubmission = bookingService.formSubmission.get();
31
+ const canBookValue = canBook({ serviceSelections, formSubmission });
32
+ const handleClick = async () => {
33
+ setIsLoading(true);
34
+ setError(null);
35
+ try {
36
+ const result = await bookingService.actions.book();
37
+ onCheckoutRequired?.(result);
38
+ }
39
+ catch (e) {
40
+ const bookingError = {
41
+ message: e instanceof Error ? e.message : 'Booking failed',
42
+ errorObject: e,
43
+ };
44
+ setError(bookingError);
45
+ onError?.(bookingError);
46
+ }
47
+ finally {
48
+ setIsLoading(false);
49
+ }
50
+ };
51
+ const isDisabled = isLoading || disabled || !canBookValue;
52
+ return children({
53
+ isLoading,
54
+ error,
55
+ canBook: canBookValue,
56
+ onClick: handleClick,
57
+ disabled: isDisabled,
58
+ });
59
+ }
@@ -89,11 +89,11 @@ export declare function StaffMemberProvider(props: StaffMemberProviderProps): Re
89
89
  export interface InfoProps {
90
90
  children: (data: {
91
91
  /** Parsed localStartDate as Date (constructed from the raw localStartDate string) */
92
- startDate: Date | undefined;
92
+ startDate: Date;
93
93
  /** Parsed localEndDate as Date (constructed from the raw localEndDate string) */
94
- endDate: Date | undefined;
95
- /** Calculated duration in minutes (null if dates unavailable) */
96
- durationInMinutes: number | null;
94
+ endDate: Date;
95
+ /** Calculated duration in minutes */
96
+ durationInMinutes: number;
97
97
  /** Whether the timeSlot is bookable */
98
98
  bookable: boolean;
99
99
  /** Whether this timeSlot is currently selected */
@@ -116,18 +116,11 @@ export function StaffMemberProvider(props) {
116
116
  */
117
117
  export function Info(props) {
118
118
  const { timeSlot, isSelected } = useTimeSlotContext();
119
- const startDate = timeSlot.localStartDate
120
- ? new Date(timeSlot.localStartDate)
121
- : undefined;
122
- const endDate = timeSlot.localEndDate
123
- ? new Date(timeSlot.localEndDate)
124
- : undefined;
119
+ const startDate = new Date(timeSlot.localStartDate);
120
+ const endDate = new Date(timeSlot.localEndDate);
125
121
  const bookable = timeSlot.bookable ?? false;
126
122
  // Calculate duration from start and end dates
127
- let durationInMinutes = null;
128
- if (startDate && endDate) {
129
- durationInMinutes = Math.round((endDate.getTime() - startDate.getTime()) / (1000 * 60));
130
- }
123
+ const durationInMinutes = Math.round((endDate.getTime() - startDate.getTime()) / (1000 * 60));
131
124
  return props.children({
132
125
  startDate,
133
126
  endDate,
@@ -16,7 +16,7 @@ export type { LocationData, StaffMemberData, } from '../core/time-slot-list/Time
16
16
  export interface StartDateProps {
17
17
  asChild?: boolean;
18
18
  children?: AsChildChildren<{
19
- startDate: Date | undefined;
19
+ startDate: Date;
20
20
  }>;
21
21
  className?: string;
22
22
  }
@@ -45,7 +45,7 @@ export declare const StartDate: React.ForwardRefExoticComponent<StartDateProps &
45
45
  export interface EndDateProps {
46
46
  asChild?: boolean;
47
47
  children?: AsChildChildren<{
48
- endDate: Date | undefined;
48
+ endDate: Date;
49
49
  }>;
50
50
  className?: string;
51
51
  }
@@ -74,7 +74,7 @@ export declare const EndDate: React.ForwardRefExoticComponent<EndDateProps & Rea
74
74
  export interface DurationProps {
75
75
  asChild?: boolean;
76
76
  children?: AsChildChildren<{
77
- durationInMinutes: number | null;
77
+ durationInMinutes: number;
78
78
  }>;
79
79
  className?: string;
80
80
  }
@@ -53,7 +53,7 @@ var TestIds;
53
53
  */
54
54
  export const StartDate = React.forwardRef((props, ref) => {
55
55
  const { asChild, children, className } = props;
56
- return (_jsx(CoreTimeSlot.Info, { children: ({ startDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotStartDate, customElement: children, customElementProps: { startDate }, children: _jsx("span", { children: startDate?.toString() }) })) }));
56
+ return (_jsx(CoreTimeSlot.Info, { children: ({ startDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotStartDate, customElement: children, customElementProps: { startDate }, children: _jsx("span", { children: startDate.toString() }) })) }));
57
57
  });
58
58
  StartDate.displayName = 'TimeSlot.StartDate';
59
59
  /**
@@ -76,7 +76,7 @@ StartDate.displayName = 'TimeSlot.StartDate';
76
76
  */
77
77
  export const EndDate = React.forwardRef((props, ref) => {
78
78
  const { asChild, children, className } = props;
79
- return (_jsx(CoreTimeSlot.Info, { children: ({ endDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotEndDate, customElement: children, customElementProps: { endDate }, children: _jsx("span", { children: endDate?.toString() }) })) }));
79
+ return (_jsx(CoreTimeSlot.Info, { children: ({ endDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotEndDate, customElement: children, customElementProps: { endDate }, children: _jsx("span", { children: endDate.toString() }) })) }));
80
80
  });
81
81
  EndDate.displayName = 'TimeSlot.EndDate';
82
82
  /**
@@ -99,7 +99,7 @@ EndDate.displayName = 'TimeSlot.EndDate';
99
99
  */
100
100
  export const Duration = React.forwardRef((props, ref) => {
101
101
  const { asChild, children, className } = props;
102
- return (_jsx(CoreTimeSlot.Info, { children: ({ durationInMinutes }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotDuration, customElement: children, customElementProps: { durationInMinutes }, children: durationInMinutes && _jsx("span", { children: durationInMinutes }) })) }));
102
+ return (_jsx(CoreTimeSlot.Info, { children: ({ durationInMinutes }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotDuration, customElement: children, customElementProps: { durationInMinutes }, children: _jsx("span", { children: durationInMinutes }) })) }));
103
103
  });
104
104
  Duration.displayName = 'TimeSlot.Duration';
105
105
  /**
@@ -85,6 +85,8 @@ export interface TimeSlotsProps {
85
85
  emptyState?: React.ReactNode;
86
86
  /** Content to display while time slots are loading */
87
87
  loadingState?: React.ReactNode;
88
+ /** Content to display when an error occurs while loading time slots */
89
+ errorState?: React.ReactNode;
88
90
  className?: string;
89
91
  }
90
92
  /**
@@ -114,11 +116,11 @@ export interface TimeSlotRepeaterChildProps {
114
116
  /** Whether the slot can be booked */
115
117
  bookable: boolean;
116
118
  /** Start date/time as Date (constructed from the raw localStartDate string) */
117
- startDate: Date | undefined;
119
+ startDate: Date;
118
120
  /** End date/time as Date (constructed from the raw localEndDate string) */
119
- endDate: Date | undefined;
121
+ endDate: Date;
120
122
  /** Duration in minutes */
121
- durationInMinutes: number | null;
123
+ durationInMinutes: number;
122
124
  /** The raw time slot data */
123
125
  timeSlot: TimeSlotType;
124
126
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Book Action - Orchestrates the booking flow
3
+ *
4
+ * 1. Validates booking data with canBook()
5
+ * 2. Creates a booking via createBooking() from @wix/auto_sdk_bookings_bookings
6
+ * 3. Creates a checkout via createCheckout() from @wix/auto_sdk_ecom_checkout
7
+ * 4. Returns the checkoutId for navigation
8
+ */
9
+ import type { BookActionParams, BookResult } from './types.js';
10
+ /**
11
+ * Executes the complete booking flow:
12
+ * 1. Validates that all required data is present
13
+ * 2. Creates a booking with the selected service, time slot, and form data
14
+ * 3. Creates a checkout session for payment (includes contact details from booking)
15
+ * 4. Returns the checkout ID for navigation to the checkout page
16
+ *
17
+ * @param params - The booking action parameters
18
+ * @returns BookResult with the checkoutId
19
+ * @throws Error if validation fails, or booking/checkout creation fails
20
+ */
21
+ export declare function executeBookAction(params: BookActionParams): Promise<BookResult>;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Book Action - Orchestrates the booking flow
3
+ *
4
+ * 1. Validates booking data with canBook()
5
+ * 2. Creates a booking via createBooking() from @wix/auto_sdk_bookings_bookings
6
+ * 3. Creates a checkout via createCheckout() from @wix/auto_sdk_ecom_checkout
7
+ * 4. Returns the checkoutId for navigation
8
+ */
9
+ import { createBooking } from '@wix/auto_sdk_bookings_bookings';
10
+ import { createCheckout } from '@wix/auto_sdk_ecom_checkout';
11
+ import { buildBookingRequest } from './buildBookingRequest.js';
12
+ import { buildCheckoutRequest } from './buildCheckoutRequest.js';
13
+ import { canBook } from './canBook.js';
14
+ /**
15
+ * Executes the complete booking flow:
16
+ * 1. Validates that all required data is present
17
+ * 2. Creates a booking with the selected service, time slot, and form data
18
+ * 3. Creates a checkout session for payment (includes contact details from booking)
19
+ * 4. Returns the checkout ID for navigation to the checkout page
20
+ *
21
+ * @param params - The booking action parameters
22
+ * @returns BookResult with the checkoutId
23
+ * @throws Error if validation fails, or booking/checkout creation fails
24
+ */
25
+ export async function executeBookAction(params) {
26
+ // Step 0: Validate
27
+ if (!canBook({
28
+ serviceSelections: params.serviceSelections,
29
+ formSubmission: params.formSubmission,
30
+ })) {
31
+ throw new Error('Cannot book: missing required data');
32
+ }
33
+ // Step 1: Create booking
34
+ const createBookingRequest = buildBookingRequest(params);
35
+ const bookingResponse = await createBooking(createBookingRequest.booking, {
36
+ participantNotification: createBookingRequest.participantNotification,
37
+ sendSmsReminder: createBookingRequest.sendSmsReminder,
38
+ formSubmission: createBookingRequest.formSubmission,
39
+ });
40
+ const bookingId = bookingResponse.booking?._id;
41
+ const contactDetails = bookingResponse.booking?.contactDetails;
42
+ if (!bookingId) {
43
+ throw new Error('Failed to create booking');
44
+ }
45
+ // Step 2: Create checkout
46
+ // businessLocationId comes from location.id for multi-location support
47
+ const businessLocationId = params.location?.id ?? undefined;
48
+ const checkoutRequest = buildCheckoutRequest({
49
+ bookingId,
50
+ contactDetails,
51
+ businessLocationId,
52
+ });
53
+ const checkoutResponse = await createCheckout(checkoutRequest);
54
+ const checkoutId = checkoutResponse._id;
55
+ if (!checkoutId) {
56
+ throw new Error('Failed to create checkout');
57
+ }
58
+ return { checkoutId };
59
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Builds the createBooking request from BookActionParams
3
+ */
4
+ import type { CreateBookingRequest } from '@wix/auto_sdk_bookings_bookings';
5
+ import type { BookActionParams } from './types.js';
6
+ /**
7
+ * Builds a CreateBookingRequest from the booking action parameters
8
+ *
9
+ * Maps ServiceSelection data to the Booking API's slot format:
10
+ * - serviceId: from serviceSelection.service._id
11
+ * - scheduleId: from serviceSelection.timeSlot.scheduleId
12
+ * - startDate: from serviceSelection.timeSlot.startDate
13
+ * - endDate: from serviceSelection.timeSlot.endDate
14
+ * - timezone: from serviceSelection.timezone
15
+ * - eventId: from serviceSelection.timeSlot.eventInfo.eventId (for classes)
16
+ * - location: from location param
17
+ * - resource: from serviceSelection.resources[0]
18
+ *
19
+ * @param params - The booking action parameters
20
+ * @returns CreateBookingRequest ready to be sent to the API
21
+ */
22
+ export declare function buildBookingRequest(params: BookActionParams): CreateBookingRequest;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Builds the createBooking request from BookActionParams
3
+ */
4
+ /**
5
+ * Derives the selected payment option from service payment settings.
6
+ * Maps service.payment.options to booking API's selectedPaymentOption.
7
+ *
8
+ * @param service - The service containing payment options
9
+ * @returns 'ONLINE' or 'OFFLINE' based on available payment methods
10
+ */
11
+ function deriveSelectedPaymentOption(service) {
12
+ const options = service.payment?.options;
13
+ // Only online payment available
14
+ if (options?.online && !options?.inPerson) {
15
+ return 'ONLINE';
16
+ }
17
+ // Only offline payment available
18
+ if (!options?.online && options?.inPerson) {
19
+ return 'OFFLINE';
20
+ }
21
+ // Both available or neither specified - default to ONLINE
22
+ return 'ONLINE';
23
+ }
24
+ /**
25
+ * Builds a CreateBookingRequest from the booking action parameters
26
+ *
27
+ * Maps ServiceSelection data to the Booking API's slot format:
28
+ * - serviceId: from serviceSelection.service._id
29
+ * - scheduleId: from serviceSelection.timeSlot.scheduleId
30
+ * - startDate: from serviceSelection.timeSlot.startDate
31
+ * - endDate: from serviceSelection.timeSlot.endDate
32
+ * - timezone: from serviceSelection.timezone
33
+ * - eventId: from serviceSelection.timeSlot.eventInfo.eventId (for classes)
34
+ * - location: from location param
35
+ * - resource: from serviceSelection.resources[0]
36
+ *
37
+ * @param params - The booking action parameters
38
+ * @returns CreateBookingRequest ready to be sent to the API
39
+ */
40
+ export function buildBookingRequest(params) {
41
+ const { serviceSelections, location, formSubmission } = params;
42
+ const item = serviceSelections[0]; // Single booking focus
43
+ if (!item) {
44
+ throw new Error('No service selection provided');
45
+ }
46
+ if (!item.timeSlot) {
47
+ throw new Error('No time slot selected for service selection');
48
+ }
49
+ // Get resource from service selection
50
+ const resource = item.resources?.[0];
51
+ return {
52
+ booking: {
53
+ // Payment option derived from service payment settings
54
+ selectedPaymentOption: deriveSelectedPaymentOption(item.service),
55
+ // Participants
56
+ totalParticipants: item.totalParticipants || 1,
57
+ // Booked entity slot
58
+ bookedEntity: {
59
+ slot: {
60
+ serviceId: item.service._id ?? undefined,
61
+ scheduleId: item.timeSlot.scheduleId ?? undefined,
62
+ startDate: item.timeSlot.startDate,
63
+ endDate: item.timeSlot.endDate,
64
+ timezone: params.timezone,
65
+ eventId: item.timeSlot.eventInfo?.eventId ?? undefined,
66
+ resource: resource
67
+ ? { _id: resource._id, name: resource.name }
68
+ : undefined,
69
+ location: location
70
+ ? { _id: location.id, name: location.name }
71
+ : undefined,
72
+ },
73
+ },
74
+ },
75
+ participantNotification: {
76
+ notifyParticipants: true,
77
+ },
78
+ sendSmsReminder: true,
79
+ formSubmission: formSubmission ?? undefined,
80
+ };
81
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Builds the createCheckout request
3
+ */
4
+ import type { CreateCheckoutRequest } from '@wix/auto_sdk_ecom_checkout';
5
+ import type { ContactDetails } from '@wix/auto_sdk_bookings_bookings';
6
+ /**
7
+ * Parameters for building checkout request
8
+ */
9
+ export interface BuildCheckoutParams {
10
+ bookingId: string;
11
+ contactDetails?: ContactDetails;
12
+ businessLocationId?: string;
13
+ }
14
+ /**
15
+ * Builds a checkout request from the booking ID and contact details
16
+ *
17
+ * Contact details come from the booking response and are used to populate:
18
+ * - billingInfo.contactDetails (firstName, lastName, phone)
19
+ * - billingInfo.address (from fullAddress if available)
20
+ * - shippingInfo.shippingDestination.contactDetails
21
+ * - buyerInfo (email, contactId)
22
+ *
23
+ * @param params - The checkout parameters
24
+ * @returns CreateCheckoutRequest ready to be sent to the API
25
+ */
26
+ export declare function buildCheckoutRequest(params: BuildCheckoutParams): CreateCheckoutRequest;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Builds the createCheckout request
3
+ */
4
+ import { ChannelType } from '@wix/auto_sdk_ecom_checkout';
5
+ const BOOKINGS_APP_DEF_ID = '13d21c63-b5ec-5912-8397-c3a5ddb27a97';
6
+ /**
7
+ * Builds a checkout request from the booking ID and contact details
8
+ *
9
+ * Contact details come from the booking response and are used to populate:
10
+ * - billingInfo.contactDetails (firstName, lastName, phone)
11
+ * - billingInfo.address (from fullAddress if available)
12
+ * - shippingInfo.shippingDestination.contactDetails
13
+ * - buyerInfo (email, contactId)
14
+ *
15
+ * @param params - The checkout parameters
16
+ * @returns CreateCheckoutRequest ready to be sent to the API
17
+ */
18
+ export function buildCheckoutRequest(params) {
19
+ const { bookingId, contactDetails, businessLocationId } = params;
20
+ // Build checkoutInfo incrementally for readability
21
+ const checkoutInfo = {};
22
+ // businessLocationId for multi-location support
23
+ if (businessLocationId) {
24
+ checkoutInfo.businessLocationId = businessLocationId;
25
+ }
26
+ // Billing info and shipping info from contact details
27
+ if (contactDetails) {
28
+ checkoutInfo.billingInfo = {
29
+ contactDetails,
30
+ ...(contactDetails.fullAddress?.country
31
+ ? { address: { ...contactDetails.fullAddress } }
32
+ : {}),
33
+ };
34
+ checkoutInfo.shippingInfo = {
35
+ shippingDestination: {
36
+ contactDetails,
37
+ },
38
+ };
39
+ }
40
+ // Buyer info with email and contactId
41
+ if (contactDetails?.email) {
42
+ checkoutInfo.buyerInfo = {
43
+ email: contactDetails.email,
44
+ ...(contactDetails.contactId
45
+ ? { contactId: contactDetails.contactId }
46
+ : {}),
47
+ };
48
+ }
49
+ return {
50
+ channelType: ChannelType.WEB,
51
+ lineItems: [
52
+ {
53
+ quantity: 1,
54
+ catalogReference: {
55
+ catalogItemId: bookingId,
56
+ appId: BOOKINGS_APP_DEF_ID,
57
+ },
58
+ },
59
+ ],
60
+ checkoutInfo,
61
+ };
62
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Validation logic for the Book action
3
+ */
4
+ import type { ServiceSelection } from '../booking.js';
5
+ import type { FormValues } from '@wix/form-public';
6
+ /**
7
+ * Parameters for canBook validation
8
+ */
9
+ export interface CanBookParams {
10
+ serviceSelections: ServiceSelection[];
11
+ formSubmission: FormValues | null;
12
+ }
13
+ /**
14
+ * Validates if booking can be executed
15
+ * Checks that all required data is present:
16
+ * - At least one service selection
17
+ * - Each service selection has a time slot selected
18
+ * - Form submission filled
19
+ *
20
+ * @param params - The booking data to validate
21
+ * @returns true if booking can proceed, false otherwise
22
+ */
23
+ export declare function canBook(params: CanBookParams): boolean;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Validation logic for the Book action
3
+ */
4
+ /**
5
+ * Validates if booking can be executed
6
+ * Checks that all required data is present:
7
+ * - At least one service selection
8
+ * - Each service selection has a time slot selected
9
+ * - Form submission filled
10
+ *
11
+ * @param params - The booking data to validate
12
+ * @returns true if booking can proceed, false otherwise
13
+ */
14
+ export function canBook(params) {
15
+ const { serviceSelections, formSubmission } = params;
16
+ return (serviceSelections.length > 0 &&
17
+ serviceSelections.every((item) => item.timeSlot !== undefined) &&
18
+ formSubmission !== null);
19
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Book Action - Exports
3
+ */
4
+ export { executeBookAction } from './bookAction.js';
5
+ export { canBook, type CanBookParams } from './canBook.js';
6
+ export { buildBookingRequest } from './buildBookingRequest.js';
7
+ export { buildCheckoutRequest, type BuildCheckoutParams, } from './buildCheckoutRequest.js';
8
+ export type { BookProps, BookResult, BookingError, BookActionParams, BookChildProps, } from './types.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Book Action - Exports
3
+ */
4
+ export { executeBookAction } from './bookAction.js';
5
+ export { canBook } from './canBook.js';
6
+ export { buildBookingRequest } from './buildBookingRequest.js';
7
+ export { buildCheckoutRequest, } from './buildCheckoutRequest.js';
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Types for the Book action
3
+ */
4
+ import type { ServiceSelection, Location } from '../booking.js';
5
+ import type { FormValues } from '@wix/form-public';
6
+ /**
7
+ * Result returned from the book action
8
+ */
9
+ export type BookResult = {
10
+ checkoutId: string;
11
+ };
12
+ /**
13
+ * Error object for booking errors
14
+ */
15
+ export interface BookingError {
16
+ message: string;
17
+ errorObject: unknown;
18
+ }
19
+ /**
20
+ * Parameters for executing the book action
21
+ */
22
+ export interface BookActionParams {
23
+ serviceSelections: ServiceSelection[];
24
+ location: Location | null;
25
+ formSubmission: FormValues | null;
26
+ timezone?: string;
27
+ }
28
+ /**
29
+ * Props passed to children of Book component
30
+ */
31
+ export interface BookChildProps {
32
+ isLoading: boolean;
33
+ error: BookingError | null;
34
+ canBook: boolean;
35
+ onClick: () => void;
36
+ disabled: boolean;
37
+ }
38
+ /**
39
+ * Props for the Book headless component
40
+ */
41
+ export interface BookProps {
42
+ asChild?: boolean;
43
+ children?: React.ReactNode | ((props: BookChildProps) => React.ReactNode);
44
+ className?: string;
45
+ label?: string;
46
+ loadingState?: string;
47
+ disabled?: boolean;
48
+ onCheckoutRequired?: (result: BookResult) => void;
49
+ onError?: (error: BookingError) => void;
50
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Types for the Book action
3
+ */
4
+ export {};
@@ -7,6 +7,7 @@ import { type Signal } from '@wix/services-definitions/core-services/signals';
7
7
  import type { Service } from '@wix/auto_sdk_bookings_services';
8
8
  import type { Resource, EventInfo } from '@wix/auto_sdk_bookings_availability-time-slots';
9
9
  import type { FormValues } from '@wix/form-public';
10
+ import type { BookResult } from './book-action/types.js';
10
11
  export declare const ANY_STAFF_MEMBER_ID = "__any_staff_member__";
11
12
  /**
12
13
  * Minimal time slot data for service selections
@@ -62,6 +63,7 @@ export interface BookingServiceActions {
62
63
  setFormSubmission(values: FormValues | null): void;
63
64
  clearFormSubmission(): void;
64
65
  clearAll(): void;
66
+ book(): Promise<BookResult>;
65
67
  }
66
68
  /**
67
69
  * Booking Service interface