@wix/headless-bookings 1.0.4 → 1.0.6

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__/booking/booking-client-service.js +4 -1
  2. package/cjs/dist/api/create-booking/index.d.ts +4 -4
  3. package/cjs/dist/api/create-bulk-booking/index.d.ts +54 -0
  4. package/cjs/dist/api/create-bulk-booking/index.js +54 -0
  5. package/cjs/dist/api/create-multi-service-booking/index.d.ts +53 -0
  6. package/cjs/dist/api/create-multi-service-booking/index.js +47 -0
  7. package/cjs/dist/api/index.d.ts +4 -1
  8. package/cjs/dist/api/index.js +32 -13
  9. package/cjs/dist/react/core/booking/Booking.d.ts +6 -1
  10. package/cjs/dist/react/core/booking/Booking.js +2 -0
  11. package/cjs/dist/services/booking/book-action/bookAction.js +39 -27
  12. package/cjs/dist/services/booking/book-action/buildBookingRequest.d.ts +1 -1
  13. package/cjs/dist/services/booking/book-action/buildBookingRequest.js +4 -2
  14. package/cjs/dist/services/booking/book-action/buildCartRequest.d.ts +20 -2
  15. package/cjs/dist/services/booking/book-action/buildCartRequest.js +24 -13
  16. package/cjs/dist/services/booking/book-action/buildDayRangeBookingRequest.d.ts +37 -0
  17. package/cjs/dist/services/booking/book-action/buildDayRangeBookingRequest.js +70 -0
  18. package/cjs/dist/services/booking/book-action/canBook.d.ts +2 -1
  19. package/cjs/dist/services/booking/book-action/canBook.js +4 -2
  20. package/cjs/dist/services/booking/book-action/createBookingsForCart.d.ts +22 -0
  21. package/cjs/dist/services/booking/book-action/createBookingsForCart.js +69 -0
  22. package/cjs/dist/services/booking/book-action/dayRange.d.ts +34 -0
  23. package/cjs/dist/services/booking/book-action/dayRange.js +43 -0
  24. package/cjs/dist/services/booking/book-action/index.d.ts +9 -6
  25. package/cjs/dist/services/booking/book-action/index.js +23 -13
  26. package/cjs/dist/services/booking/book-action/types.d.ts +15 -0
  27. package/cjs/dist/services/booking/booking.d.ts +31 -2
  28. package/cjs/dist/services/booking/booking.js +8 -0
  29. package/cjs/dist/services/index.d.ts +1 -1
  30. package/cjs/dist/services/index.js +6 -1
  31. package/cjs/dist/services/payment/payment.d.ts +4 -0
  32. package/cjs/dist/services/payment/payment.def.d.ts +6 -0
  33. package/cjs/dist/services/payment/payment.js +39 -17
  34. package/dist/__mocks__/booking/booking-client-service.js +4 -1
  35. package/dist/api/create-booking/index.d.ts +4 -4
  36. package/dist/api/create-bulk-booking/index.d.ts +54 -0
  37. package/dist/api/create-bulk-booking/index.js +51 -0
  38. package/dist/api/create-multi-service-booking/index.d.ts +53 -0
  39. package/dist/api/create-multi-service-booking/index.js +44 -0
  40. package/dist/api/index.d.ts +4 -1
  41. package/dist/api/index.js +3 -0
  42. package/dist/react/core/booking/Booking.d.ts +6 -1
  43. package/dist/react/core/booking/Booking.js +2 -0
  44. package/dist/services/booking/book-action/bookAction.js +34 -22
  45. package/dist/services/booking/book-action/buildBookingRequest.d.ts +1 -1
  46. package/dist/services/booking/book-action/buildBookingRequest.js +4 -2
  47. package/dist/services/booking/book-action/buildCartRequest.d.ts +20 -2
  48. package/dist/services/booking/book-action/buildCartRequest.js +23 -13
  49. package/dist/services/booking/book-action/buildDayRangeBookingRequest.d.ts +37 -0
  50. package/dist/services/booking/book-action/buildDayRangeBookingRequest.js +67 -0
  51. package/dist/services/booking/book-action/canBook.d.ts +2 -1
  52. package/dist/services/booking/book-action/canBook.js +4 -2
  53. package/dist/services/booking/book-action/createBookingsForCart.d.ts +22 -0
  54. package/dist/services/booking/book-action/createBookingsForCart.js +66 -0
  55. package/dist/services/booking/book-action/dayRange.d.ts +34 -0
  56. package/dist/services/booking/book-action/dayRange.js +39 -0
  57. package/dist/services/booking/book-action/index.d.ts +9 -6
  58. package/dist/services/booking/book-action/index.js +9 -6
  59. package/dist/services/booking/book-action/types.d.ts +15 -0
  60. package/dist/services/booking/booking.d.ts +31 -2
  61. package/dist/services/booking/booking.js +8 -0
  62. package/dist/services/index.d.ts +1 -1
  63. package/dist/services/index.js +1 -1
  64. package/dist/services/payment/payment.d.ts +4 -0
  65. package/dist/services/payment/payment.def.d.ts +6 -0
  66. package/dist/services/payment/payment.js +39 -17
  67. package/package.json +2 -2
@@ -57,7 +57,21 @@ function getAdditionalInfo(service, lineItemId) {
57
57
  : undefined;
58
58
  return { lineItemId, customPrice };
59
59
  }
60
- function getPaymentOption(service, hasDeposit) {
60
+ /**
61
+ * Resolves the line item's `paymentOption`.
62
+ *
63
+ * Precedence: explicit consumer choice (`selectedPaymentOption`) wins over the
64
+ * service-config heuristic so the preview always agrees with what `book()`
65
+ * will send. When unset, the existing heuristic is preserved for backward
66
+ * compat.
67
+ */
68
+ function getPaymentOption(service, hasDeposit, selectedPaymentOption) {
69
+ if (selectedPaymentOption === 'OFFLINE') {
70
+ return 'FULL_PAYMENT_OFFLINE';
71
+ }
72
+ if (selectedPaymentOption === 'ONLINE') {
73
+ return hasDeposit ? 'DEPOSIT_ONLINE' : 'FULL_PAYMENT_ONLINE';
74
+ }
61
75
  const options = service.payment?.options;
62
76
  if (options?.inPerson) {
63
77
  return 'FULL_PAYMENT_OFFLINE';
@@ -68,7 +82,7 @@ function getPaymentOption(service, hasDeposit) {
68
82
  }
69
83
  return 'FULL_PAYMENT_ONLINE';
70
84
  }
71
- function buildLineItem(service, totalParticipants, lineItemId) {
85
+ function buildLineItem(service, totalParticipants, lineItemId, selectedPaymentOption) {
72
86
  // Get fixed price from service
73
87
  const servicePrice = service.payment?.fixed?.price?.value;
74
88
  const price = Number(servicePrice || 0) * totalParticipants;
@@ -79,7 +93,7 @@ function buildLineItem(service, totalParticipants, lineItemId) {
79
93
  ? Number(depositValue) * totalParticipants
80
94
  : undefined;
81
95
  // Get payment option from service configuration (pass hasDeposit for correct API behavior)
82
- const paymentOption = getPaymentOption(service, hasDeposit);
96
+ const paymentOption = getPaymentOption(service, hasDeposit, selectedPaymentOption);
83
97
  return {
84
98
  _id: lineItemId,
85
99
  catalogReference: {
@@ -117,7 +131,7 @@ async function getPreviewedPrice(service, localStartDate, localEndDate, timeZone
117
131
  deposit: result.priceInfo?.deposit ?? null,
118
132
  };
119
133
  }
120
- async function buildLineItemWithPreview(item, totalParticipants) {
134
+ async function buildLineItemWithPreview(item, totalParticipants, selectedPaymentOption) {
121
135
  if (item.localStartDate &&
122
136
  item.localEndDate &&
123
137
  getDurationRange(item.service)) {
@@ -142,18 +156,18 @@ async function buildLineItemWithPreview(item, totalParticipants) {
142
156
  price: String(previewedPrice.price),
143
157
  ...(deposit ? { depositAmount: String(deposit) } : {}),
144
158
  quantity: 1,
145
- paymentOption: getPaymentOption(item.service, hasDeposit),
159
+ paymentOption: getPaymentOption(item.service, hasDeposit, selectedPaymentOption),
146
160
  };
147
161
  }
148
162
  }
149
- return buildLineItem(item.service, totalParticipants, item.lineItemId);
163
+ return buildLineItem(item.service, totalParticipants, item.lineItemId, selectedPaymentOption);
150
164
  }
151
- async function calculatePaymentDetails(pricingServiceSelections, totalParticipants) {
165
+ async function calculatePaymentDetails(pricingServiceSelections, totalParticipants, selectedPaymentOption) {
152
166
  const data = pricingServiceSelections.map((selection) => ({
153
167
  ...selection,
154
168
  lineItemId: selection.lineItemId || (0, guid_js_1.generateLineItemId)(),
155
169
  }));
156
- const lineItems = await Promise.all(data.map((item) => buildLineItemWithPreview(item, totalParticipants)));
170
+ const lineItems = await Promise.all(data.map((item) => buildLineItemWithPreview(item, totalParticipants, selectedPaymentOption)));
157
171
  const response = await (0, auto_sdk_ecom_totals_calculator_1.calculateTotals)({
158
172
  lineItems,
159
173
  });
@@ -184,7 +198,7 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
184
198
  const errorSignal = signalsService.signal(null);
185
199
  const serviceOptionsSignal = signalsService.signal(config.serviceOptions || DEFAULT_SERVICE_OPTIONS);
186
200
  // Internal calculate function
187
- const calculate = async (pricingServiceSelections, totalParticipants) => {
201
+ const calculate = async (pricingServiceSelections, totalParticipants, selectedPaymentOption) => {
188
202
  if (!pricingServiceSelections.length) {
189
203
  paymentDetailsSignal.set(null);
190
204
  return;
@@ -192,7 +206,7 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
192
206
  isLoadingSignal.set(true);
193
207
  errorSignal.set(null);
194
208
  try {
195
- const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants);
209
+ const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants, selectedPaymentOption);
196
210
  paymentDetailsSignal.set(paymentDetails);
197
211
  }
198
212
  catch (error) {
@@ -220,24 +234,30 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
220
234
  // Mode 2: Lazy loading - pricingServiceSelections provided via internal config
221
235
  else if (config.pricingServiceSelections?.length) {
222
236
  serviceOptionsSignal.set(deriveServiceOptions(config.pricingServiceSelections));
223
- void calculate(config.pricingServiceSelections, config.totalParticipants ?? 1);
237
+ void calculate(config.pricingServiceSelections, config.totalParticipants ?? 1, config.selectedPaymentOption);
224
238
  }
225
239
  // Mode 3: Reactive - observe BookingService signals (client-side only)
226
240
  else if (typeof window !== 'undefined' && bookingService) {
227
- // Computed signal that changes when services or selected time slots change (not participants)
241
+ // Computed signal that changes when services, selected time slots, or
242
+ // the consumer-selected payment option change (not participants).
228
243
  const servicesKey = signalsService.computed(() => {
229
244
  const selections = bookingService?.serviceSelections.get() || [];
230
245
  const timezone = bookingService?.timezone.get();
246
+ const selectedPaymentOption = bookingService?.selectedPaymentOption.get();
231
247
  return (selections
232
248
  .map((s) => `${s.service._id}:${s.timeSlot?.startDate ?? ''}:${s.timeSlot?.endDate ?? ''}`)
233
- .join(',') + `:${timezone ?? ''}`);
249
+ .join(',') +
250
+ `:${timezone ?? ''}` +
251
+ `:${selectedPaymentOption ?? ''}`);
234
252
  });
235
253
  signalsService.effect(() => {
236
- // Subscribe to servicesKey to trigger effect when service or time slot changes
254
+ // Subscribe to servicesKey to trigger effect when service, time
255
+ // slot, or selected payment option changes.
237
256
  servicesKey.get();
238
257
  // Read current selections without subscribing (peek)
239
258
  const selections = bookingService?.serviceSelections.peek() || [];
240
259
  const timezone = bookingService?.timezone.peek();
260
+ const selectedPaymentOption = bookingService?.selectedPaymentOption.peek();
241
261
  // For single service mode, take the first selection
242
262
  const firstSelection = selections[0];
243
263
  const numParticipants = firstSelection?.totalParticipants || 1;
@@ -251,7 +271,7 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
251
271
  if (pricingServiceSelections.length) {
252
272
  serviceOptionsSignal.set(deriveServiceOptions(pricingServiceSelections));
253
273
  }
254
- void calculate(pricingServiceSelections, numParticipants);
274
+ void calculate(pricingServiceSelections, numParticipants, selectedPaymentOption);
255
275
  });
256
276
  }
257
277
  return {
@@ -267,10 +287,12 @@ exports.PaymentService = services_definitions_1.implementService.withConfig()(pa
267
287
  *
268
288
  * @param params.pricingServiceSelections - Pricing service selections for calculation
269
289
  * @param params.totalParticipants - Number of participants (default: 1)
290
+ * @param params.selectedPaymentOption - Optional consumer-selected payment option;
291
+ * overrides the service-config heuristic on each line item's `paymentOption`.
270
292
  */
271
293
  async function loadPaymentConfig(params) {
272
294
  try {
273
- const { pricingServiceSelections, totalParticipants = 1 } = params;
295
+ const { pricingServiceSelections, totalParticipants = 1, selectedPaymentOption, } = params;
274
296
  if (!pricingServiceSelections.length) {
275
297
  return {
276
298
  type: 'success',
@@ -280,7 +302,7 @@ async function loadPaymentConfig(params) {
280
302
  },
281
303
  };
282
304
  }
283
- const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants);
305
+ const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, totalParticipants, selectedPaymentOption);
284
306
  return {
285
307
  type: 'success',
286
308
  config: {
@@ -24,16 +24,18 @@ export function createMockSignalsService() {
24
24
  };
25
25
  }
26
26
  export function createMockBookingService(config = {}) {
27
- const { serviceSelections = [], location = null, timezone = 'UTC', formSubmission = null, } = config;
27
+ const { serviceSelections = [], location = null, timezone = 'UTC', formSubmission = null, selectedPaymentOption, } = config;
28
28
  const serviceSelectionsSignal = mockSignal(serviceSelections);
29
29
  const locationSignal = mockSignal(location);
30
30
  const timezoneSignal = mockSignal(timezone);
31
31
  const formSubmissionSignal = mockSignal(formSubmission);
32
+ const selectedPaymentOptionSignal = mockSignal(selectedPaymentOption);
32
33
  return {
33
34
  serviceSelections: serviceSelectionsSignal,
34
35
  location: locationSignal,
35
36
  timezone: timezoneSignal,
36
37
  formSubmission: formSubmissionSignal,
38
+ selectedPaymentOption: selectedPaymentOptionSignal,
37
39
  actions: {
38
40
  updateItem: vi.fn(),
39
41
  setItem: vi.fn(),
@@ -48,6 +50,7 @@ export function createMockBookingService(config = {}) {
48
50
  setTimezone: vi.fn(),
49
51
  setFormSubmission: vi.fn(),
50
52
  clearFormSubmission: vi.fn(),
53
+ setSelectedPaymentOption: vi.fn(),
51
54
  clearAll: vi.fn(),
52
55
  book: vi.fn(),
53
56
  },
@@ -5,15 +5,16 @@
5
5
  import { type Booking, type CreateBookingResponse, type ParticipantNotification } from '@wix/auto_sdk_bookings_bookings';
6
6
  import type { FormValues } from '@wix/forms/components';
7
7
  /**
8
- * Options for creating a booking
8
+ * Options for creating a booking. `null` is accepted on optional fields so
9
+ * callers can forward the SDK's `T | null` shape without coercion.
9
10
  */
10
11
  export interface CreateBookingOptions {
11
12
  /** Notification settings for participants */
12
13
  participantNotification?: ParticipantNotification;
13
14
  /** Whether to send SMS reminder */
14
- sendSmsReminder?: boolean;
15
+ sendSmsReminder?: boolean | null;
15
16
  /** Form submission data */
16
- formSubmission?: FormValues;
17
+ formSubmission?: FormValues | null;
17
18
  }
18
19
  /**
19
20
  * Creates a booking with the specified booking data and options
@@ -39,4 +40,3 @@ export interface CreateBookingOptions {
39
40
  * ```
40
41
  */
41
42
  export declare function createBooking(booking: Booking, options?: CreateBookingOptions): Promise<CreateBookingResponse>;
42
- export type { Booking, CreateBookingRequest, CreateBookingResponse, ParticipantNotification, ContactDetails, } from '@wix/auto_sdk_bookings_bookings';
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Bulk Create Booking API
3
+ * Wrapper for the Wix Bookings bulkCreateBooking SDK function.
4
+ *
5
+ * Used for the 1-day case of the DAY-range expansion: a single APPOINTMENT
6
+ * service whose duration is configured as a DAY range, with exactly one
7
+ * per-day availability slot picked. The 2-8 day case is routed to
8
+ * createMultiServiceBooking.
9
+ */
10
+ import { type CreateBookingInfo, type BulkCreateBookingResponse, type ParticipantNotification } from '@wix/auto_sdk_bookings_bookings';
11
+ /**
12
+ * Options for creating bookings in bulk.
13
+ *
14
+ * `formSubmission` is intentionally absent: the SDK's
15
+ * `BulkCreateBookingOptions` does not accept it. Customer details flow
16
+ * through the cart layer instead. We always set `returnFullEntity: true` so
17
+ * the response includes per-booking entities (with `contactDetails`) for
18
+ * cart wiring.
19
+ */
20
+ export interface BulkCreateBookingOptions {
21
+ /** Notification settings for participants. Applied to every entry. */
22
+ participantNotification?: ParticipantNotification;
23
+ /** Whether to send SMS reminder. Applied to every entry. */
24
+ sendSmsReminder?: boolean;
25
+ }
26
+ /**
27
+ * Creates 1-8 bookings in a single bulk call.
28
+ *
29
+ * Per the SDK contract, `createBookingsInfo` accepts 1-8 bookings. When the
30
+ * DAY-range routing picks exactly one per-day slot, this wrapper is invoked
31
+ * with a single-element array. Validation of constraints is delegated to the
32
+ * SDK; this wrapper only forwards the request and pins
33
+ * `returnFullEntity: true` so the caller can read
34
+ * `results[].item.contactDetails` for downstream cart construction.
35
+ *
36
+ * @param createBookingsInfo - 1-8 single-service booking specifications
37
+ * @param options - Optional notification / SMS settings (applied to every entry)
38
+ * @returns Promise resolving to the bulk-create response
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const response = await bulkCreateBooking(
43
+ * [{ booking: bookingDay1 }],
44
+ * {
45
+ * participantNotification: { notifyParticipants: true },
46
+ * sendSmsReminder: true,
47
+ * }
48
+ * );
49
+ * const ids = response.results
50
+ * ?.map((r) => r.item?._id)
51
+ * .filter(Boolean);
52
+ * ```
53
+ */
54
+ export declare function bulkCreateBooking(createBookingsInfo: CreateBookingInfo[], options?: BulkCreateBookingOptions): Promise<BulkCreateBookingResponse>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Bulk Create Booking API
3
+ * Wrapper for the Wix Bookings bulkCreateBooking SDK function.
4
+ *
5
+ * Used for the 1-day case of the DAY-range expansion: a single APPOINTMENT
6
+ * service whose duration is configured as a DAY range, with exactly one
7
+ * per-day availability slot picked. The 2-8 day case is routed to
8
+ * createMultiServiceBooking.
9
+ */
10
+ import { bulkCreateBooking as bulkCreateBookingSdk, } from '@wix/auto_sdk_bookings_bookings';
11
+ /**
12
+ * Creates 1-8 bookings in a single bulk call.
13
+ *
14
+ * Per the SDK contract, `createBookingsInfo` accepts 1-8 bookings. When the
15
+ * DAY-range routing picks exactly one per-day slot, this wrapper is invoked
16
+ * with a single-element array. Validation of constraints is delegated to the
17
+ * SDK; this wrapper only forwards the request and pins
18
+ * `returnFullEntity: true` so the caller can read
19
+ * `results[].item.contactDetails` for downstream cart construction.
20
+ *
21
+ * @param createBookingsInfo - 1-8 single-service booking specifications
22
+ * @param options - Optional notification / SMS settings (applied to every entry)
23
+ * @returns Promise resolving to the bulk-create response
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const response = await bulkCreateBooking(
28
+ * [{ booking: bookingDay1 }],
29
+ * {
30
+ * participantNotification: { notifyParticipants: true },
31
+ * sendSmsReminder: true,
32
+ * }
33
+ * );
34
+ * const ids = response.results
35
+ * ?.map((r) => r.item?._id)
36
+ * .filter(Boolean);
37
+ * ```
38
+ */
39
+ export function bulkCreateBooking(createBookingsInfo, options) {
40
+ // Apply the shared participantNotification / sendSmsReminder onto each
41
+ // CreateBookingInfo entry. The SDK's per-call options object only carries
42
+ // `returnFullEntity`, so notification settings live on each entry.
43
+ const enriched = createBookingsInfo.map((info) => ({
44
+ ...info,
45
+ participantNotification: info.participantNotification ?? options?.participantNotification,
46
+ sendSmsReminder: info.sendSmsReminder ?? options?.sendSmsReminder,
47
+ }));
48
+ return bulkCreateBookingSdk(enriched, {
49
+ returnFullEntity: true,
50
+ });
51
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Create Multi-Service Booking API
3
+ * Wrapper for the Wix Bookings createMultiServiceBooking SDK function.
4
+ *
5
+ * Used for the 2-8 day case of the DAY-range expansion: a single
6
+ * APPOINTMENT service whose duration is configured as a DAY range, with
7
+ * 2-8 per-day availability slots picked. One Booking entry is sent per
8
+ * picked slot.
9
+ */
10
+ import { type Booking, type CreateMultiServiceBookingResponse, type ParticipantNotification } from '@wix/auto_sdk_bookings_bookings';
11
+ /**
12
+ * Options for creating a multi-service booking.
13
+ *
14
+ * `formSubmission` is intentionally absent: the SDK's
15
+ * `CreateMultiServiceBookingOptions` does not accept it. Customer details
16
+ * flow through the cart layer instead. We always set `returnFullEntity: true`
17
+ * so the response includes per-booking entities (with `contactDetails`) for
18
+ * cart wiring.
19
+ */
20
+ export interface CreateMultiServiceBookingOptions {
21
+ /** Notification settings for participants */
22
+ participantNotification?: ParticipantNotification;
23
+ /** Whether to send SMS reminder */
24
+ sendSmsReminder?: boolean;
25
+ }
26
+ /**
27
+ * Creates a multi-service booking from 2-8 single-service booking specs.
28
+ *
29
+ * Per the SDK contract, the bookings array must contain 2-8 APPOINTMENT
30
+ * bookings at the same location with sequential, gapless scheduling.
31
+ * Validation of those constraints is delegated to the SDK; this wrapper only
32
+ * forwards the request and pins `returnFullEntity: true` so the caller can
33
+ * read `bookings[].booking.contactDetails` for downstream cart construction.
34
+ *
35
+ * @param bookings - 2 to 8 single-service booking specifications
36
+ * @param options - Optional notification / SMS settings
37
+ * @returns Promise resolving to the created multi-service booking response
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const response = await createMultiServiceBooking(
42
+ * [bookingDay1, bookingDay2, bookingDay3],
43
+ * {
44
+ * participantNotification: { notifyParticipants: true },
45
+ * sendSmsReminder: true,
46
+ * }
47
+ * );
48
+ * const ids = response.multiServiceBooking?.bookings
49
+ * ?.map((b) => b.bookingId)
50
+ * .filter(Boolean);
51
+ * ```
52
+ */
53
+ export declare function createMultiServiceBooking(bookings: Booking[], options?: CreateMultiServiceBookingOptions): Promise<CreateMultiServiceBookingResponse>;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Create Multi-Service Booking API
3
+ * Wrapper for the Wix Bookings createMultiServiceBooking SDK function.
4
+ *
5
+ * Used for the 2-8 day case of the DAY-range expansion: a single
6
+ * APPOINTMENT service whose duration is configured as a DAY range, with
7
+ * 2-8 per-day availability slots picked. One Booking entry is sent per
8
+ * picked slot.
9
+ */
10
+ import { createMultiServiceBooking as createMultiServiceBookingSdk, } from '@wix/auto_sdk_bookings_bookings';
11
+ /**
12
+ * Creates a multi-service booking from 2-8 single-service booking specs.
13
+ *
14
+ * Per the SDK contract, the bookings array must contain 2-8 APPOINTMENT
15
+ * bookings at the same location with sequential, gapless scheduling.
16
+ * Validation of those constraints is delegated to the SDK; this wrapper only
17
+ * forwards the request and pins `returnFullEntity: true` so the caller can
18
+ * read `bookings[].booking.contactDetails` for downstream cart construction.
19
+ *
20
+ * @param bookings - 2 to 8 single-service booking specifications
21
+ * @param options - Optional notification / SMS settings
22
+ * @returns Promise resolving to the created multi-service booking response
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const response = await createMultiServiceBooking(
27
+ * [bookingDay1, bookingDay2, bookingDay3],
28
+ * {
29
+ * participantNotification: { notifyParticipants: true },
30
+ * sendSmsReminder: true,
31
+ * }
32
+ * );
33
+ * const ids = response.multiServiceBooking?.bookings
34
+ * ?.map((b) => b.bookingId)
35
+ * .filter(Boolean);
36
+ * ```
37
+ */
38
+ export function createMultiServiceBooking(bookings, options) {
39
+ return createMultiServiceBookingSdk(bookings, {
40
+ participantNotification: options?.participantNotification,
41
+ sendSmsReminder: options?.sendSmsReminder,
42
+ returnFullEntity: true,
43
+ });
44
+ }
@@ -6,7 +6,10 @@
6
6
  */
7
7
  export { getServiceById, getServiceBySlug, queryServices, convertPlatformFilterToApiFilter, type PagingMetadata, type QueryServicesRequest, SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from './query-services/index.js';
8
8
  export { fetchAvailability } from './fetch-availability/index.js';
9
- export { createBooking, type CreateBookingOptions, type Booking, type CreateBookingRequest, type CreateBookingResponse, type ParticipantNotification, type ContactDetails, } from './create-booking/index.js';
9
+ export * from '@wix/auto_sdk_bookings_bookings';
10
+ export { createBooking, type CreateBookingOptions, } from './create-booking/index.js';
11
+ export { bulkCreateBooking, type BulkCreateBookingOptions, } from './create-bulk-booking/index.js';
12
+ export { createMultiServiceBooking, type CreateMultiServiceBookingOptions, } from './create-multi-service-booking/index.js';
10
13
  export { createCart, ChannelType, type CreateCartOptions, type Cart, } from './create-cart/index.js';
11
14
  export { calculateCart, type CalculateCartResponse, type CartSummary, } from './calculate-cart/index.js';
12
15
  export { placeOrder, type PlaceOrderOptions, type PlaceOrderResponse, } from './place-order/index.js';
package/dist/api/index.js CHANGED
@@ -6,7 +6,10 @@
6
6
  */
7
7
  export { getServiceById, getServiceBySlug, queryServices, convertPlatformFilterToApiFilter, SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from './query-services/index.js';
8
8
  export { fetchAvailability } from './fetch-availability/index.js';
9
+ export * from '@wix/auto_sdk_bookings_bookings';
9
10
  export { createBooking, } from './create-booking/index.js';
11
+ export { bulkCreateBooking, } from './create-bulk-booking/index.js';
12
+ export { createMultiServiceBooking, } from './create-multi-service-booking/index.js';
10
13
  export { createCart, ChannelType, } from './create-cart/index.js';
11
14
  export { calculateCart, } from './calculate-cart/index.js';
12
15
  export { placeOrder, } from './place-order/index.js';
@@ -3,7 +3,7 @@
3
3
  * Provides low-level access to BookingService via render props and service setup
4
4
  */
5
5
  import React from 'react';
6
- import { type BookingServiceConfig, type ServiceSelection, type BookingServiceActions } from '../../../services/booking/booking.js';
6
+ import { type BookingServiceConfig, type ServiceSelection, type BookingServiceActions, type SelectedPaymentOption } from '../../../services/booking/booking.js';
7
7
  import type { FormValues } from '@wix/forms/components';
8
8
  import type { TimeSlot, Resource } from '@wix/auto_sdk_bookings_availability-time-slots';
9
9
  import { type Service, type V2Location as ServiceLocationType } from '@wix/auto_sdk_bookings_services';
@@ -40,6 +40,11 @@ export interface BookingData {
40
40
  location: ServiceLocationType | null;
41
41
  timezone: string | undefined;
42
42
  formSubmission: FormValues | null;
43
+ /**
44
+ * Consumer-selected payment option. `undefined` means the service-config
45
+ * heuristic applies. Set via `actions.setSelectedPaymentOption`.
46
+ */
47
+ selectedPaymentOption: SelectedPaymentOption | undefined;
43
48
  actions: BookingServiceActions;
44
49
  }
45
50
  /**
@@ -105,6 +105,7 @@ export function Data(props) {
105
105
  const bookingLocation = bookingService.location.get();
106
106
  const timezone = bookingService.timezone.get();
107
107
  const formSubmission = bookingService.formSubmission.get();
108
+ const selectedPaymentOption = bookingService.selectedPaymentOption.get();
108
109
  const { actions } = bookingService;
109
110
  const location = resolveLocation(bookingLocation, serviceSelections);
110
111
  return children({
@@ -112,6 +113,7 @@ export function Data(props) {
112
113
  location,
113
114
  timezone,
114
115
  formSubmission,
116
+ selectedPaymentOption,
115
117
  actions,
116
118
  });
117
119
  }
@@ -1,11 +1,11 @@
1
- import { createBooking } from '../../../api/create-booking/index.js';
2
1
  import { createCart } from '../../../api/create-cart/index.js';
3
2
  import { calculateCart } from '../../../api/calculate-cart/index.js';
4
3
  import { placeOrder } from '../../../api/place-order/index.js';
5
- import { buildBookingRequest } from './buildBookingRequest.js';
6
- import { buildCartRequest } from './buildCartRequest.js';
4
+ import { buildCartRequest, buildDayRangeCartRequest, } from './buildCartRequest.js';
7
5
  import { canBook } from './canBook.js';
6
+ import { createBookingsForCart } from './createBookingsForCart.js';
8
7
  import { isCheckoutRequired } from './isCheckoutRequired.js';
8
+ import { isDayRangeService } from './dayRange.js';
9
9
  import { BookResultType, } from './types.js';
10
10
  export async function executeBookAction(params) {
11
11
  if (!canBook({
@@ -14,30 +14,40 @@ export async function executeBookAction(params) {
14
14
  })) {
15
15
  throw new Error('Cannot book: missing required data');
16
16
  }
17
- // Step 1: Create booking
18
- const createBookingRequest = buildBookingRequest(params);
19
- const bookingResponse = await createBooking(createBookingRequest.booking, {
20
- participantNotification: createBookingRequest.participantNotification,
21
- sendSmsReminder: createBookingRequest.sendSmsReminder ?? undefined,
22
- formSubmission: createBookingRequest.formSubmission ?? undefined,
23
- });
24
- const bookingId = bookingResponse.booking?._id;
25
- const contactDetails = bookingResponse.booking?.contactDetails;
26
- if (!bookingId) {
17
+ // Step 1: Create booking(s). The helper picks single vs bulk vs
18
+ // multi-service (DAY-range routing) and returns a uniform
19
+ // { bookingIds, contactDetails } shape.
20
+ const { bookingIds, contactDetails } = await createBookingsForCart(params);
21
+ if (bookingIds.length === 0) {
27
22
  throw new Error('Failed to create booking');
28
23
  }
29
- // Step 2: Create cart
24
+ // Step 2: Create cart. `firstSelection` is guaranteed non-empty because
25
+ // `canBook` already enforced `serviceSelections.length > 0` above.
30
26
  const firstSelection = params.serviceSelections[0];
31
27
  if (!firstSelection) {
32
28
  throw new Error('No service selection found');
33
29
  }
34
- const businessLocationId = firstSelection.timeSlot?.location?._id ?? undefined;
35
- const cartRequest = buildCartRequest({
36
- bookingId,
37
- contactDetails,
38
- businessLocationId,
39
- appId: params.appId,
40
- });
30
+ const isDayRange = isDayRangeService(firstSelection.service);
31
+ const businessLocationId = isDayRange
32
+ ? // TODO: derive from the selected resource (staff member)'s location
33
+ // once the resource-location work lands. Until then, the DAY-range
34
+ // slot list doesn't reliably carry per-slot location, so we read
35
+ // it from the service's first configured location.
36
+ firstSelection.service.locations?.[0]?._id
37
+ : firstSelection.timeSlot?.location?._id;
38
+ const cartRequest = isDayRange
39
+ ? buildDayRangeCartRequest({
40
+ bookingIds,
41
+ contactDetails,
42
+ businessLocationId,
43
+ appId: params.appId,
44
+ })
45
+ : buildCartRequest({
46
+ bookingId: bookingIds[0],
47
+ contactDetails,
48
+ businessLocationId,
49
+ appId: params.appId,
50
+ });
41
51
  const cart = await createCart(cartRequest);
42
52
  if (!cart._id) {
43
53
  throw new Error('Failed to create cart');
@@ -47,7 +57,9 @@ export async function executeBookAction(params) {
47
57
  if (!calculatedCart || !summary) {
48
58
  throw new Error('Failed to calculate cart');
49
59
  }
50
- // Step 4: Determine if checkout is required or can be skipped
60
+ // Step 4: Determine if checkout is required or can be skipped. For
61
+ // DAY-range expansion all N bookings are the same service, so reading the
62
+ // first selection's service is still correct.
51
63
  const service = firstSelection.service;
52
64
  if (isCheckoutRequired(calculatedCart, summary, service)) {
53
65
  return { type: BookResultType.CheckoutRequired, cartId: cart._id };
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Builds the createBooking request from BookActionParams
3
3
  */
4
- import type { CreateBookingRequest } from '../../../api/create-booking/index.js';
4
+ import { type CreateBookingRequest } from '@wix/auto_sdk_bookings_bookings';
5
5
  import type { BookActionParams } from './types.js';
6
6
  /**
7
7
  * Builds a CreateBookingRequest from the booking action parameters
@@ -55,8 +55,10 @@ export function buildBookingRequest(params) {
55
55
  const slotLocation = item.timeSlot.location ?? location;
56
56
  return {
57
57
  booking: {
58
- // Payment option derived from service payment settings
59
- selectedPaymentOption: deriveSelectedPaymentOption(item.service),
58
+ // Payment option: consumer override (if set) wins over the
59
+ // service-config heuristic.
60
+ selectedPaymentOption: params.selectedPaymentOption ??
61
+ deriveSelectedPaymentOption(item.service),
60
62
  // Participants
61
63
  totalParticipants: item.totalParticipants || 1,
62
64
  // Booked entity slot
@@ -1,10 +1,28 @@
1
1
  import { type CreateCartOptions } from '../../../api/create-cart/index.js';
2
- import type { ContactDetails } from '../../../api/create-booking/index.js';
2
+ import type { ContactDetails } from '@wix/auto_sdk_bookings_bookings';
3
3
  export interface BuildCartParams {
4
4
  bookingId: string;
5
5
  contactDetails?: ContactDetails;
6
- businessLocationId?: string;
6
+ /** `null` accepted so callers can forward SDK fields typed `string | null`. */
7
+ businessLocationId?: string | null;
7
8
  /** Optional app ID override for the catalog reference. Defaults to BOOKING_APP_ID. */
8
9
  appId?: string;
9
10
  }
10
11
  export declare function buildCartRequest(params: BuildCartParams): CreateCartOptions;
12
+ /**
13
+ * DAY-range variant of `buildCartRequest`. Takes the N booking IDs returned
14
+ * by `bulkCreateBooking` (1 slot) or `createMultiServiceBooking` (2-8 slots)
15
+ * and emits one catalog item per ID. Per SDK guidance, never use
16
+ * `multiServiceBooking._id` here; always feed the per-day single-service
17
+ * booking IDs into the cart.
18
+ */
19
+ export interface BuildDayRangeCartParams {
20
+ /** One catalog item per booking id. Bulk / multi-service responses produce N>=1 ids. */
21
+ bookingIds: string[];
22
+ contactDetails?: ContactDetails;
23
+ /** `null` accepted so callers can forward SDK fields typed `string | null`. */
24
+ businessLocationId?: string | null;
25
+ /** Optional app ID override for the catalog reference. Defaults to BOOKING_APP_ID. */
26
+ appId?: string;
27
+ }
28
+ export declare function buildDayRangeCartRequest(params: BuildDayRangeCartParams): CreateCartOptions;