@wix/headless-bookings 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/cjs/dist/__mocks__/services/services-mocks.d.ts +17 -1
  2. package/cjs/dist/__mocks__/services/services-mocks.js +35 -1
  3. package/cjs/dist/api/index.d.ts +1 -1
  4. package/cjs/dist/api/index.js +2 -1
  5. package/cjs/dist/api/query-services/index.d.ts +60 -15
  6. package/cjs/dist/api/query-services/index.js +86 -10
  7. package/cjs/dist/react/core/service-list/ServiceList.d.ts +2 -3
  8. package/cjs/dist/react/service-list/ServiceList.d.ts +3 -3
  9. package/cjs/dist/react/service-list/ServiceList.js +1 -1
  10. package/cjs/dist/react/service-list/ServiceList.stories.d.ts +29 -0
  11. package/cjs/dist/react/service-list/ServiceList.stories.js +125 -0
  12. package/cjs/dist/services/constants.d.ts +2 -0
  13. package/cjs/dist/services/constants.js +3 -1
  14. package/cjs/dist/services/index.d.ts +2 -2
  15. package/cjs/dist/services/index.js +2 -1
  16. package/cjs/dist/services/service-list/service-list.d.ts +45 -36
  17. package/cjs/dist/services/service-list/service-list.js +90 -112
  18. package/dist/__mocks__/services/services-mocks.d.ts +17 -1
  19. package/dist/__mocks__/services/services-mocks.js +34 -0
  20. package/dist/api/index.d.ts +1 -1
  21. package/dist/api/index.js +1 -1
  22. package/dist/api/query-services/index.d.ts +60 -15
  23. package/dist/api/query-services/index.js +89 -10
  24. package/dist/react/core/service-list/ServiceList.d.ts +2 -3
  25. package/dist/react/service-list/ServiceList.d.ts +3 -3
  26. package/dist/react/service-list/ServiceList.js +1 -1
  27. package/dist/react/service-list/ServiceList.stories.d.ts +29 -0
  28. package/dist/react/service-list/ServiceList.stories.js +89 -0
  29. package/dist/services/constants.d.ts +2 -0
  30. package/dist/services/constants.js +2 -0
  31. package/dist/services/index.d.ts +2 -2
  32. package/dist/services/index.js +1 -1
  33. package/dist/services/service-list/service-list.d.ts +45 -36
  34. package/dist/services/service-list/service-list.js +90 -112
  35. package/package.json +18 -3
@@ -5,7 +5,7 @@ import type { Service, StaffMember } from '@wix/auto_sdk_bookings_services';
5
5
  import type { ServiceServiceConfig } from '../../services/service/service.js';
6
6
  import type { ServiceListServiceConfig } from '../../services/service-list/service-list.js';
7
7
  import type { StaffMemberListServiceConfig } from '../../services/staff-member-list/staff-member-list.js';
8
- import type { PagingMetadata } from '../../api/query-services/index.js';
8
+ import type { PagingMetadata, AvailabilityFilter, ServiceWithAvailabilityItem } from '../../api/query-services/index.js';
9
9
  import type { Category } from '../../api/query-categories/index.js';
10
10
  /**
11
11
  * Base mock service with all fields populated (APPOINTMENT type, FIXED pricing)
@@ -102,6 +102,22 @@ export declare const mockCategories: Category[];
102
102
  * Service list config with categories for filter testing
103
103
  */
104
104
  export declare const mockServiceListConfigWithCategories: ServiceListServiceConfig;
105
+ /**
106
+ * Mock services enriched with an `available` flag (catalog-search results).
107
+ */
108
+ export declare const mockServicesWithAvailability: ServiceWithAvailabilityItem[];
109
+ /**
110
+ * Cursor-based paging metadata with a next cursor available.
111
+ */
112
+ export declare const mockAvailabilityPagingMetadata: PagingMetadata;
113
+ /**
114
+ * Cursor-based paging metadata for the last page (no next cursor).
115
+ */
116
+ export declare const mockAvailabilityPagingMetadataLast: PagingMetadata;
117
+ /**
118
+ * Mock availability filter (date window + timezone).
119
+ */
120
+ export declare const mockAvailabilityFilter: AvailabilityFilter;
105
121
  /**
106
122
  * Base mock staff member with all fields
107
123
  */
@@ -3,7 +3,7 @@
3
3
  * Shared mock data for booking service tests
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.mockServiceInPerson = exports.mockServiceWithDeposit = exports.mockSingleStaffMemberListConfig = exports.mockEmptyStaffMemberListConfig = exports.mockStaffMemberListConfig = exports.mockStaffMembers = exports.mockStaffMemberNoName = exports.mockStaffMemberMinimal = exports.mockStaffMember2 = exports.mockStaffMember = exports.mockServiceListConfigWithCategories = exports.mockCategories = exports.mockServiceConfig = exports.mockEmptyServiceListConfig = exports.mockServiceListConfigWithMore = exports.mockServiceListConfig = exports.mockPagingMetadataEmpty = exports.mockPagingMetadataWithMore = exports.mockPagingMetadata = exports.mockServices = exports.mockServiceDayRange = exports.mockServiceHourRange = exports.mockServiceFixedDuration = exports.mockServiceMinimal = exports.mockServiceCourse = exports.mockServiceClass = exports.mockServiceNoFee = exports.mockServiceCustomPrice = exports.mockServiceVariedPrice = exports.mockService = void 0;
6
+ exports.mockServiceInPerson = exports.mockServiceWithDeposit = exports.mockSingleStaffMemberListConfig = exports.mockEmptyStaffMemberListConfig = exports.mockStaffMemberListConfig = exports.mockStaffMembers = exports.mockStaffMemberNoName = exports.mockStaffMemberMinimal = exports.mockStaffMember2 = exports.mockStaffMember = exports.mockAvailabilityFilter = exports.mockAvailabilityPagingMetadataLast = exports.mockAvailabilityPagingMetadata = exports.mockServicesWithAvailability = exports.mockServiceListConfigWithCategories = exports.mockCategories = exports.mockServiceConfig = exports.mockEmptyServiceListConfig = exports.mockServiceListConfigWithMore = exports.mockServiceListConfig = exports.mockPagingMetadataEmpty = exports.mockPagingMetadataWithMore = exports.mockPagingMetadata = exports.mockServices = exports.mockServiceDayRange = exports.mockServiceHourRange = exports.mockServiceFixedDuration = exports.mockServiceMinimal = exports.mockServiceCourse = exports.mockServiceClass = exports.mockServiceNoFee = exports.mockServiceCustomPrice = exports.mockServiceVariedPrice = exports.mockService = void 0;
7
7
  exports.createLargeServiceList = createLargeServiceList;
8
8
  exports.createLargeStaffMemberList = createLargeStaffMemberList;
9
9
  const auto_sdk_bookings_services_1 = require("@wix/auto_sdk_bookings_services");
@@ -401,6 +401,40 @@ exports.mockServiceListConfigWithCategories = {
401
401
  },
402
402
  };
403
403
  // ============================================================================
404
+ // Availability (catalog-search) Mocks
405
+ // ============================================================================
406
+ /**
407
+ * Mock services enriched with an `available` flag (catalog-search results).
408
+ */
409
+ exports.mockServicesWithAvailability = exports.mockServices.map((service, index) => ({
410
+ ...service,
411
+ available: index !== 2, // third service is unavailable
412
+ }));
413
+ /**
414
+ * Cursor-based paging metadata with a next cursor available.
415
+ */
416
+ exports.mockAvailabilityPagingMetadata = {
417
+ count: 3,
418
+ hasNext: true,
419
+ cursor: 'next-cursor-token',
420
+ };
421
+ /**
422
+ * Cursor-based paging metadata for the last page (no next cursor).
423
+ */
424
+ exports.mockAvailabilityPagingMetadataLast = {
425
+ count: 3,
426
+ hasNext: false,
427
+ cursor: undefined,
428
+ };
429
+ /**
430
+ * Mock availability filter (date window + timezone).
431
+ */
432
+ exports.mockAvailabilityFilter = {
433
+ localStartDate: '2026-06-01T09:00:00',
434
+ localEndDate: '2026-06-01T17:00:00',
435
+ timeZone: 'America/New_York',
436
+ };
437
+ // ============================================================================
404
438
  // Staff Member Mocks
405
439
  // ============================================================================
406
440
  /**
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * @module API
6
6
  */
7
- export { getServiceById, getServiceBySlug, queryServices, convertPlatformFilterToApiFilter, type PagingMetadata, type QueryServicesRequest, SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from './query-services/index.js';
7
+ export { getServiceById, getServiceBySlug, queryServices, convertPlatformFilterToApiFilter, type PagingMetadata, type QueryServicesRequest, type QueryServicesResult, type ServiceWithAvailabilityItem, AvailabilityLocationType, type AvailabilityFilter, type Attribute, type AvailabilityLocation, SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from './query-services/index.js';
8
8
  export { fetchAvailability } from './fetch-availability/index.js';
9
9
  export * from '@wix/auto_sdk_bookings_bookings';
10
10
  export { createBooking, type CreateBookingOptions, } from './create-booking/index.js';
@@ -20,12 +20,13 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
20
20
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
21
  };
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.queryCategories = exports.getLocationById = exports.queryLocations = exports.placeOrder = exports.calculateCart = exports.ChannelType = exports.createCart = exports.createMultiServiceBooking = exports.bulkCreateBooking = exports.createBooking = exports.fetchAvailability = exports.SYNTHETIC_CUSTOMER_ID = exports.SYNTHETIC_CUSTOM_ID = exports.convertPlatformFilterToApiFilter = exports.queryServices = exports.getServiceBySlug = exports.getServiceById = void 0;
23
+ exports.queryCategories = exports.getLocationById = exports.queryLocations = exports.placeOrder = exports.calculateCart = exports.ChannelType = exports.createCart = exports.createMultiServiceBooking = exports.bulkCreateBooking = exports.createBooking = exports.fetchAvailability = exports.SYNTHETIC_CUSTOMER_ID = exports.SYNTHETIC_CUSTOM_ID = exports.AvailabilityLocationType = exports.convertPlatformFilterToApiFilter = exports.queryServices = exports.getServiceBySlug = exports.getServiceById = void 0;
24
24
  var index_js_1 = require("./query-services/index.js");
25
25
  Object.defineProperty(exports, "getServiceById", { enumerable: true, get: function () { return index_js_1.getServiceById; } });
26
26
  Object.defineProperty(exports, "getServiceBySlug", { enumerable: true, get: function () { return index_js_1.getServiceBySlug; } });
27
27
  Object.defineProperty(exports, "queryServices", { enumerable: true, get: function () { return index_js_1.queryServices; } });
28
28
  Object.defineProperty(exports, "convertPlatformFilterToApiFilter", { enumerable: true, get: function () { return index_js_1.convertPlatformFilterToApiFilter; } });
29
+ Object.defineProperty(exports, "AvailabilityLocationType", { enumerable: true, get: function () { return index_js_1.AvailabilityLocationType; } });
29
30
  Object.defineProperty(exports, "SYNTHETIC_CUSTOM_ID", { enumerable: true, get: function () { return index_js_1.SYNTHETIC_CUSTOM_ID; } });
30
31
  Object.defineProperty(exports, "SYNTHETIC_CUSTOMER_ID", { enumerable: true, get: function () { return index_js_1.SYNTHETIC_CUSTOMER_ID; } });
31
32
  var index_js_2 = require("./fetch-availability/index.js");
@@ -1,8 +1,16 @@
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 { type Service, type Paging, type Sorting } from '@wix/auto_sdk_bookings_services';
13
+ import { LocationType as AvailabilityLocationType, type ServiceFilters as AvailabilityFilter, type Attribute, type Location as AvailabilityLocation } from '@wix/auto_sdk_bookings_catalog-search';
6
14
  import type { FilterValue as Filter } from '@wix/headless-components/react';
7
15
  /**
8
16
  * Synthetic location ID constants for non-business locations
@@ -10,24 +18,48 @@ import type { FilterValue as Filter } from '@wix/headless-components/react';
10
18
  export declare const SYNTHETIC_CUSTOM_ID = "custom";
11
19
  export declare const SYNTHETIC_CUSTOMER_ID = "customer";
12
20
  /**
13
- * Paging metadata from query services response.
14
- * Extends the SDK's Paging type with hasNext flag to indicate if more services are available.
21
+ * Re-exported availability filter types so consumers can build availability UIs
22
+ * without importing the ambassador package directly.
23
+ */
24
+ export { AvailabilityLocationType, type AvailabilityFilter, type Attribute, type AvailabilityLocation, };
25
+ /**
26
+ * A service, optionally enriched with a bookable-availability flag.
27
+ * `available` is only populated when the query runs in availability mode.
28
+ */
29
+ export type ServiceWithAvailabilityItem = Service & {
30
+ /** Whether the service has bookable slots in the requested window. */
31
+ available?: boolean;
32
+ };
33
+ /**
34
+ * Paging metadata from a query response. Extends the SDK's Paging type with a
35
+ * `hasNext` flag and an opaque `cursor` (used in availability/cursor mode).
15
36
  */
16
37
  export type PagingMetadata = Paging & {
17
38
  /** Number of items returned in the current page */
18
39
  count?: number;
19
40
  /** Whether there are more services available to load */
20
41
  hasNext?: boolean;
42
+ /** Opaque cursor for the next page (availability/cursor mode only). Undefined in offset mode. */
43
+ cursor?: string;
21
44
  };
22
45
  /**
23
- * Query services request options
24
- * Used internally by ServiceListService to track query state
46
+ * Query services request options.
47
+ * When `availabilityFilter` is present, services are queried by real bookable
48
+ * availability; `pagingMetadata.cursor` then drives pagination instead of `offset`.
25
49
  */
26
50
  export interface QueryServicesRequest {
27
51
  appId: string;
28
52
  filter?: Filter;
29
53
  pagingMetadata?: PagingMetadata;
30
54
  sort?: Sorting[];
55
+ availabilityFilter?: AvailabilityFilter;
56
+ }
57
+ /**
58
+ * Result shape shared by both query modes.
59
+ */
60
+ export interface QueryServicesResult {
61
+ services: ServiceWithAvailabilityItem[];
62
+ pagingMetadata: PagingMetadata;
31
63
  }
32
64
  /**
33
65
  * Fetches a service by its ID
@@ -72,28 +104,41 @@ export declare const getServiceBySlug: (slug: string, appId?: string) => Promise
72
104
  * ```
73
105
  */
74
106
  export declare function convertPlatformFilterToApiFilter(platformFilter: Filter): Record<string, any>;
107
+ /**
108
+ * Whether an availability filter carries a real bookable constraint that warrants
109
+ * routing to the availability endpoint. Modifiers that don't define a query on their
110
+ * own (`timeZone`, `exactMatch`, `includeUnavailable`) are ignored: only a complete
111
+ * date window (both `localStartDate` and `localEndDate`) or a resource constraint
112
+ * (`locations`/`attributes`/`resourceTypes`) counts.
113
+ *
114
+ * This is the single source of truth shared by the API dispatch and the service's
115
+ * `hasAvailabilityFilterSignal`.
116
+ */
117
+ export declare function hasActiveAvailabilityFilter(availabilityFilter?: AvailabilityFilter): boolean;
75
118
  /**
76
119
  * Query services with pagination and sorting.
77
- * Returns services array and paging metadata including hasNext flag.
120
+ * Picks the underlying endpoint by request: availability filter present → catalog-search
121
+ * (cursor paging, `available` flag); otherwise the offset services query.
78
122
  *
79
- * @param params - Query parameters including appId, filter (platform Filter type), paging metadata, and sort options
123
+ * @param request - Query parameters including appId, filter (platform Filter type), paging
124
+ * metadata, sort options, and an optional availability filter
80
125
  * @returns Promise that resolves to services array and paging metadata with hasNext flag
81
126
  *
82
127
  * @example
83
128
  * ```ts
129
+ * // Offset query
84
130
  * const { services, pagingMetadata } = await queryServices({
85
131
  * appId: '13d21c63-b5ec-5912-8397-c3a5ddb27a97',
86
132
  * filter: { '_id': { $in: ['service1', 'service2'] } },
87
133
  * pagingMetadata: { limit: 20, offset: 0 },
88
- * sort: [{ fieldName: 'name', order: 'ASC' }]
89
134
  * });
90
135
  *
91
- * if (pagingMetadata.hasNext) {
92
- * // Load more services
93
- * }
136
+ * // Availability-aware query (cursor paging + `available` flag)
137
+ * const availability = await queryServices({
138
+ * appId: '13d21c63-b5ec-5912-8397-c3a5ddb27a97',
139
+ * availabilityFilter: { localStartDate: '...', localEndDate: '...', timeZone: '...' },
140
+ * pagingMetadata: { limit: 20 },
141
+ * });
94
142
  * ```
95
143
  */
96
- export declare function queryServices({ appId, filter, pagingMetadata, sort, }: QueryServicesRequest): Promise<{
97
- services: Service[];
98
- pagingMetadata: PagingMetadata;
99
- }>;
144
+ export declare function queryServices(request: QueryServicesRequest): Promise<QueryServicesResult>;
@@ -1,13 +1,23 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Query Services API
4
- * Fetches single or multiple services with pagination and sorting
4
+ * Fetches single or multiple services with pagination and sorting.
5
+ *
6
+ * `queryServices` is the single entry point. It decides which underlying endpoint
7
+ * to call based on the request: when an `availabilityFilter` is present it queries
8
+ * the catalog-search `QueryServicesByAvailability` endpoint (cursor paging, each
9
+ * service enriched with an `available` flag); otherwise it uses the services
10
+ * `queryServices` endpoint (offset paging). Callers always get the same
11
+ * `{ services, pagingMetadata }` shape.
5
12
  */
6
13
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.getServiceBySlug = exports.getServiceById = exports.SYNTHETIC_CUSTOMER_ID = exports.SYNTHETIC_CUSTOM_ID = void 0;
14
+ exports.getServiceBySlug = exports.getServiceById = exports.AvailabilityLocationType = exports.SYNTHETIC_CUSTOMER_ID = exports.SYNTHETIC_CUSTOM_ID = void 0;
8
15
  exports.convertPlatformFilterToApiFilter = convertPlatformFilterToApiFilter;
16
+ exports.hasActiveAvailabilityFilter = hasActiveAvailabilityFilter;
9
17
  exports.queryServices = queryServices;
10
18
  const auto_sdk_bookings_services_1 = require("@wix/auto_sdk_bookings_services");
19
+ const auto_sdk_bookings_catalog_search_1 = require("@wix/auto_sdk_bookings_catalog-search");
20
+ Object.defineProperty(exports, "AvailabilityLocationType", { enumerable: true, get: function () { return auto_sdk_bookings_catalog_search_1.LocationType; } });
11
21
  const constants_js_1 = require("../../services/constants.js");
12
22
  /**
13
23
  * Synthetic location ID constants for non-business locations
@@ -187,34 +197,100 @@ const buildServicesQueryRequest = ({ appId, filter: platformFilter, paging, sort
187
197
  const query = {
188
198
  filter: Object.keys(filter).length > 0 ? filter : undefined,
189
199
  paging: paging || {
190
- limit: 100, // Default limit
200
+ limit: constants_js_1.DEFAULT_QUERY_LIMIT,
191
201
  },
192
202
  sort,
193
203
  };
194
204
  return { query };
195
205
  };
206
+ /**
207
+ * Whether an availability filter carries a real bookable constraint that warrants
208
+ * routing to the availability endpoint. Modifiers that don't define a query on their
209
+ * own (`timeZone`, `exactMatch`, `includeUnavailable`) are ignored: only a complete
210
+ * date window (both `localStartDate` and `localEndDate`) or a resource constraint
211
+ * (`locations`/`attributes`/`resourceTypes`) counts.
212
+ *
213
+ * This is the single source of truth shared by the API dispatch and the service's
214
+ * `hasAvailabilityFilterSignal`.
215
+ */
216
+ function hasActiveAvailabilityFilter(availabilityFilter) {
217
+ if (!availabilityFilter)
218
+ return false;
219
+ const { localStartDate, localEndDate, locationIds, attributes, resourceTypes, } = availabilityFilter;
220
+ const hasDateWindow = Boolean(localStartDate && localEndDate);
221
+ const hasResourceConstraint = (locationIds?.length ?? 0) > 0 ||
222
+ (attributes?.length ?? 0) > 0 ||
223
+ (resourceTypes?.length ?? 0) > 0;
224
+ return hasDateWindow || hasResourceConstraint;
225
+ }
226
+ /**
227
+ * Queries services by real bookable availability (catalog-search). Cursor-based.
228
+ * The WQL filter reuses the same platform→API conversion as the offset query.
229
+ */
230
+ async function queryServicesByAvailability({ appId, filter, availabilityFilter, sort, pagingMetadata, }) {
231
+ const wqlFilter = {
232
+ appId: appId ?? constants_js_1.BOOKING_APP_ID,
233
+ };
234
+ if (filter) {
235
+ Object.assign(wqlFilter, convertPlatformFilterToApiFilter(filter));
236
+ }
237
+ const limit = pagingMetadata?.limit ?? constants_js_1.DEFAULT_QUERY_LIMIT;
238
+ const cursor = pagingMetadata?.cursor;
239
+ const availabilityQuery = {
240
+ filter: wqlFilter,
241
+ sort,
242
+ cursorPaging: { limit, ...(cursor ? { cursor } : {}) },
243
+ };
244
+ const response = await (0, auto_sdk_bookings_catalog_search_1.queryServicesByFilters)({
245
+ query: availabilityQuery,
246
+ serviceFilters: availabilityFilter,
247
+ });
248
+ const results = response.results ?? [];
249
+ return {
250
+ services: results.map((result) => ({
251
+ ...result.service,
252
+ available: result.available ?? true,
253
+ })),
254
+ pagingMetadata: {
255
+ limit,
256
+ count: response.pagingMetadata?.count ?? results.length,
257
+ hasNext: response.pagingMetadata?.hasNext ?? false,
258
+ cursor: response.pagingMetadata?.cursors?.next ?? undefined,
259
+ },
260
+ };
261
+ }
196
262
  /**
197
263
  * Query services with pagination and sorting.
198
- * Returns services array and paging metadata including hasNext flag.
264
+ * Picks the underlying endpoint by request: availability filter present → catalog-search
265
+ * (cursor paging, `available` flag); otherwise the offset services query.
199
266
  *
200
- * @param params - Query parameters including appId, filter (platform Filter type), paging metadata, and sort options
267
+ * @param request - Query parameters including appId, filter (platform Filter type), paging
268
+ * metadata, sort options, and an optional availability filter
201
269
  * @returns Promise that resolves to services array and paging metadata with hasNext flag
202
270
  *
203
271
  * @example
204
272
  * ```ts
273
+ * // Offset query
205
274
  * const { services, pagingMetadata } = await queryServices({
206
275
  * appId: '13d21c63-b5ec-5912-8397-c3a5ddb27a97',
207
276
  * filter: { '_id': { $in: ['service1', 'service2'] } },
208
277
  * pagingMetadata: { limit: 20, offset: 0 },
209
- * sort: [{ fieldName: 'name', order: 'ASC' }]
210
278
  * });
211
279
  *
212
- * if (pagingMetadata.hasNext) {
213
- * // Load more services
214
- * }
280
+ * // Availability-aware query (cursor paging + `available` flag)
281
+ * const availability = await queryServices({
282
+ * appId: '13d21c63-b5ec-5912-8397-c3a5ddb27a97',
283
+ * availabilityFilter: { localStartDate: '...', localEndDate: '...', timeZone: '...' },
284
+ * pagingMetadata: { limit: 20 },
285
+ * });
215
286
  * ```
216
287
  */
217
- async function queryServices({ appId, filter, pagingMetadata, sort, }) {
288
+ async function queryServices(request) {
289
+ const { appId, filter, pagingMetadata, sort, availabilityFilter } = request;
290
+ // Decide the endpoint by the filters passed.
291
+ if (hasActiveAvailabilityFilter(availabilityFilter)) {
292
+ return queryServicesByAvailability(request);
293
+ }
218
294
  try {
219
295
  const queryRequest = buildServicesQueryRequest({
220
296
  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 { Service } from '@wix/auto_sdk_bookings_services';
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: Service[];
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 { Service as ServiceType } from '@wix/auto_sdk_bookings_services';
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: ServiceType;
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: ServiceType[];
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;
@@ -160,7 +160,7 @@ exports.ServiceRepeater = react_1.default.forwardRef((props, ref) => {
160
160
  // Custom itemWrapper that matches ItemWrapperProps signature
161
161
  const serviceItemWrapper = (wrapperProps) => {
162
162
  const { item: service, children: itemChildren } = wrapperProps;
163
- return ((0, jsx_runtime_1.jsx)(Service.Root, { service: service, "data-testid": exports.TestIds.serviceListService, "data-service-id": service._id, "data-item-id": service._id, children: isServiceRootFunction
163
+ return ((0, jsx_runtime_1.jsx)(Service.Root, { service: service, "data-testid": exports.TestIds.serviceListService, "data-service-id": service._id, "data-item-id": service._id, "data-available": service.available, children: isServiceRootFunction
164
164
  ? children
165
165
  : itemChildren }, service._id));
166
166
  };
@@ -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,125 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.WithAvailabilityStates = exports.WithServiceDetails = exports.EmptyState = exports.BasicList = void 0;
37
+ const jsx_runtime_1 = require("react/jsx-runtime");
38
+ const ServiceList = __importStar(require("./ServiceList"));
39
+ const Booking = __importStar(require("../booking/Booking"));
40
+ const Service = __importStar(require("../service/Service"));
41
+ const services_mocks_1 = require("../../__mocks__/services/services-mocks");
42
+ const constants_1 = require("../../services/constants");
43
+ const config = {
44
+ services: services_mocks_1.mockServicesWithAvailability,
45
+ categories: services_mocks_1.mockCategories,
46
+ options: { appId: constants_1.BOOKING_APP_ID },
47
+ };
48
+ const emptyConfig = {
49
+ services: [],
50
+ categories: [],
51
+ options: { appId: constants_1.BOOKING_APP_ID },
52
+ };
53
+ /**
54
+ * ServiceList component story.
55
+ *
56
+ * ServiceList renders a list of bookings services. When the configured services
57
+ * carry an `available` flag (availability mode), stories can style each item by
58
+ * its availability. It supports filtering by categories, availability dates, and
59
+ * custom attributes.
60
+ */
61
+ const meta = {
62
+ title: 'Bookings/ServiceList',
63
+ component: ServiceList.Root,
64
+ tags: ['autodocs'],
65
+ };
66
+ exports.default = meta;
67
+ /**
68
+ * Basic list with three services, two available and one unavailable.
69
+ */
70
+ exports.BasicList = {
71
+ render: () => ((0, jsx_runtime_1.jsx)(Booking.Root, { children: (0, jsx_runtime_1.jsx)(ServiceList.Root, { serviceListConfig: config, children: (0, jsx_runtime_1.jsx)(ServiceList.Services, { children: (0, jsx_runtime_1.jsx)(ServiceList.ServiceRepeater, { children: (0, jsx_runtime_1.jsx)(Service.Name, {}) }) }) }) })),
72
+ };
73
+ /**
74
+ * List with empty state message displayed when no services are available.
75
+ */
76
+ exports.EmptyState = {
77
+ render: () => ((0, jsx_runtime_1.jsx)(Booking.Root, { children: (0, jsx_runtime_1.jsx)(ServiceList.Root, { serviceListConfig: emptyConfig, children: (0, jsx_runtime_1.jsx)(ServiceList.Services, { emptyState: (0, jsx_runtime_1.jsx)("div", { style: {
78
+ padding: '2rem',
79
+ textAlign: 'center',
80
+ color: 'var(--color-muted-foreground)',
81
+ }, children: "No services available" }), children: (0, jsx_runtime_1.jsx)(ServiceList.ServiceRepeater, { children: (0, jsx_runtime_1.jsx)(Service.Name, {}) }) }) }) })),
82
+ };
83
+ /**
84
+ * List with detailed service information (name and price).
85
+ */
86
+ exports.WithServiceDetails = {
87
+ render: () => ((0, jsx_runtime_1.jsx)(Booking.Root, { children: (0, jsx_runtime_1.jsx)(ServiceList.Root, { serviceListConfig: config, children: (0, jsx_runtime_1.jsx)(ServiceList.Services, { children: (0, jsx_runtime_1.jsx)(ServiceList.ServiceRepeater, { asChild: true, children: ({ items, itemWrapper, }) => ((0, jsx_runtime_1.jsx)("ul", { style: { listStyle: 'none', padding: 0 }, children: items.map((service, index) => ((0, jsx_runtime_1.jsx)("li", { style: {
88
+ padding: '1rem',
89
+ borderBottom: '1px solid var(--color-border)',
90
+ opacity: service.available === false ? 0.6 : 1,
91
+ }, children: itemWrapper({
92
+ item: service,
93
+ index,
94
+ children: ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(Service.Name, {}), (0, jsx_runtime_1.jsx)(Service.Price, {})] })),
95
+ }) }, service._id))) })) }) }) }) })),
96
+ };
97
+ /**
98
+ * List with custom styling for available/unavailable items.
99
+ */
100
+ exports.WithAvailabilityStates = {
101
+ render: () => ((0, jsx_runtime_1.jsx)(Booking.Root, { children: (0, jsx_runtime_1.jsx)(ServiceList.Root, { serviceListConfig: config, children: (0, jsx_runtime_1.jsx)(ServiceList.Services, { children: (0, jsx_runtime_1.jsx)(ServiceList.ServiceRepeater, { asChild: true, children: ({ items, itemWrapper, }) => ((0, jsx_runtime_1.jsx)("ul", { style: { listStyle: 'none', padding: 0 }, children: items.map((service, index) => ((0, jsx_runtime_1.jsx)("li", { style: {
102
+ padding: '1rem',
103
+ marginBottom: '0.5rem',
104
+ borderRadius: '4px',
105
+ backgroundColor: service.available
106
+ ? 'color-mix(in srgb, var(--color-primary) 8%, transparent)'
107
+ : 'var(--color-secondary)',
108
+ borderLeft: `4px solid ${service.available ? 'var(--color-primary)' : 'var(--color-border)'}`,
109
+ opacity: service.available ? 1 : 0.7,
110
+ }, children: itemWrapper({
111
+ item: service,
112
+ index,
113
+ children: ((0, jsx_runtime_1.jsxs)("div", { style: {
114
+ display: 'flex',
115
+ justifyContent: 'space-between',
116
+ alignItems: 'center',
117
+ }, children: [(0, jsx_runtime_1.jsx)(Service.Name, {}), (0, jsx_runtime_1.jsx)("span", { style: {
118
+ fontSize: '0.875rem',
119
+ fontWeight: 500,
120
+ color: service.available
121
+ ? 'var(--color-primary)'
122
+ : 'var(--color-muted-foreground)',
123
+ }, children: service.available ? 'Available' : 'Not Available' })] })),
124
+ }) }, service._id))) })) }) }) }) })),
125
+ };
@@ -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,9 +5,11 @@
5
5
  * @module Services/Constants
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.ClientServiceIds = exports.STAFF_MEMBER_RESOURCE_TYPE_ID = exports.BOOKING_APP_ID = void 0;
8
+ exports.ClientServiceIds = exports.STAFF_MEMBER_RESOURCE_TYPE_ID = exports.DEFAULT_QUERY_LIMIT = exports.BOOKING_APP_ID = void 0;
9
9
  /** Booking app definition ID */
10
10
  exports.BOOKING_APP_ID = '13d21c63-b5ec-5912-8397-c3a5ddb27a97';
11
+ /** Default page size for service queries (offset and cursor/availability modes) */
12
+ exports.DEFAULT_QUERY_LIMIT = 100;
11
13
  /** Resource type ID for staff members */
12
14
  exports.STAFF_MEMBER_RESOURCE_TYPE_ID = '1cd44cf8-756f-41c3-bd90-3e2ffcaf1155';
13
15
  exports.ClientServiceIds = {
@@ -5,8 +5,8 @@
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';
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
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';