@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.
- package/cjs/dist/__mocks__/booking/booking-client-service.js +4 -1
- package/cjs/dist/api/create-booking/index.d.ts +4 -4
- package/cjs/dist/api/create-bulk-booking/index.d.ts +54 -0
- package/cjs/dist/api/create-bulk-booking/index.js +54 -0
- package/cjs/dist/api/create-multi-service-booking/index.d.ts +53 -0
- package/cjs/dist/api/create-multi-service-booking/index.js +47 -0
- package/cjs/dist/api/index.d.ts +4 -1
- package/cjs/dist/api/index.js +32 -13
- package/cjs/dist/react/core/booking/Booking.d.ts +6 -1
- package/cjs/dist/react/core/booking/Booking.js +2 -0
- package/cjs/dist/services/booking/book-action/bookAction.js +39 -27
- package/cjs/dist/services/booking/book-action/buildBookingRequest.d.ts +1 -1
- package/cjs/dist/services/booking/book-action/buildBookingRequest.js +4 -2
- package/cjs/dist/services/booking/book-action/buildCartRequest.d.ts +20 -2
- package/cjs/dist/services/booking/book-action/buildCartRequest.js +24 -13
- package/cjs/dist/services/booking/book-action/buildDayRangeBookingRequest.d.ts +37 -0
- package/cjs/dist/services/booking/book-action/buildDayRangeBookingRequest.js +70 -0
- package/cjs/dist/services/booking/book-action/canBook.d.ts +2 -1
- package/cjs/dist/services/booking/book-action/canBook.js +4 -2
- package/cjs/dist/services/booking/book-action/createBookingsForCart.d.ts +22 -0
- package/cjs/dist/services/booking/book-action/createBookingsForCart.js +69 -0
- package/cjs/dist/services/booking/book-action/dayRange.d.ts +34 -0
- package/cjs/dist/services/booking/book-action/dayRange.js +43 -0
- package/cjs/dist/services/booking/book-action/index.d.ts +9 -6
- package/cjs/dist/services/booking/book-action/index.js +23 -13
- package/cjs/dist/services/booking/book-action/types.d.ts +15 -0
- package/cjs/dist/services/booking/booking.d.ts +31 -2
- package/cjs/dist/services/booking/booking.js +8 -0
- package/cjs/dist/services/index.d.ts +1 -1
- package/cjs/dist/services/index.js +6 -1
- package/cjs/dist/services/payment/payment.d.ts +4 -0
- package/cjs/dist/services/payment/payment.def.d.ts +6 -0
- package/cjs/dist/services/payment/payment.js +39 -17
- package/dist/__mocks__/booking/booking-client-service.js +4 -1
- package/dist/api/create-booking/index.d.ts +4 -4
- package/dist/api/create-bulk-booking/index.d.ts +54 -0
- package/dist/api/create-bulk-booking/index.js +51 -0
- package/dist/api/create-multi-service-booking/index.d.ts +53 -0
- package/dist/api/create-multi-service-booking/index.js +44 -0
- package/dist/api/index.d.ts +4 -1
- package/dist/api/index.js +3 -0
- package/dist/react/core/booking/Booking.d.ts +6 -1
- package/dist/react/core/booking/Booking.js +2 -0
- package/dist/services/booking/book-action/bookAction.js +34 -22
- package/dist/services/booking/book-action/buildBookingRequest.d.ts +1 -1
- package/dist/services/booking/book-action/buildBookingRequest.js +4 -2
- package/dist/services/booking/book-action/buildCartRequest.d.ts +20 -2
- package/dist/services/booking/book-action/buildCartRequest.js +23 -13
- package/dist/services/booking/book-action/buildDayRangeBookingRequest.d.ts +37 -0
- package/dist/services/booking/book-action/buildDayRangeBookingRequest.js +67 -0
- package/dist/services/booking/book-action/canBook.d.ts +2 -1
- package/dist/services/booking/book-action/canBook.js +4 -2
- package/dist/services/booking/book-action/createBookingsForCart.d.ts +22 -0
- package/dist/services/booking/book-action/createBookingsForCart.js +66 -0
- package/dist/services/booking/book-action/dayRange.d.ts +34 -0
- package/dist/services/booking/book-action/dayRange.js +39 -0
- package/dist/services/booking/book-action/index.d.ts +9 -6
- package/dist/services/booking/book-action/index.js +9 -6
- package/dist/services/booking/book-action/types.d.ts +15 -0
- package/dist/services/booking/booking.d.ts +31 -2
- package/dist/services/booking/booking.js +8 -0
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/payment/payment.d.ts +4 -0
- package/dist/services/payment/payment.def.d.ts +6 -0
- package/dist/services/payment/payment.js +39 -17
- package/package.json +2 -2
|
@@ -57,7 +57,21 @@ function getAdditionalInfo(service, lineItemId) {
|
|
|
57
57
|
: undefined;
|
|
58
58
|
return { lineItemId, customPrice };
|
|
59
59
|
}
|
|
60
|
-
|
|
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
|
|
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(',') +
|
|
249
|
+
.join(',') +
|
|
250
|
+
`:${timezone ?? ''}` +
|
|
251
|
+
`:${selectedPaymentOption ?? ''}`);
|
|
234
252
|
});
|
|
235
253
|
signalsService.effect(() => {
|
|
236
|
-
// Subscribe to servicesKey to trigger effect when service
|
|
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
|
+
}
|
package/dist/api/index.d.ts
CHANGED
|
@@ -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
|
|
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 {
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
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
|
|
59
|
-
|
|
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 '
|
|
2
|
+
import type { ContactDetails } from '@wix/auto_sdk_bookings_bookings';
|
|
3
3
|
export interface BuildCartParams {
|
|
4
4
|
bookingId: string;
|
|
5
5
|
contactDetails?: ContactDetails;
|
|
6
|
-
|
|
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;
|