@wix/headless-bookings 1.0.3 → 1.0.5
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/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/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/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/booking.d.ts +11 -1
- package/cjs/dist/services/index.d.ts +1 -1
- package/cjs/dist/services/index.js +6 -1
- package/cjs/dist/services/payment/payment.js +15 -4
- 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/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/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/booking.d.ts +11 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/payment/payment.js +15 -4
- package/package.json +2 -2
|
@@ -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
|
|
@@ -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;
|
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
import { ChannelType, } from '../../../api/create-cart/index.js';
|
|
2
2
|
import { BOOKING_APP_ID } from '../../constants.js';
|
|
3
3
|
export function buildCartRequest(params) {
|
|
4
|
-
|
|
4
|
+
return {
|
|
5
|
+
catalogItems: [buildCatalogItem(params.bookingId, params.appId)],
|
|
6
|
+
cart: buildCartShape(params),
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function buildDayRangeCartRequest(params) {
|
|
10
|
+
return {
|
|
11
|
+
catalogItems: params.bookingIds.map((id) => buildCatalogItem(id, params.appId)),
|
|
12
|
+
cart: buildCartShape(params),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function buildCatalogItem(bookingId, appId) {
|
|
16
|
+
return {
|
|
17
|
+
quantity: 1,
|
|
18
|
+
catalogReference: {
|
|
19
|
+
catalogItemId: bookingId,
|
|
20
|
+
appId: appId ?? BOOKING_APP_ID,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function buildCartShape(params) {
|
|
25
|
+
const { contactDetails, businessLocationId } = params;
|
|
5
26
|
const cart = {
|
|
6
27
|
source: { channelType: ChannelType.WEB },
|
|
7
28
|
};
|
|
@@ -19,16 +40,5 @@ export function buildCartRequest(params) {
|
|
|
19
40
|
};
|
|
20
41
|
}
|
|
21
42
|
}
|
|
22
|
-
return
|
|
23
|
-
catalogItems: [
|
|
24
|
-
{
|
|
25
|
-
quantity: 1,
|
|
26
|
-
catalogReference: {
|
|
27
|
-
catalogItemId: bookingId,
|
|
28
|
-
appId: appId ?? BOOKING_APP_ID,
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
cart,
|
|
33
|
-
};
|
|
43
|
+
return cart;
|
|
34
44
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the request payload(s) for a single APPOINTMENT service whose
|
|
3
|
+
* duration is configured as a DAY range.
|
|
4
|
+
*
|
|
5
|
+
* The slot list comes from availability — the user picks a start + end date
|
|
6
|
+
* and the picker emits one slot per day in `rangeTimeSlots`. The client just
|
|
7
|
+
* packages them as one Booking each. Routing is decided by `bookings.length`
|
|
8
|
+
* in `createBookingsForCart`:
|
|
9
|
+
* - length 1 -> `bulkCreateBooking`
|
|
10
|
+
* - length 2-8 -> `createMultiServiceBooking`
|
|
11
|
+
*
|
|
12
|
+
* The 8-day cap is enforced server-side; the builder does not gate on it.
|
|
13
|
+
*/
|
|
14
|
+
import type { Booking, ParticipantNotification } from '@wix/auto_sdk_bookings_bookings';
|
|
15
|
+
import type { BookActionParams } from './types.js';
|
|
16
|
+
export interface BuildDayRangeBookingRequest {
|
|
17
|
+
/**
|
|
18
|
+
* One Booking per picked per-day slot. Length 1 routes to bulkCreateBooking,
|
|
19
|
+
* length >=2 routes to createMultiServiceBooking.
|
|
20
|
+
*/
|
|
21
|
+
bookings: Booking[];
|
|
22
|
+
/** Notification settings forwarded to the server. */
|
|
23
|
+
participantNotification?: ParticipantNotification;
|
|
24
|
+
/** Whether to send SMS reminder. */
|
|
25
|
+
sendSmsReminder: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Builds N Bookings from a DAY-range selection.
|
|
29
|
+
*
|
|
30
|
+
* The base Booking (service, resource, location, payment, participants) is
|
|
31
|
+
* produced by `buildBookingRequest` from the singular `timeSlot`; per-slot
|
|
32
|
+
* cloning then overrides only `bookedEntity.slot.startDate` / `endDate` from
|
|
33
|
+
* the multi-slot input.
|
|
34
|
+
*
|
|
35
|
+
* @throws if no service selection or no slots are available.
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildDayRangeBookingRequest(params: BookActionParams): BuildDayRangeBookingRequest;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the request payload(s) for a single APPOINTMENT service whose
|
|
3
|
+
* duration is configured as a DAY range.
|
|
4
|
+
*
|
|
5
|
+
* The slot list comes from availability — the user picks a start + end date
|
|
6
|
+
* and the picker emits one slot per day in `rangeTimeSlots`. The client just
|
|
7
|
+
* packages them as one Booking each. Routing is decided by `bookings.length`
|
|
8
|
+
* in `createBookingsForCart`:
|
|
9
|
+
* - length 1 -> `bulkCreateBooking`
|
|
10
|
+
* - length 2-8 -> `createMultiServiceBooking`
|
|
11
|
+
*
|
|
12
|
+
* The 8-day cap is enforced server-side; the builder does not gate on it.
|
|
13
|
+
*/
|
|
14
|
+
import { buildBookingRequest } from './buildBookingRequest.js';
|
|
15
|
+
/**
|
|
16
|
+
* Builds N Bookings from a DAY-range selection.
|
|
17
|
+
*
|
|
18
|
+
* The base Booking (service, resource, location, payment, participants) is
|
|
19
|
+
* produced by `buildBookingRequest` from the singular `timeSlot`; per-slot
|
|
20
|
+
* cloning then overrides only `bookedEntity.slot.startDate` / `endDate` from
|
|
21
|
+
* the multi-slot input.
|
|
22
|
+
*
|
|
23
|
+
* @throws if no service selection or no slots are available.
|
|
24
|
+
*/
|
|
25
|
+
export function buildDayRangeBookingRequest(params) {
|
|
26
|
+
const item = params.serviceSelections[0];
|
|
27
|
+
if (!item) {
|
|
28
|
+
throw new Error('No service selection provided');
|
|
29
|
+
}
|
|
30
|
+
const slots = item.rangeTimeSlots && item.rangeTimeSlots.length > 0
|
|
31
|
+
? item.rangeTimeSlots
|
|
32
|
+
: item.timeSlot
|
|
33
|
+
? [item.timeSlot]
|
|
34
|
+
: [];
|
|
35
|
+
if (slots.length === 0) {
|
|
36
|
+
throw new Error('No time slot selected for service selection');
|
|
37
|
+
}
|
|
38
|
+
// Build the base Booking using the singular timeSlot path so that shared
|
|
39
|
+
// fields (service, resource, location, payment, participants) are derived
|
|
40
|
+
// exactly once and consistently. Each per-day Booking is then a shallow
|
|
41
|
+
// clone with only the slot start/end overridden from `slots[i]`.
|
|
42
|
+
const baseSelection = item.timeSlot ? item : { ...item, timeSlot: slots[0] };
|
|
43
|
+
const baseRequest = buildBookingRequest({
|
|
44
|
+
...params,
|
|
45
|
+
serviceSelections: [baseSelection, ...params.serviceSelections.slice(1)],
|
|
46
|
+
});
|
|
47
|
+
const bookings = slots.map((slot) => ({
|
|
48
|
+
...baseRequest.booking,
|
|
49
|
+
bookedEntity: {
|
|
50
|
+
...baseRequest.booking.bookedEntity,
|
|
51
|
+
slot: {
|
|
52
|
+
...baseRequest.booking.bookedEntity?.slot,
|
|
53
|
+
startDate: slot.startDate,
|
|
54
|
+
endDate: slot.endDate,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
}));
|
|
58
|
+
// TODO(createAsOneBooking): when the server flag lands, this builder should
|
|
59
|
+
// optionally collapse all slots into a single Booking spanning
|
|
60
|
+
// `slots[0].startDate -> slots[N-1].endDate` with `fullDay: true`, and
|
|
61
|
+
// routing should use plain `createBooking` (not bulk / multi-service).
|
|
62
|
+
return {
|
|
63
|
+
bookings,
|
|
64
|
+
participantNotification: baseRequest.participantNotification,
|
|
65
|
+
sendSmsReminder: baseRequest.sendSmsReminder ?? true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -14,7 +14,8 @@ export interface CanBookParams {
|
|
|
14
14
|
* Validates if booking can be executed
|
|
15
15
|
* Checks that all required data is present:
|
|
16
16
|
* - At least one service selection
|
|
17
|
-
* - Each service selection has a
|
|
17
|
+
* - Each service selection has either a `timeSlot` or a non-empty
|
|
18
|
+
* `rangeTimeSlots` (DAY-range path)
|
|
18
19
|
* - Form submission filled
|
|
19
20
|
*
|
|
20
21
|
* @param params - The booking data to validate
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* Validates if booking can be executed
|
|
6
6
|
* Checks that all required data is present:
|
|
7
7
|
* - At least one service selection
|
|
8
|
-
* - Each service selection has a
|
|
8
|
+
* - Each service selection has either a `timeSlot` or a non-empty
|
|
9
|
+
* `rangeTimeSlots` (DAY-range path)
|
|
9
10
|
* - Form submission filled
|
|
10
11
|
*
|
|
11
12
|
* @param params - The booking data to validate
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
export function canBook(params) {
|
|
15
16
|
const { serviceSelections, formSubmission } = params;
|
|
16
17
|
return (serviceSelections.length > 0 &&
|
|
17
|
-
serviceSelections.every((item) => item.timeSlot !== undefined
|
|
18
|
+
serviceSelections.every((item) => item.timeSlot !== undefined ||
|
|
19
|
+
(item.rangeTimeSlots !== undefined && item.rangeTimeSlots.length > 0)) &&
|
|
18
20
|
formSubmission !== null);
|
|
19
21
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the booking(s) needed for cart construction.
|
|
3
|
+
*
|
|
4
|
+
* Branches on the service selection:
|
|
5
|
+
* - DAY-range path with 1 picked slot -> `bulkCreateBooking`
|
|
6
|
+
* - DAY-range path with 2..8 picked slots -> `createMultiServiceBooking`
|
|
7
|
+
* - default path -> `createBooking`
|
|
8
|
+
*
|
|
9
|
+
* All branches return the same shape so `executeBookAction` can stay
|
|
10
|
+
* branchless from cart-creation onwards. Cart construction reads each
|
|
11
|
+
* single-service booking ID — per SDK guidance, never the
|
|
12
|
+
* `multiServiceBooking._id`.
|
|
13
|
+
*/
|
|
14
|
+
import type { ContactDetails } from '@wix/auto_sdk_bookings_bookings';
|
|
15
|
+
import type { BookActionParams } from './types.js';
|
|
16
|
+
export interface CreatedBookings {
|
|
17
|
+
/** One ID per single-service booking. >=1 on success. */
|
|
18
|
+
bookingIds: string[];
|
|
19
|
+
/** Canonical contact details echoed by the server (read from the first booking). */
|
|
20
|
+
contactDetails?: ContactDetails;
|
|
21
|
+
}
|
|
22
|
+
export declare function createBookingsForCart(params: BookActionParams): Promise<CreatedBookings>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the booking(s) needed for cart construction.
|
|
3
|
+
*
|
|
4
|
+
* Branches on the service selection:
|
|
5
|
+
* - DAY-range path with 1 picked slot -> `bulkCreateBooking`
|
|
6
|
+
* - DAY-range path with 2..8 picked slots -> `createMultiServiceBooking`
|
|
7
|
+
* - default path -> `createBooking`
|
|
8
|
+
*
|
|
9
|
+
* All branches return the same shape so `executeBookAction` can stay
|
|
10
|
+
* branchless from cart-creation onwards. Cart construction reads each
|
|
11
|
+
* single-service booking ID — per SDK guidance, never the
|
|
12
|
+
* `multiServiceBooking._id`.
|
|
13
|
+
*/
|
|
14
|
+
import { createBooking } from '../../../api/create-booking/index.js';
|
|
15
|
+
import { bulkCreateBooking } from '../../../api/create-bulk-booking/index.js';
|
|
16
|
+
import { createMultiServiceBooking } from '../../../api/create-multi-service-booking/index.js';
|
|
17
|
+
import { buildBookingRequest } from './buildBookingRequest.js';
|
|
18
|
+
import { buildDayRangeBookingRequest } from './buildDayRangeBookingRequest.js';
|
|
19
|
+
import { isDayRangeService } from './dayRange.js';
|
|
20
|
+
export async function createBookingsForCart(params) {
|
|
21
|
+
const firstSelection = params.serviceSelections[0];
|
|
22
|
+
if (!firstSelection) {
|
|
23
|
+
throw new Error('No service selection provided');
|
|
24
|
+
}
|
|
25
|
+
if (isDayRangeService(firstSelection.service)) {
|
|
26
|
+
const req = buildDayRangeBookingRequest(params);
|
|
27
|
+
// TODO(createAsOneBooking): when the server flag lands, branch here:
|
|
28
|
+
// if (createAsOneBooking) -> createBooking({ ...wideRange, fullDay: true })
|
|
29
|
+
// collapsing all slots into one combined Booking. Until then, we always
|
|
30
|
+
// use one Booking per picked slot.
|
|
31
|
+
if (req.bookings.length === 1) {
|
|
32
|
+
const response = await bulkCreateBooking([{ booking: req.bookings[0] }], {
|
|
33
|
+
participantNotification: req.participantNotification,
|
|
34
|
+
sendSmsReminder: req.sendSmsReminder,
|
|
35
|
+
});
|
|
36
|
+
const results = response.results ?? [];
|
|
37
|
+
return {
|
|
38
|
+
bookingIds: results.map((r) => r.item?._id).filter(Boolean),
|
|
39
|
+
// All N daily bookings share the same customer; read from the first.
|
|
40
|
+
contactDetails: results[0]?.item?.contactDetails,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const response = await createMultiServiceBooking(req.bookings, {
|
|
44
|
+
participantNotification: req.participantNotification,
|
|
45
|
+
sendSmsReminder: req.sendSmsReminder,
|
|
46
|
+
});
|
|
47
|
+
const bookings = response.multiServiceBooking?.bookings ?? [];
|
|
48
|
+
return {
|
|
49
|
+
bookingIds: bookings
|
|
50
|
+
.map((b) => b.booking?._id)
|
|
51
|
+
.filter(Boolean),
|
|
52
|
+
contactDetails: bookings[0]?.booking?.contactDetails,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const createBookingRequest = buildBookingRequest(params);
|
|
56
|
+
const bookingResponse = await createBooking(createBookingRequest.booking, {
|
|
57
|
+
participantNotification: createBookingRequest.participantNotification,
|
|
58
|
+
sendSmsReminder: createBookingRequest.sendSmsReminder,
|
|
59
|
+
formSubmission: createBookingRequest.formSubmission,
|
|
60
|
+
});
|
|
61
|
+
const bookingId = bookingResponse.booking?._id;
|
|
62
|
+
return {
|
|
63
|
+
bookingIds: bookingId ? [bookingId] : [],
|
|
64
|
+
contactDetails: bookingResponse.booking?.contactDetails,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DAY-range service helpers.
|
|
3
|
+
*
|
|
4
|
+
* APPOINTMENT services whose schedule has a `durationRange` with
|
|
5
|
+
* `unitType === DAY` route through the per-day slot path:
|
|
6
|
+
* - 1 picked slot -> `bulkCreateBooking`
|
|
7
|
+
* - 2..8 picked slots -> `createMultiServiceBooking`
|
|
8
|
+
*
|
|
9
|
+
* CLASS/COURSE selections and HOUR-range / fixed-duration APPOINTMENT
|
|
10
|
+
* selections keep using the existing `createBooking` path.
|
|
11
|
+
*/
|
|
12
|
+
import { type Service } from '@wix/auto_sdk_bookings_services';
|
|
13
|
+
/**
|
|
14
|
+
* Returns true when the given service is an APPOINTMENT whose duration is
|
|
15
|
+
* configured as a DAY range. Useful at the UI layer (e.g. routing) to know
|
|
16
|
+
* before a slot has been picked whether the selection will go through the
|
|
17
|
+
* DAY-range create path.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isDayRangeService(service: Service): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Bounds (in days) that a customer may pick for a DAY-range service.
|
|
22
|
+
* Both bounds are inclusive. The server enforces 1..8 per `DailyConfig` (SDK);
|
|
23
|
+
* this helper just exposes whatever the configured values are.
|
|
24
|
+
*/
|
|
25
|
+
export type DayRangeBounds = {
|
|
26
|
+
min: number;
|
|
27
|
+
max: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Returns the configured min/max day bounds for a DAY-range service, or
|
|
31
|
+
* `null` if the service is not DAY-range. Falls back to `min = 1` / `max = min`
|
|
32
|
+
* when the service is missing `dayOptions` configuration values.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getDayRangeBounds(service: Service): DayRangeBounds | null;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DAY-range service helpers.
|
|
3
|
+
*
|
|
4
|
+
* APPOINTMENT services whose schedule has a `durationRange` with
|
|
5
|
+
* `unitType === DAY` route through the per-day slot path:
|
|
6
|
+
* - 1 picked slot -> `bulkCreateBooking`
|
|
7
|
+
* - 2..8 picked slots -> `createMultiServiceBooking`
|
|
8
|
+
*
|
|
9
|
+
* CLASS/COURSE selections and HOUR-range / fixed-duration APPOINTMENT
|
|
10
|
+
* selections keep using the existing `createBooking` path.
|
|
11
|
+
*/
|
|
12
|
+
import { ServiceType, UnitType, } from '@wix/auto_sdk_bookings_services';
|
|
13
|
+
/**
|
|
14
|
+
* Returns true when the given service is an APPOINTMENT whose duration is
|
|
15
|
+
* configured as a DAY range. Useful at the UI layer (e.g. routing) to know
|
|
16
|
+
* before a slot has been picked whether the selection will go through the
|
|
17
|
+
* DAY-range create path.
|
|
18
|
+
*/
|
|
19
|
+
export function isDayRangeService(service) {
|
|
20
|
+
const isAppointment = service.type === ServiceType.APPOINTMENT;
|
|
21
|
+
const isDayRange = service.schedule?.availabilityConstraints?.durationRange?.unitType ===
|
|
22
|
+
UnitType.DAY;
|
|
23
|
+
return isAppointment && isDayRange;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns the configured min/max day bounds for a DAY-range service, or
|
|
27
|
+
* `null` if the service is not DAY-range. Falls back to `min = 1` / `max = min`
|
|
28
|
+
* when the service is missing `dayOptions` configuration values.
|
|
29
|
+
*/
|
|
30
|
+
export function getDayRangeBounds(service) {
|
|
31
|
+
if (!isDayRangeService(service))
|
|
32
|
+
return null;
|
|
33
|
+
const dayOptions = service.schedule?.availabilityConstraints?.durationRange?.dayOptions;
|
|
34
|
+
const min = dayOptions?.minDurationInDays ?? 1;
|
|
35
|
+
const max = dayOptions?.maxDurationInDays ?? min;
|
|
36
|
+
if (max < min)
|
|
37
|
+
return null;
|
|
38
|
+
return { min, max };
|
|
39
|
+
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Book Action - Exports
|
|
3
3
|
*/
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
9
|
-
export
|
|
4
|
+
export * from './bookAction.js';
|
|
5
|
+
export * from './canBook.js';
|
|
6
|
+
export * from './buildBookingRequest.js';
|
|
7
|
+
export * from './buildDayRangeBookingRequest.js';
|
|
8
|
+
export * from './buildCartRequest.js';
|
|
9
|
+
export * from './createBookingsForCart.js';
|
|
10
|
+
export * from './dayRange.js';
|
|
11
|
+
export * from './isCheckoutRequired.js';
|
|
12
|
+
export * from './types.js';
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Book Action - Exports
|
|
3
3
|
*/
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
9
|
-
export
|
|
4
|
+
export * from './bookAction.js';
|
|
5
|
+
export * from './canBook.js';
|
|
6
|
+
export * from './buildBookingRequest.js';
|
|
7
|
+
export * from './buildDayRangeBookingRequest.js';
|
|
8
|
+
export * from './buildCartRequest.js';
|
|
9
|
+
export * from './createBookingsForCart.js';
|
|
10
|
+
export * from './dayRange.js';
|
|
11
|
+
export * from './isCheckoutRequired.js';
|
|
12
|
+
export * from './types.js';
|
|
@@ -40,8 +40,18 @@ export interface ServiceSelection {
|
|
|
40
40
|
service: Service;
|
|
41
41
|
/** Selected resources (staff members, rooms, etc.) with type information */
|
|
42
42
|
resources: ExtendedResource[];
|
|
43
|
-
/** Per-item time slot data */
|
|
43
|
+
/** Per-item time slot data (single-slot path; existing behavior). */
|
|
44
44
|
timeSlot?: ServiceSelectionTimeSlot;
|
|
45
|
+
/**
|
|
46
|
+
* Per-day slots for DAY-range services. Populated by the availability
|
|
47
|
+
* picker after the customer picks a start + end date: one entry per day
|
|
48
|
+
* in the picked range. When set, supersedes `timeSlot` for the DAY-range
|
|
49
|
+
* builder, which produces one Booking per entry and routes via length:
|
|
50
|
+
* - 1 -> bulkCreateBooking
|
|
51
|
+
* - 2-8 -> createMultiServiceBooking
|
|
52
|
+
* Optional & additive: existing single-slot callers stay on `timeSlot`.
|
|
53
|
+
*/
|
|
54
|
+
rangeTimeSlots?: ServiceSelectionTimeSlot[];
|
|
45
55
|
/** Number of participants for this service selection */
|
|
46
56
|
totalParticipants?: number;
|
|
47
57
|
}
|
package/dist/services/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { BookingService, BookingServiceDefinition, type BookingServiceConfig, ty
|
|
|
7
7
|
export { TimeSlotListService, TimeSlotListServiceDefinition, TimeSlotService, TimeSlotServiceDefinition, ViewMode, loadTimeSlotListConfig, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type TimeSlotServiceAPI, type TimeSlotServiceConfig, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list/index.js';
|
|
8
8
|
export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
|
|
9
9
|
export { SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from '../api/query-services/index.js';
|
|
10
|
-
export { executeBookAction, canBook, buildBookingRequest, buildCartRequest, type CanBookParams, type BookResult, type BookingError, type BookActionParams, type BookChildProps, type BookProps, type BuildCartParams, } from './booking/book-action/index.js';
|
|
10
|
+
export { executeBookAction, canBook, buildBookingRequest, buildCartRequest, buildDayRangeCartRequest, buildDayRangeBookingRequest, createBookingsForCart, isDayRangeService, getDayRangeBounds, type CanBookParams, type BookResult, type BookingError, type BookActionParams, type BookChildProps, type BookProps, type BuildCartParams, type BuildDayRangeCartParams, type BuildDayRangeBookingRequest, type CreatedBookings, type DayRangeBounds, } from './booking/book-action/index.js';
|
|
11
11
|
export { BookingFormService, BookingFormServiceDefinition, type BookingFormServiceAPI, type BookingFormServiceConfig, } from './booking-form/booking-form.js';
|
|
12
12
|
export { PaymentService, PaymentServiceDefinition, type PaymentServiceAPI, type PaymentServiceConfig, type PaymentDetails, type PricingServiceSelection, type LineItemAdditionalInfo, type ServicePaymentOptions, loadPaymentConfig, type SuccessPaymentConfigResult, type ErrorPaymentConfigResult, } from './payment/payment.js';
|
|
13
13
|
export { LocationListService, LocationListServiceDefinition, LocationType, loadLocationListServiceInitialData, locationListServiceBinding, type LocationListServiceAPI, type LocationListServiceConfig, type LocationListActions, type Location, type LoadLocationListServiceResult, getLocationById, } from './location-list/location-list.js';
|
package/dist/services/index.js
CHANGED
|
@@ -7,7 +7,7 @@ export { BookingService, BookingServiceDefinition, } from './booking/booking.js'
|
|
|
7
7
|
export { TimeSlotListService, TimeSlotListServiceDefinition, TimeSlotService, TimeSlotServiceDefinition, ViewMode, loadTimeSlotListConfig, } from './time-slot-list/index.js';
|
|
8
8
|
export { ServiceListService, ServiceListServiceDefinition, loadServiceListServiceInitialData, } from './service-list/service-list.js';
|
|
9
9
|
export { SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from '../api/query-services/index.js';
|
|
10
|
-
export { executeBookAction, canBook, buildBookingRequest, buildCartRequest, } from './booking/book-action/index.js';
|
|
10
|
+
export { executeBookAction, canBook, buildBookingRequest, buildCartRequest, buildDayRangeCartRequest, buildDayRangeBookingRequest, createBookingsForCart, isDayRangeService, getDayRangeBounds, } from './booking/book-action/index.js';
|
|
11
11
|
export { BookingFormService, BookingFormServiceDefinition, } from './booking-form/booking-form.js';
|
|
12
12
|
export { PaymentService, PaymentServiceDefinition, loadPaymentConfig, } from './payment/payment.js';
|
|
13
13
|
export { LocationListService, LocationListServiceDefinition, LocationType, loadLocationListServiceInitialData, locationListServiceBinding, getLocationById, } from './location-list/location-list.js';
|
|
@@ -104,7 +104,13 @@ async function getPreviewedPrice(service, localStartDate, localEndDate, timeZone
|
|
|
104
104
|
...(timeZone ? { timeZone } : {}),
|
|
105
105
|
};
|
|
106
106
|
const result = await previewPrice([lineItemInput]);
|
|
107
|
-
|
|
107
|
+
const price = result.priceInfo?.calculatedPrice;
|
|
108
|
+
if (price == null)
|
|
109
|
+
return null;
|
|
110
|
+
return {
|
|
111
|
+
price,
|
|
112
|
+
deposit: result.priceInfo?.deposit ?? null,
|
|
113
|
+
};
|
|
108
114
|
}
|
|
109
115
|
async function buildLineItemWithPreview(item, totalParticipants) {
|
|
110
116
|
if (item.localStartDate &&
|
|
@@ -113,9 +119,14 @@ async function buildLineItemWithPreview(item, totalParticipants) {
|
|
|
113
119
|
const previewedPrice = await getPreviewedPrice(item.service, item.localStartDate, item.localEndDate, item.timeZone, totalParticipants).catch(() => null);
|
|
114
120
|
if (previewedPrice != null) {
|
|
115
121
|
const depositValue = item.service.payment?.fixed?.deposit?.value;
|
|
116
|
-
const
|
|
122
|
+
const previewedDeposit = previewedPrice.deposit;
|
|
123
|
+
const hasDeposit = previewedDeposit != null
|
|
124
|
+
? previewedDeposit > 0
|
|
125
|
+
: !!depositValue && Number(depositValue) > 0;
|
|
117
126
|
const deposit = hasDeposit
|
|
118
|
-
?
|
|
127
|
+
? previewedDeposit != null
|
|
128
|
+
? previewedDeposit
|
|
129
|
+
: Number(depositValue) * totalParticipants
|
|
119
130
|
: undefined;
|
|
120
131
|
return {
|
|
121
132
|
_id: item.lineItemId,
|
|
@@ -123,7 +134,7 @@ async function buildLineItemWithPreview(item, totalParticipants) {
|
|
|
123
134
|
catalogItemId: item.service._id || '',
|
|
124
135
|
appId: BOOKING_APP_ID,
|
|
125
136
|
},
|
|
126
|
-
price: String(previewedPrice),
|
|
137
|
+
price: String(previewedPrice.price),
|
|
127
138
|
...(deposit ? { depositAmount: String(deposit) } : {}),
|
|
128
139
|
quantity: 1,
|
|
129
140
|
paymentOption: getPaymentOption(item.service, hasDeposit),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-bookings",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
@@ -75,5 +75,5 @@
|
|
|
75
75
|
"groupId": "com.wixpress.headless-components"
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
|
-
"falconPackageHash": "
|
|
78
|
+
"falconPackageHash": "874dff357223e1f787076e35a7a2f3268eeb3e12e98104b2a779ebf8"
|
|
79
79
|
}
|