@wix/headless-bookings 1.0.8 → 1.0.10
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__/services/services-mocks.d.ts +17 -1
- package/cjs/dist/__mocks__/services/services-mocks.js +35 -1
- package/cjs/dist/api/index.d.ts +1 -1
- package/cjs/dist/api/index.js +2 -1
- package/cjs/dist/api/query-services/index.d.ts +60 -15
- package/cjs/dist/api/query-services/index.js +86 -10
- package/cjs/dist/react/core/service-list/ServiceList.d.ts +2 -3
- package/cjs/dist/react/service-list/ServiceList.d.ts +3 -3
- package/cjs/dist/react/service-list/ServiceList.js +1 -1
- package/cjs/dist/react/service-list/ServiceList.stories.d.ts +29 -0
- package/cjs/dist/react/service-list/ServiceList.stories.js +125 -0
- package/cjs/dist/services/booking/book-action/bookAction.js +9 -18
- package/cjs/dist/services/booking/book-action/buildCartRequest.d.ts +7 -18
- package/cjs/dist/services/booking/book-action/buildCartRequest.js +0 -7
- package/cjs/dist/services/booking/book-action/buildDayRangeBookingRequest.d.ts +7 -4
- package/cjs/dist/services/booking/book-action/buildDayRangeBookingRequest.js +4 -5
- package/cjs/dist/services/booking/book-action/createBookingsForCart.d.ts +2 -2
- package/cjs/dist/services/booking/book-action/createBookingsForCart.js +5 -21
- package/cjs/dist/services/booking/book-action/dayRange.d.ts +2 -1
- package/cjs/dist/services/booking/book-action/dayRange.js +2 -1
- package/cjs/dist/services/booking/book-action/types.d.ts +2 -3
- package/cjs/dist/services/booking/booking.d.ts +4 -4
- package/cjs/dist/services/constants.d.ts +2 -0
- package/cjs/dist/services/constants.js +3 -1
- package/cjs/dist/services/index.d.ts +3 -3
- package/cjs/dist/services/index.js +2 -2
- package/cjs/dist/services/service-list/service-list.d.ts +45 -36
- package/cjs/dist/services/service-list/service-list.js +90 -112
- package/dist/__mocks__/services/services-mocks.d.ts +17 -1
- package/dist/__mocks__/services/services-mocks.js +34 -0
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +1 -1
- package/dist/api/query-services/index.d.ts +60 -15
- package/dist/api/query-services/index.js +89 -10
- package/dist/react/core/service-list/ServiceList.d.ts +2 -3
- package/dist/react/service-list/ServiceList.d.ts +3 -3
- package/dist/react/service-list/ServiceList.js +1 -1
- package/dist/react/service-list/ServiceList.stories.d.ts +29 -0
- package/dist/react/service-list/ServiceList.stories.js +89 -0
- package/dist/services/booking/book-action/bookAction.js +10 -19
- package/dist/services/booking/book-action/buildCartRequest.d.ts +7 -18
- package/dist/services/booking/book-action/buildCartRequest.js +0 -6
- package/dist/services/booking/book-action/buildDayRangeBookingRequest.d.ts +7 -4
- package/dist/services/booking/book-action/buildDayRangeBookingRequest.js +4 -5
- package/dist/services/booking/book-action/createBookingsForCart.d.ts +2 -2
- package/dist/services/booking/book-action/createBookingsForCart.js +3 -19
- package/dist/services/booking/book-action/dayRange.d.ts +2 -1
- package/dist/services/booking/book-action/dayRange.js +2 -1
- package/dist/services/booking/book-action/types.d.ts +2 -3
- package/dist/services/booking/booking.d.ts +4 -4
- package/dist/services/constants.d.ts +2 -0
- package/dist/services/constants.js +2 -0
- package/dist/services/index.d.ts +3 -3
- package/dist/services/index.js +2 -2
- package/dist/services/service-list/service-list.d.ts +45 -36
- package/dist/services/service-list/service-list.js +90 -112
- package/package.json +17 -2
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Query Services API
|
|
3
|
-
* Fetches single or multiple services with pagination and sorting
|
|
3
|
+
* Fetches single or multiple services with pagination and sorting.
|
|
4
|
+
*
|
|
5
|
+
* `queryServices` is the single entry point. It decides which underlying endpoint
|
|
6
|
+
* to call based on the request: when an `availabilityFilter` is present it queries
|
|
7
|
+
* the catalog-search `QueryServicesByAvailability` endpoint (cursor paging, each
|
|
8
|
+
* service enriched with an `available` flag); otherwise it uses the services
|
|
9
|
+
* `queryServices` endpoint (offset paging). Callers always get the same
|
|
10
|
+
* `{ services, pagingMetadata }` shape.
|
|
4
11
|
*/
|
|
5
12
|
import { queryServices as queryServicesApi, LocationTypeEnumLocationType as LocationType, } from '@wix/auto_sdk_bookings_services';
|
|
6
|
-
import {
|
|
13
|
+
import { queryServicesByAvailability as queryServicesByAvailabilitySdk, LocationType as AvailabilityLocationType, } from '@wix/auto_sdk_bookings_catalog-search';
|
|
14
|
+
import { BOOKING_APP_ID, DEFAULT_QUERY_LIMIT, } from '../../services/constants.js';
|
|
7
15
|
/**
|
|
8
16
|
* Synthetic location ID constants for non-business locations
|
|
9
17
|
*/
|
|
10
18
|
export const SYNTHETIC_CUSTOM_ID = 'custom';
|
|
11
19
|
export const SYNTHETIC_CUSTOMER_ID = 'customer';
|
|
20
|
+
/**
|
|
21
|
+
* Re-exported availability filter types so consumers can build availability UIs
|
|
22
|
+
* without importing the ambassador package directly.
|
|
23
|
+
*/
|
|
24
|
+
export { AvailabilityLocationType, };
|
|
12
25
|
/**
|
|
13
26
|
* Fetches a service by its ID
|
|
14
27
|
* @param id - The service ID
|
|
@@ -180,34 +193,100 @@ const buildServicesQueryRequest = ({ appId, filter: platformFilter, paging, sort
|
|
|
180
193
|
const query = {
|
|
181
194
|
filter: Object.keys(filter).length > 0 ? filter : undefined,
|
|
182
195
|
paging: paging || {
|
|
183
|
-
limit:
|
|
196
|
+
limit: DEFAULT_QUERY_LIMIT,
|
|
184
197
|
},
|
|
185
198
|
sort,
|
|
186
199
|
};
|
|
187
200
|
return { query };
|
|
188
201
|
};
|
|
202
|
+
/**
|
|
203
|
+
* Whether an availability filter carries a real bookable constraint that warrants
|
|
204
|
+
* routing to the availability endpoint. Modifiers that don't define a query on their
|
|
205
|
+
* own (`timeZone`, `exactMatch`, `includeUnavailable`) are ignored: only a complete
|
|
206
|
+
* date window (both `localStartDate` and `localEndDate`) or a resource constraint
|
|
207
|
+
* (`locations`/`attributes`/`resourceTypes`) counts.
|
|
208
|
+
*
|
|
209
|
+
* This is the single source of truth shared by the API dispatch and the service's
|
|
210
|
+
* `hasAvailabilityFilterSignal`.
|
|
211
|
+
*/
|
|
212
|
+
export function hasActiveAvailabilityFilter(availabilityFilter) {
|
|
213
|
+
if (!availabilityFilter)
|
|
214
|
+
return false;
|
|
215
|
+
const { localStartDate, localEndDate, locationIds, attributes, resourceTypes, } = availabilityFilter;
|
|
216
|
+
const hasDateWindow = Boolean(localStartDate && localEndDate);
|
|
217
|
+
const hasResourceConstraint = (locationIds?.length ?? 0) > 0 ||
|
|
218
|
+
(attributes?.length ?? 0) > 0 ||
|
|
219
|
+
(resourceTypes?.length ?? 0) > 0;
|
|
220
|
+
return hasDateWindow || hasResourceConstraint;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Queries services by real bookable availability (catalog-search). Cursor-based.
|
|
224
|
+
* The WQL filter reuses the same platform→API conversion as the offset query.
|
|
225
|
+
*/
|
|
226
|
+
async function queryServicesByAvailability({ appId, filter, availabilityFilter, sort, pagingMetadata, }) {
|
|
227
|
+
const wqlFilter = {
|
|
228
|
+
appId: appId ?? BOOKING_APP_ID,
|
|
229
|
+
};
|
|
230
|
+
if (filter) {
|
|
231
|
+
Object.assign(wqlFilter, convertPlatformFilterToApiFilter(filter));
|
|
232
|
+
}
|
|
233
|
+
const limit = pagingMetadata?.limit ?? DEFAULT_QUERY_LIMIT;
|
|
234
|
+
const cursor = pagingMetadata?.cursor;
|
|
235
|
+
const availabilityQuery = {
|
|
236
|
+
filter: wqlFilter,
|
|
237
|
+
sort,
|
|
238
|
+
cursorPaging: { limit, ...(cursor ? { cursor } : {}) },
|
|
239
|
+
};
|
|
240
|
+
const response = await queryServicesByAvailabilitySdk({
|
|
241
|
+
query: availabilityQuery,
|
|
242
|
+
availabilityFilter,
|
|
243
|
+
});
|
|
244
|
+
const results = response.results ?? [];
|
|
245
|
+
return {
|
|
246
|
+
services: results.map((result) => ({
|
|
247
|
+
...result.service,
|
|
248
|
+
available: result.available ?? true,
|
|
249
|
+
})),
|
|
250
|
+
pagingMetadata: {
|
|
251
|
+
limit,
|
|
252
|
+
count: response.pagingMetadata?.count ?? results.length,
|
|
253
|
+
hasNext: response.pagingMetadata?.hasNext ?? false,
|
|
254
|
+
cursor: response.pagingMetadata?.cursors?.next ?? undefined,
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
189
258
|
/**
|
|
190
259
|
* Query services with pagination and sorting.
|
|
191
|
-
*
|
|
260
|
+
* Picks the underlying endpoint by request: availability filter present → catalog-search
|
|
261
|
+
* (cursor paging, `available` flag); otherwise the offset services query.
|
|
192
262
|
*
|
|
193
|
-
* @param
|
|
263
|
+
* @param request - Query parameters including appId, filter (platform Filter type), paging
|
|
264
|
+
* metadata, sort options, and an optional availability filter
|
|
194
265
|
* @returns Promise that resolves to services array and paging metadata with hasNext flag
|
|
195
266
|
*
|
|
196
267
|
* @example
|
|
197
268
|
* ```ts
|
|
269
|
+
* // Offset query
|
|
198
270
|
* const { services, pagingMetadata } = await queryServices({
|
|
199
271
|
* appId: '13d21c63-b5ec-5912-8397-c3a5ddb27a97',
|
|
200
272
|
* filter: { '_id': { $in: ['service1', 'service2'] } },
|
|
201
273
|
* pagingMetadata: { limit: 20, offset: 0 },
|
|
202
|
-
* sort: [{ fieldName: 'name', order: 'ASC' }]
|
|
203
274
|
* });
|
|
204
275
|
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
276
|
+
* // Availability-aware query (cursor paging + `available` flag)
|
|
277
|
+
* const availability = await queryServices({
|
|
278
|
+
* appId: '13d21c63-b5ec-5912-8397-c3a5ddb27a97',
|
|
279
|
+
* availabilityFilter: { localStartDate: '...', localEndDate: '...', timeZone: '...' },
|
|
280
|
+
* pagingMetadata: { limit: 20 },
|
|
281
|
+
* });
|
|
208
282
|
* ```
|
|
209
283
|
*/
|
|
210
|
-
export async function queryServices(
|
|
284
|
+
export async function queryServices(request) {
|
|
285
|
+
const { appId, filter, pagingMetadata, sort, availabilityFilter } = request;
|
|
286
|
+
// Decide the endpoint by the filters passed.
|
|
287
|
+
if (hasActiveAvailabilityFilter(availabilityFilter)) {
|
|
288
|
+
return queryServicesByAvailability(request);
|
|
289
|
+
}
|
|
211
290
|
try {
|
|
212
291
|
const queryRequest = buildServicesQueryRequest({
|
|
213
292
|
appId,
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { type ServiceListServiceConfig, type QueryOptions } from '../../../services/service-list/service-list.js';
|
|
7
|
-
import type {
|
|
8
|
-
import type { PagingMetadata } from '../../../api/query-services/index.js';
|
|
7
|
+
import type { PagingMetadata, ServiceWithAvailabilityItem } from '../../../api/query-services/index.js';
|
|
9
8
|
/**
|
|
10
9
|
* Props for ServiceList.Root component
|
|
11
10
|
*/
|
|
@@ -37,7 +36,7 @@ export interface ServicesProps {
|
|
|
37
36
|
*/
|
|
38
37
|
export interface ServicesRenderProps {
|
|
39
38
|
/** Array of services */
|
|
40
|
-
services:
|
|
39
|
+
services: ServiceWithAvailabilityItem[];
|
|
41
40
|
/** Whether services are currently loading */
|
|
42
41
|
isLoading: boolean;
|
|
43
42
|
/** Error message, or null if no error */
|
|
@@ -6,7 +6,7 @@ import React from 'react';
|
|
|
6
6
|
import { ListVariant, GenericListLoadMoreRenderProps, type FilterRootProps } from '@wix/headless-components/react';
|
|
7
7
|
import { AsChildChildren } from '@wix/headless-utils/react';
|
|
8
8
|
import type { ServiceListServiceConfig } from '../../services/service-list/service-list.js';
|
|
9
|
-
import type {
|
|
9
|
+
import type { ServiceWithAvailabilityItem } from '../../api/query-services/index.js';
|
|
10
10
|
export declare const TestIds: {
|
|
11
11
|
readonly serviceListRoot: "service-list-root";
|
|
12
12
|
readonly serviceListServices: "service-list-services";
|
|
@@ -95,7 +95,7 @@ export type ServiceRootChildren = AsChildChildren<{
|
|
|
95
95
|
* Supports both ReactNode and function children that receive { selected, bookable }
|
|
96
96
|
*/
|
|
97
97
|
export interface ServiceItemWrapperProps {
|
|
98
|
-
item:
|
|
98
|
+
item: ServiceWithAvailabilityItem;
|
|
99
99
|
index?: number;
|
|
100
100
|
/** Children can be ReactNode or a function that receives { selected, bookable } from Service.Root */
|
|
101
101
|
children: React.ReactNode | ServiceRootChildren;
|
|
@@ -105,7 +105,7 @@ export interface ServiceItemWrapperProps {
|
|
|
105
105
|
* Provides items array and itemWrapper function for custom rendering
|
|
106
106
|
*/
|
|
107
107
|
export interface ServiceRepeaterRenderProps {
|
|
108
|
-
items:
|
|
108
|
+
items: ServiceWithAvailabilityItem[];
|
|
109
109
|
variant?: ListVariant;
|
|
110
110
|
/** Wraps each item in Service.Root - children can be ReactNode or function receiving { selected, bookable } */
|
|
111
111
|
itemWrapper: (props: ServiceItemWrapperProps) => React.ReactNode;
|
|
@@ -121,7 +121,7 @@ export const ServiceRepeater = React.forwardRef((props, ref) => {
|
|
|
121
121
|
// Custom itemWrapper that matches ItemWrapperProps signature
|
|
122
122
|
const serviceItemWrapper = (wrapperProps) => {
|
|
123
123
|
const { item: service, children: itemChildren } = wrapperProps;
|
|
124
|
-
return (_jsx(Service.Root, { service: service, "data-testid": TestIds.serviceListService, "data-service-id": service._id, "data-item-id": service._id, children: isServiceRootFunction
|
|
124
|
+
return (_jsx(Service.Root, { service: service, "data-testid": TestIds.serviceListService, "data-service-id": service._id, "data-item-id": service._id, "data-available": service.available, children: isServiceRootFunction
|
|
125
125
|
? children
|
|
126
126
|
: itemChildren }, service._id));
|
|
127
127
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import * as ServiceList from './ServiceList';
|
|
3
|
+
/**
|
|
4
|
+
* ServiceList component story.
|
|
5
|
+
*
|
|
6
|
+
* ServiceList renders a list of bookings services. When the configured services
|
|
7
|
+
* carry an `available` flag (availability mode), stories can style each item by
|
|
8
|
+
* its availability. It supports filtering by categories, availability dates, and
|
|
9
|
+
* custom attributes.
|
|
10
|
+
*/
|
|
11
|
+
declare const meta: Meta<typeof ServiceList.Root>;
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof meta>;
|
|
14
|
+
/**
|
|
15
|
+
* Basic list with three services, two available and one unavailable.
|
|
16
|
+
*/
|
|
17
|
+
export declare const BasicList: Story;
|
|
18
|
+
/**
|
|
19
|
+
* List with empty state message displayed when no services are available.
|
|
20
|
+
*/
|
|
21
|
+
export declare const EmptyState: Story;
|
|
22
|
+
/**
|
|
23
|
+
* List with detailed service information (name and price).
|
|
24
|
+
*/
|
|
25
|
+
export declare const WithServiceDetails: Story;
|
|
26
|
+
/**
|
|
27
|
+
* List with custom styling for available/unavailable items.
|
|
28
|
+
*/
|
|
29
|
+
export declare const WithAvailabilityStates: Story;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as ServiceList from './ServiceList';
|
|
3
|
+
import * as Booking from '../booking/Booking';
|
|
4
|
+
import * as Service from '../service/Service';
|
|
5
|
+
import { mockServicesWithAvailability, mockCategories, } from '../../__mocks__/services/services-mocks';
|
|
6
|
+
import { BOOKING_APP_ID } from '../../services/constants';
|
|
7
|
+
const config = {
|
|
8
|
+
services: mockServicesWithAvailability,
|
|
9
|
+
categories: mockCategories,
|
|
10
|
+
options: { appId: BOOKING_APP_ID },
|
|
11
|
+
};
|
|
12
|
+
const emptyConfig = {
|
|
13
|
+
services: [],
|
|
14
|
+
categories: [],
|
|
15
|
+
options: { appId: BOOKING_APP_ID },
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* ServiceList component story.
|
|
19
|
+
*
|
|
20
|
+
* ServiceList renders a list of bookings services. When the configured services
|
|
21
|
+
* carry an `available` flag (availability mode), stories can style each item by
|
|
22
|
+
* its availability. It supports filtering by categories, availability dates, and
|
|
23
|
+
* custom attributes.
|
|
24
|
+
*/
|
|
25
|
+
const meta = {
|
|
26
|
+
title: 'Bookings/ServiceList',
|
|
27
|
+
component: ServiceList.Root,
|
|
28
|
+
tags: ['autodocs'],
|
|
29
|
+
};
|
|
30
|
+
export default meta;
|
|
31
|
+
/**
|
|
32
|
+
* Basic list with three services, two available and one unavailable.
|
|
33
|
+
*/
|
|
34
|
+
export const BasicList = {
|
|
35
|
+
render: () => (_jsx(Booking.Root, { children: _jsx(ServiceList.Root, { serviceListConfig: config, children: _jsx(ServiceList.Services, { children: _jsx(ServiceList.ServiceRepeater, { children: _jsx(Service.Name, {}) }) }) }) })),
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* List with empty state message displayed when no services are available.
|
|
39
|
+
*/
|
|
40
|
+
export const EmptyState = {
|
|
41
|
+
render: () => (_jsx(Booking.Root, { children: _jsx(ServiceList.Root, { serviceListConfig: emptyConfig, children: _jsx(ServiceList.Services, { emptyState: _jsx("div", { style: {
|
|
42
|
+
padding: '2rem',
|
|
43
|
+
textAlign: 'center',
|
|
44
|
+
color: 'var(--color-muted-foreground)',
|
|
45
|
+
}, children: "No services available" }), children: _jsx(ServiceList.ServiceRepeater, { children: _jsx(Service.Name, {}) }) }) }) })),
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* List with detailed service information (name and price).
|
|
49
|
+
*/
|
|
50
|
+
export const WithServiceDetails = {
|
|
51
|
+
render: () => (_jsx(Booking.Root, { children: _jsx(ServiceList.Root, { serviceListConfig: config, children: _jsx(ServiceList.Services, { children: _jsx(ServiceList.ServiceRepeater, { asChild: true, children: ({ items, itemWrapper, }) => (_jsx("ul", { style: { listStyle: 'none', padding: 0 }, children: items.map((service, index) => (_jsx("li", { style: {
|
|
52
|
+
padding: '1rem',
|
|
53
|
+
borderBottom: '1px solid var(--color-border)',
|
|
54
|
+
opacity: service.available === false ? 0.6 : 1,
|
|
55
|
+
}, children: itemWrapper({
|
|
56
|
+
item: service,
|
|
57
|
+
index,
|
|
58
|
+
children: (_jsxs("div", { children: [_jsx(Service.Name, {}), _jsx(Service.Price, {})] })),
|
|
59
|
+
}) }, service._id))) })) }) }) }) })),
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* List with custom styling for available/unavailable items.
|
|
63
|
+
*/
|
|
64
|
+
export const WithAvailabilityStates = {
|
|
65
|
+
render: () => (_jsx(Booking.Root, { children: _jsx(ServiceList.Root, { serviceListConfig: config, children: _jsx(ServiceList.Services, { children: _jsx(ServiceList.ServiceRepeater, { asChild: true, children: ({ items, itemWrapper, }) => (_jsx("ul", { style: { listStyle: 'none', padding: 0 }, children: items.map((service, index) => (_jsx("li", { style: {
|
|
66
|
+
padding: '1rem',
|
|
67
|
+
marginBottom: '0.5rem',
|
|
68
|
+
borderRadius: '4px',
|
|
69
|
+
backgroundColor: service.available
|
|
70
|
+
? 'color-mix(in srgb, var(--color-primary) 8%, transparent)'
|
|
71
|
+
: 'var(--color-secondary)',
|
|
72
|
+
borderLeft: `4px solid ${service.available ? 'var(--color-primary)' : 'var(--color-border)'}`,
|
|
73
|
+
opacity: service.available ? 1 : 0.7,
|
|
74
|
+
}, children: itemWrapper({
|
|
75
|
+
item: service,
|
|
76
|
+
index,
|
|
77
|
+
children: (_jsxs("div", { style: {
|
|
78
|
+
display: 'flex',
|
|
79
|
+
justifyContent: 'space-between',
|
|
80
|
+
alignItems: 'center',
|
|
81
|
+
}, children: [_jsx(Service.Name, {}), _jsx("span", { style: {
|
|
82
|
+
fontSize: '0.875rem',
|
|
83
|
+
fontWeight: 500,
|
|
84
|
+
color: service.available
|
|
85
|
+
? 'var(--color-primary)'
|
|
86
|
+
: 'var(--color-muted-foreground)',
|
|
87
|
+
}, children: service.available ? 'Available' : 'Not Available' })] })),
|
|
88
|
+
}) }, service._id))) })) }) }) }) })),
|
|
89
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createCart } from '../../../api/create-cart/index.js';
|
|
2
2
|
import { calculateCart } from '../../../api/calculate-cart/index.js';
|
|
3
3
|
import { placeOrder } from '../../../api/place-order/index.js';
|
|
4
|
-
import { buildCartRequest
|
|
4
|
+
import { buildCartRequest } from './buildCartRequest.js';
|
|
5
5
|
import { canBook } from './canBook.js';
|
|
6
6
|
import { createBookingsForCart } from './createBookingsForCart.js';
|
|
7
7
|
import { isCheckoutRequired } from './isCheckoutRequired.js';
|
|
@@ -14,7 +14,7 @@ export async function executeBookAction(params) {
|
|
|
14
14
|
})) {
|
|
15
15
|
throw new Error('Cannot book: missing required data');
|
|
16
16
|
}
|
|
17
|
-
// Step 1: Create booking(s). The helper picks single vs
|
|
17
|
+
// Step 1: Create booking(s). The helper picks single vs
|
|
18
18
|
// multi-service (DAY-range routing) and returns a uniform
|
|
19
19
|
// { bookingIds, contactDetails } shape.
|
|
20
20
|
const { bookingIds, contactDetails } = await createBookingsForCart(params);
|
|
@@ -35,23 +35,14 @@ export async function executeBookAction(params) {
|
|
|
35
35
|
// it from the service's first configured location.
|
|
36
36
|
firstSelection.service.locations?.[0]?._id
|
|
37
37
|
: firstSelection.timeSlot?.location?._id;
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
businessLocationId,
|
|
47
|
-
appId: params.appId,
|
|
48
|
-
})
|
|
49
|
-
: buildCartRequest({
|
|
50
|
-
bookingId: bookingIds[0],
|
|
51
|
-
contactDetails,
|
|
52
|
-
businessLocationId,
|
|
53
|
-
appId: params.appId,
|
|
54
|
-
});
|
|
38
|
+
// One cart item per booking ID. Single-day and allDay ranges produce one
|
|
39
|
+
// booking ID; DAY-range expansion produces one per day.
|
|
40
|
+
const cartRequest = buildCartRequest({
|
|
41
|
+
bookingIds,
|
|
42
|
+
contactDetails,
|
|
43
|
+
businessLocationId,
|
|
44
|
+
appId: params.appId,
|
|
45
|
+
});
|
|
55
46
|
const cart = await createCart(cartRequest);
|
|
56
47
|
if (!cart._id) {
|
|
57
48
|
throw new Error('Failed to create cart');
|
|
@@ -1,23 +1,12 @@
|
|
|
1
1
|
import { type CreateCartOptions } from '../../../api/create-cart/index.js';
|
|
2
2
|
import type { ContactDetails } from '@wix/auto_sdk_bookings_bookings';
|
|
3
3
|
export interface BuildCartParams {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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. */
|
|
4
|
+
/**
|
|
5
|
+
* One catalog item is emitted per booking id. Single-service and allDay
|
|
6
|
+
* bookings produce one id; DAY-range expansion produces one per day. Per SDK
|
|
7
|
+
* guidance, never use `multiServiceBooking._id` here; always feed the
|
|
8
|
+
* per-day single-service booking IDs into the cart.
|
|
9
|
+
*/
|
|
21
10
|
bookingIds: string[];
|
|
22
11
|
contactDetails?: ContactDetails;
|
|
23
12
|
/** `null` accepted so callers can forward SDK fields typed `string | null`. */
|
|
@@ -25,4 +14,4 @@ export interface BuildDayRangeCartParams {
|
|
|
25
14
|
/** Optional app ID override for the catalog reference. Defaults to BOOKING_APP_ID. */
|
|
26
15
|
appId?: string;
|
|
27
16
|
}
|
|
28
|
-
export declare function
|
|
17
|
+
export declare function buildCartRequest(params: BuildCartParams): CreateCartOptions;
|
|
@@ -1,12 +1,6 @@
|
|
|
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
|
-
return {
|
|
5
|
-
catalogItems: [buildCatalogItem(params.bookingId, params.appId)],
|
|
6
|
-
cart: buildCartShape(params),
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
export function buildDayRangeCartRequest(params) {
|
|
10
4
|
return {
|
|
11
5
|
catalogItems: params.bookingIds.map((id) => buildCatalogItem(id, params.appId)),
|
|
12
6
|
cart: buildCartShape(params),
|
|
@@ -6,9 +6,12 @@
|
|
|
6
6
|
* and the picker emits one slot per day in `rangeTimeSlots`. The client just
|
|
7
7
|
* packages them as one Booking each. Routing is decided by `bookings.length`
|
|
8
8
|
* in `createBookingsForCart`:
|
|
9
|
-
* - length 1 -> `
|
|
9
|
+
* - length 1 -> `createBooking`
|
|
10
10
|
* - length 2-8 -> `createMultiServiceBooking`
|
|
11
11
|
*
|
|
12
|
+
* With `allDay`, the picked slots collapse into one Booking spanning the full
|
|
13
|
+
* range, so that case routes through `createBooking`.
|
|
14
|
+
*
|
|
12
15
|
* The 8-day cap is enforced server-side; the builder does not gate on it.
|
|
13
16
|
*/
|
|
14
17
|
import type { Booking, ParticipantNotification } from '@wix/auto_sdk_bookings_bookings';
|
|
@@ -17,9 +20,9 @@ import type { BookActionParams } from './types.js';
|
|
|
17
20
|
export interface BuildDayRangeBookingRequest {
|
|
18
21
|
/**
|
|
19
22
|
* The Bookings to create. With `allDay`, this is a single Booking spanning
|
|
20
|
-
* the whole range
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
+
* the whole range. Otherwise it is one Booking per picked per-day slot:
|
|
24
|
+
* length 1 routes to createBooking, length >=2 routes to
|
|
25
|
+
* createMultiServiceBooking.
|
|
23
26
|
*/
|
|
24
27
|
bookings: Booking[];
|
|
25
28
|
/** Notification settings forwarded to the server. */
|
|
@@ -6,9 +6,12 @@
|
|
|
6
6
|
* and the picker emits one slot per day in `rangeTimeSlots`. The client just
|
|
7
7
|
* packages them as one Booking each. Routing is decided by `bookings.length`
|
|
8
8
|
* in `createBookingsForCart`:
|
|
9
|
-
* - length 1 -> `
|
|
9
|
+
* - length 1 -> `createBooking`
|
|
10
10
|
* - length 2-8 -> `createMultiServiceBooking`
|
|
11
11
|
*
|
|
12
|
+
* With `allDay`, the picked slots collapse into one Booking spanning the full
|
|
13
|
+
* range, so that case routes through `createBooking`.
|
|
14
|
+
*
|
|
12
15
|
* The 8-day cap is enforced server-side; the builder does not gate on it.
|
|
13
16
|
*/
|
|
14
17
|
import { buildBookingRequest } from './buildBookingRequest.js';
|
|
@@ -56,10 +59,6 @@ export function buildDayRangeBookingRequest(params) {
|
|
|
56
59
|
},
|
|
57
60
|
},
|
|
58
61
|
});
|
|
59
|
-
// `allDay`: collapse every picked slot into one Booking spanning the whole
|
|
60
|
-
// range (`slots[0].startDate -> slots[N-1].endDate`). `createBookingsForCart`
|
|
61
|
-
// then routes this single Booking through plain `createBooking` instead of
|
|
62
|
-
// bulk / multi-service. Otherwise, emit one Booking per picked slot.
|
|
63
62
|
const bookings = params.allDay
|
|
64
63
|
? [buildBooking(slots[0].startDate, slots[slots.length - 1].endDate)]
|
|
65
64
|
: slots.map((slot) => buildBooking(slot.startDate, slot.endDate));
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Creates the booking(s) needed for cart construction.
|
|
3
3
|
*
|
|
4
4
|
* Branches on the service selection:
|
|
5
|
+
* - DAY-range path with 1 picked day -> `createBooking`
|
|
5
6
|
* - DAY-range path with `allDay` -> `createBooking` (one combined Booking)
|
|
6
|
-
* - DAY-range path with
|
|
7
|
-
* - DAY-range path with 2..8 picked slots -> `createMultiServiceBooking`
|
|
7
|
+
* - DAY-range path with 2..8 picked days -> `createMultiServiceBooking`
|
|
8
8
|
* - default path -> `createBooking`
|
|
9
9
|
*
|
|
10
10
|
* All branches return the same shape so `executeBookAction` can stay
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Creates the booking(s) needed for cart construction.
|
|
3
3
|
*
|
|
4
4
|
* Branches on the service selection:
|
|
5
|
+
* - DAY-range path with 1 picked day -> `createBooking`
|
|
5
6
|
* - DAY-range path with `allDay` -> `createBooking` (one combined Booking)
|
|
6
|
-
* - DAY-range path with
|
|
7
|
-
* - DAY-range path with 2..8 picked slots -> `createMultiServiceBooking`
|
|
7
|
+
* - DAY-range path with 2..8 picked days -> `createMultiServiceBooking`
|
|
8
8
|
* - default path -> `createBooking`
|
|
9
9
|
*
|
|
10
10
|
* All branches return the same shape so `executeBookAction` can stay
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
* `multiServiceBooking._id`.
|
|
14
14
|
*/
|
|
15
15
|
import { createBooking } from '../../../api/create-booking/index.js';
|
|
16
|
-
import { bulkCreateBooking } from '../../../api/create-bulk-booking/index.js';
|
|
17
16
|
import { createMultiServiceBooking } from '../../../api/create-multi-service-booking/index.js';
|
|
18
17
|
import { buildBookingRequest } from './buildBookingRequest.js';
|
|
19
18
|
import { buildDayRangeBookingRequest } from './buildDayRangeBookingRequest.js';
|
|
@@ -25,9 +24,7 @@ export async function createBookingsForCart(params) {
|
|
|
25
24
|
}
|
|
26
25
|
if (isDayRangeService(firstSelection.service)) {
|
|
27
26
|
const req = buildDayRangeBookingRequest(params);
|
|
28
|
-
|
|
29
|
-
// Booking. Create it with plain `createBooking` (not bulk / multi-service).
|
|
30
|
-
if (params.allDay) {
|
|
27
|
+
if (req.bookings.length === 1) {
|
|
31
28
|
const bookingResponse = await createBooking(req.bookings[0], {
|
|
32
29
|
participantNotification: req.participantNotification,
|
|
33
30
|
sendSmsReminder: req.sendSmsReminder,
|
|
@@ -39,19 +36,6 @@ export async function createBookingsForCart(params) {
|
|
|
39
36
|
contactDetails: bookingResponse.booking?.contactDetails,
|
|
40
37
|
};
|
|
41
38
|
}
|
|
42
|
-
if (req.bookings.length === 1) {
|
|
43
|
-
const response = await bulkCreateBooking([{ booking: req.bookings[0] }], {
|
|
44
|
-
participantNotification: req.participantNotification,
|
|
45
|
-
sendSmsReminder: req.sendSmsReminder,
|
|
46
|
-
formSubmission: req.formSubmission,
|
|
47
|
-
});
|
|
48
|
-
const results = response.results ?? [];
|
|
49
|
-
return {
|
|
50
|
-
bookingIds: results.map((r) => r.item?._id).filter(Boolean),
|
|
51
|
-
// All N daily bookings share the same customer; read from the first.
|
|
52
|
-
contactDetails: results[0]?.item?.contactDetails,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
39
|
const response = await createMultiServiceBooking(req.bookings, {
|
|
56
40
|
participantNotification: req.participantNotification,
|
|
57
41
|
sendSmsReminder: req.sendSmsReminder,
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* APPOINTMENT services whose schedule has a `durationRange` with
|
|
5
5
|
* `unitType === DAY` route through the per-day slot path:
|
|
6
|
-
* - 1 picked slot -> `
|
|
6
|
+
* - 1 picked slot -> `createBooking`
|
|
7
7
|
* - 2..8 picked slots -> `createMultiServiceBooking`
|
|
8
|
+
* - 2..8 picked slots with `allDay` -> `createBooking`
|
|
8
9
|
*
|
|
9
10
|
* CLASS/COURSE selections and HOUR-range / fixed-duration APPOINTMENT
|
|
10
11
|
* selections keep using the existing `createBooking` path.
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* APPOINTMENT services whose schedule has a `durationRange` with
|
|
5
5
|
* `unitType === DAY` route through the per-day slot path:
|
|
6
|
-
* - 1 picked slot -> `
|
|
6
|
+
* - 1 picked slot -> `createBooking`
|
|
7
7
|
* - 2..8 picked slots -> `createMultiServiceBooking`
|
|
8
|
+
* - 2..8 picked slots with `allDay` -> `createBooking`
|
|
8
9
|
*
|
|
9
10
|
* CLASS/COURSE selections and HOUR-range / fixed-duration APPOINTMENT
|
|
10
11
|
* selections keep using the existing `createBooking` path.
|
|
@@ -56,9 +56,8 @@ export interface BookActionParams {
|
|
|
56
56
|
/**
|
|
57
57
|
* DAY-range only. When `true`, the picked per-day slots are collapsed into a
|
|
58
58
|
* single Booking spanning the whole range (`firstSlot.startDate ->
|
|
59
|
-
* lastSlot.endDate`) and created via plain `createBooking
|
|
60
|
-
*
|
|
61
|
-
* `createMultiServiceBooking` (2-8 slots). Ignored for non-DAY-range services.
|
|
59
|
+
* lastSlot.endDate`) and created via plain `createBooking`. Ignored for
|
|
60
|
+
* non-DAY-range services.
|
|
62
61
|
*/
|
|
63
62
|
allDay?: boolean;
|
|
64
63
|
}
|
|
@@ -48,8 +48,9 @@ export interface ServiceSelection {
|
|
|
48
48
|
* picker after the customer picks a start + end date: one entry per day
|
|
49
49
|
* in the picked range. When set, supersedes `timeSlot` for the DAY-range
|
|
50
50
|
* builder, which produces one Booking per entry and routes via length:
|
|
51
|
-
* - 1 ->
|
|
51
|
+
* - 1 -> createBooking
|
|
52
52
|
* - 2-8 -> createMultiServiceBooking
|
|
53
|
+
* - 2-8 with allDay -> createBooking
|
|
53
54
|
* Optional & additive: existing single-slot callers stay on `timeSlot`.
|
|
54
55
|
*/
|
|
55
56
|
rangeTimeSlots?: ServiceSelectionTimeSlot[];
|
|
@@ -90,9 +91,8 @@ export interface BookingServiceActions {
|
|
|
90
91
|
setSelectedPaymentOption(value: SelectedPaymentOption | null): void;
|
|
91
92
|
/**
|
|
92
93
|
* Toggle the DAY-range `allDay` flag. When `true`, `book()` collapses the
|
|
93
|
-
* picked per-day slots
|
|
94
|
-
*
|
|
95
|
-
* Ignored for non-DAY-range services.
|
|
94
|
+
* picked per-day slots into one range-spanning Booking created via
|
|
95
|
+
* `createBooking`. Ignored for non-DAY-range services.
|
|
96
96
|
*/
|
|
97
97
|
setAllDay(value: boolean): void;
|
|
98
98
|
clearAll(): void;
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/** Booking app definition ID */
|
|
7
7
|
export declare const BOOKING_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97";
|
|
8
|
+
/** Default page size for service queries (offset and cursor/availability modes) */
|
|
9
|
+
export declare const DEFAULT_QUERY_LIMIT = 100;
|
|
8
10
|
/** Resource type ID for staff members */
|
|
9
11
|
export declare const STAFF_MEMBER_RESOURCE_TYPE_ID = "1cd44cf8-756f-41c3-bd90-3e2ffcaf1155";
|
|
10
12
|
export declare const ClientServiceIds: {
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/** Booking app definition ID */
|
|
7
7
|
export const BOOKING_APP_ID = '13d21c63-b5ec-5912-8397-c3a5ddb27a97';
|
|
8
|
+
/** Default page size for service queries (offset and cursor/availability modes) */
|
|
9
|
+
export const DEFAULT_QUERY_LIMIT = 100;
|
|
8
10
|
/** Resource type ID for staff members */
|
|
9
11
|
export const STAFF_MEMBER_RESOURCE_TYPE_ID = '1cd44cf8-756f-41c3-bd90-3e2ffcaf1155';
|
|
10
12
|
export const ClientServiceIds = {
|
package/dist/services/index.d.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
export { ServiceService, ServiceServiceDefinition, type ServiceServiceAPI, type ServiceServiceConfig, type ServiceActions, type ServicePaymentData, loadServiceServiceInitialData, type SuccessServiceServiceConfigResult, type NotFoundServiceServiceConfigResult, } from './service/service.js';
|
|
6
6
|
export { BookingService, BookingServiceDefinition, type BookingServiceConfig, type BookingServiceActions, type ServiceSelection, type ServiceSelectionTimeSlot, } from './booking/booking.js';
|
|
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
|
-
export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
|
|
9
|
-
export { SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from '../api/query-services/index.js';
|
|
10
|
-
export { executeBookAction, canBook, buildBookingRequest, buildCartRequest,
|
|
8
|
+
export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type LoadServiceListServiceOptions, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
|
|
9
|
+
export { SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, AvailabilityLocationType, type ServiceWithAvailabilityItem, type AvailabilityFilter, type Attribute, type AvailabilityLocation, } from '../api/query-services/index.js';
|
|
10
|
+
export { executeBookAction, canBook, buildBookingRequest, buildCartRequest, buildDayRangeBookingRequest, createBookingsForCart, isDayRangeService, getDayRangeBounds, type CanBookParams, type BookResult, type BookingError, type BookActionParams, type BookChildProps, type BookProps, type BuildCartParams, 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';
|