@wix/headless-bookings 0.0.10 → 0.0.12

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 (69) hide show
  1. package/cjs/dist/api/index.d.ts +10 -1
  2. package/cjs/dist/api/index.js +10 -1
  3. package/cjs/dist/api/query-services/index.d.ts +78 -0
  4. package/cjs/dist/api/query-services/index.js +137 -0
  5. package/cjs/dist/react/core/booking/Booking.d.ts +5 -5
  6. package/cjs/dist/react/core/booking/Booking.js +5 -5
  7. package/cjs/dist/react/core/location/Location.d.ts +44 -0
  8. package/cjs/dist/react/core/location/Location.js +78 -0
  9. package/cjs/dist/react/core/service/Service.d.ts +160 -17
  10. package/cjs/dist/react/core/service/Service.js +211 -13
  11. package/cjs/dist/react/core/service-list/ServiceList.d.ts +75 -0
  12. package/cjs/dist/react/core/service-list/ServiceList.js +58 -0
  13. package/cjs/dist/react/index.d.ts +2 -0
  14. package/cjs/dist/react/index.js +2 -0
  15. package/cjs/dist/react/location/Location.d.ts +77 -0
  16. package/cjs/dist/react/location/Location.js +89 -0
  17. package/cjs/dist/react/service/Service.d.ts +1036 -31
  18. package/cjs/dist/react/service/Service.js +1060 -47
  19. package/cjs/dist/react/service-list/ServiceList.d.ts +185 -0
  20. package/cjs/dist/react/service-list/ServiceList.js +184 -0
  21. package/cjs/dist/services/booking/booking.d.ts +19 -11
  22. package/cjs/dist/services/booking/booking.js +48 -16
  23. package/cjs/dist/services/index.d.ts +3 -2
  24. package/cjs/dist/services/index.js +2 -1
  25. package/cjs/dist/services/service/service.d.ts +71 -18
  26. package/cjs/dist/services/service/service.js +186 -51
  27. package/cjs/dist/services/service-list/service-list.d.ts +157 -0
  28. package/cjs/dist/services/service-list/service-list.js +274 -0
  29. package/cjs/dist/utils/dateAndTime.d.ts +2 -0
  30. package/cjs/dist/utils/dateAndTime.js +18 -0
  31. package/cjs/dist/utils/money.d.ts +21 -0
  32. package/cjs/dist/utils/money.js +34 -0
  33. package/dist/api/index.d.ts +10 -1
  34. package/dist/api/index.js +10 -1
  35. package/dist/api/query-services/index.d.ts +78 -0
  36. package/dist/api/query-services/index.js +137 -0
  37. package/dist/react/core/booking/Booking.d.ts +5 -5
  38. package/dist/react/core/booking/Booking.js +5 -5
  39. package/dist/react/core/location/Location.d.ts +44 -0
  40. package/dist/react/core/location/Location.js +78 -0
  41. package/dist/react/core/service/Service.d.ts +160 -17
  42. package/dist/react/core/service/Service.js +211 -13
  43. package/dist/react/core/service-list/ServiceList.d.ts +75 -0
  44. package/dist/react/core/service-list/ServiceList.js +58 -0
  45. package/dist/react/index.d.ts +2 -0
  46. package/dist/react/index.js +2 -0
  47. package/dist/react/location/Location.d.ts +77 -0
  48. package/dist/react/location/Location.js +89 -0
  49. package/dist/react/service/Service.d.ts +1036 -31
  50. package/dist/react/service/Service.js +1060 -47
  51. package/dist/react/service-list/ServiceList.d.ts +185 -0
  52. package/dist/react/service-list/ServiceList.js +184 -0
  53. package/dist/services/booking/booking.d.ts +19 -11
  54. package/dist/services/booking/booking.js +48 -16
  55. package/dist/services/index.d.ts +3 -2
  56. package/dist/services/index.js +2 -1
  57. package/dist/services/service/service.d.ts +71 -18
  58. package/dist/services/service/service.js +186 -51
  59. package/dist/services/service-list/service-list.d.ts +157 -0
  60. package/dist/services/service-list/service-list.js +274 -0
  61. package/dist/utils/dateAndTime.d.ts +2 -0
  62. package/dist/utils/dateAndTime.js +18 -0
  63. package/dist/utils/money.d.ts +21 -0
  64. package/dist/utils/money.js +34 -0
  65. package/package.json +2 -2
  66. package/cjs/dist/api/query-service/index.d.ts +0 -23
  67. package/cjs/dist/api/query-service/index.js +0 -41
  68. package/dist/api/query-service/index.d.ts +0 -23
  69. package/dist/api/query-service/index.js +0 -41
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * Service - High-level component for displaying service information
4
4
  * Provides components for displaying service details and booking actions
@@ -6,14 +6,49 @@ import { jsx as _jsx } from "react/jsx-runtime";
6
6
  import React from 'react';
7
7
  import * as CoreService from '../core/service/Service.js';
8
8
  import { AsChildSlot } from '@wix/headless-utils/react';
9
+ import { Money, GenericList } from '@wix/headless-components/react';
10
+ import { WixMediaImage } from '@wix/headless-media/react';
11
+ import { MediaGallery } from '@wix/headless-media/react';
12
+ import { services } from '@wix/bookings';
13
+ import * as Location from '../location/Location.js';
9
14
  var TestIds;
10
15
  (function (TestIds) {
11
16
  TestIds["serviceRoot"] = "service-root";
12
17
  TestIds["serviceName"] = "service-name";
13
18
  TestIds["serviceDescription"] = "service-description";
19
+ TestIds["servicePrice"] = "service-price";
20
+ TestIds["serviceTagline"] = "service-tagline";
21
+ TestIds["serviceDurationInMinutes"] = "service-duration-in-minutes";
22
+ TestIds["serviceType"] = "service-type";
23
+ TestIds["serviceCategory"] = "service-category";
24
+ TestIds["serviceLocations"] = "service-locations";
25
+ TestIds["serviceLocation"] = "service-location";
26
+ TestIds["serviceLocationRepeater"] = "service-location-repeater";
27
+ TestIds["locationRoot"] = "location-root";
28
+ TestIds["serviceStaffMembers"] = "service-staff-members";
29
+ TestIds["serviceStaffMemberRepeater"] = "service-staff-member-repeater";
30
+ TestIds["serviceMedia"] = "service-media";
31
+ TestIds["serviceMainMedia"] = "service-main-media";
32
+ TestIds["serviceCoverMedia"] = "service-cover-media";
33
+ TestIds["serviceMediaItems"] = "service-media-items";
34
+ TestIds["serviceSchedule"] = "service-schedule";
35
+ TestIds["serviceDefaultCapacity"] = "service-default-capacity";
36
+ TestIds["serviceRaw"] = "service-raw";
14
37
  TestIds["serviceActionSelect"] = "service-action-select";
15
38
  TestIds["serviceActionAdd"] = "service-action-add";
16
39
  TestIds["serviceActionRemove"] = "service-action-remove";
40
+ // Policy component TestIds
41
+ TestIds["servicePolicyLatestCancellationInMinutes"] = "service-policy-latest-cancellation-in-minutes";
42
+ TestIds["servicePolicyLatestBookingInMinutes"] = "service-policy-latest-booking-in-minutes";
43
+ TestIds["servicePolicyEarliestBookingInMinutes"] = "service-policy-earliest-booking-in-minutes";
44
+ TestIds["servicePolicyLatestRescheduleInMinutes"] = "service-policy-latest-reschedule-in-minutes";
45
+ TestIds["servicePolicyWaitlistCapacity"] = "service-policy-waitlist-capacity";
46
+ TestIds["servicePolicyBookAfterStartEnabled"] = "service-policy-book-after-start-enabled";
47
+ TestIds["servicePolicyMaxParticipantsPerBooking"] = "service-policy-max-participants-per-booking";
48
+ // Schedule component TestIds
49
+ TestIds["serviceScheduleFirstSessionStart"] = "service-schedule-first-session-start";
50
+ TestIds["serviceScheduleLastSessionEnd"] = "service-schedule-last-session-end";
51
+ TestIds["serviceScheduleSessionDuration"] = "service-schedule-session-duration";
17
52
  })(TestIds || (TestIds = {}));
18
53
  /**
19
54
  * Root component that provides service context to the entire app.
@@ -35,8 +70,8 @@ var TestIds;
35
70
  * ```
36
71
  */
37
72
  export const Root = React.forwardRef((props, ref) => {
38
- const { children, service, asChild, className, ...attrs } = props;
39
- return (_jsx(CoreService.Root, { serviceServiceConfig: { service }, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceRoot, customElement: children, ...attrs, children: _jsx("div", { children: children }) }) }));
73
+ const { children, service, appId, asChild, className, ...attrs } = props;
74
+ return (_jsx(CoreService.Root, { serviceServiceConfig: { service, appId }, children: ({ selected, bookable }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceRoot, "data-selected": selected, "data-bookable": bookable, customElement: children, customElementProps: { selected, bookable }, ...attrs, children: children })) }));
40
75
  });
41
76
  Root.displayName = 'Service.Root';
42
77
  /**
@@ -88,6 +123,595 @@ export const Description = React.forwardRef((props, ref) => {
88
123
  return (_jsx(CoreService.Info, { children: ({ description }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceDescription, customElement: children, customElementProps: { description }, children: _jsx("div", { children: description }) })) }));
89
124
  });
90
125
  Description.displayName = 'Service.Description';
126
+ /**
127
+ * Displays the service price using the Money component with customizable rendering.
128
+ * Renders different defaults based on rate type:
129
+ * - FIXED: Shows formatted price (e.g., '$50.00')
130
+ * - VARIED: Shows price range (e.g., '$30.00 - $50.00')
131
+ * - CUSTOM: Shows custom description text
132
+ * - NO_FEE: Shows zero price (e.g., '$0.00')
133
+ *
134
+ * @component
135
+ * @example
136
+ * ```tsx
137
+ * // Default usage - renders based on rate type
138
+ * <Service.Price className="text-3xl font-bold" />
139
+ *
140
+ * // Custom rendering with full price data
141
+ * <Service.Price asChild>
142
+ * {({ rateType, money, minPrice, maxPrice, priceDescription, hasAddOns, hasPricingPlans }) => (
143
+ * <div>
144
+ * {rateType === 'FIXED' && money && <Money money={money} />}
145
+ * {rateType === 'VARIED' && minPrice && maxPrice && (
146
+ * <span><Money money={minPrice} /> - <Money money={maxPrice} /></span>
147
+ * )}
148
+ * {rateType === 'CUSTOM' && priceDescription && <span>{priceDescription}</span>}
149
+ * {rateType === 'NO_FEE' && money && <Money money={money} />}
150
+ * {hasPricingPlans && <span>Pricing plans available</span>}
151
+ * </div>
152
+ * )}
153
+ * </Service.Price>
154
+ * ```
155
+ */
156
+ export const Price = React.forwardRef((props, ref) => {
157
+ const { asChild, children, className } = props;
158
+ return (_jsx(CoreService.Price, { children: (childProps) => {
159
+ const { rateType, money, minPrice, maxPrice, priceDescription } = childProps;
160
+ // Default rendering based on rate type
161
+ const renderDefaultPrice = () => {
162
+ switch (rateType) {
163
+ case services.RateType.FIXED:
164
+ return money ? (_jsx(Money, { money: money, className: className })) : null;
165
+ case services.RateType.VARIED:
166
+ if (minPrice && maxPrice) {
167
+ return (_jsxs("span", { className: className, "data-testid": TestIds.servicePrice, children: [_jsx(Money, { money: minPrice }), " - ", _jsx(Money, { money: maxPrice })] }));
168
+ }
169
+ // Fallback to defaultPrice if min/max not available
170
+ return money ? (_jsx(Money, { money: money, className: className })) : null;
171
+ case services.RateType.CUSTOM:
172
+ return priceDescription ? (_jsx("span", { className: className, "data-testid": TestIds.servicePrice, children: priceDescription })) : null;
173
+ case services.RateType.NO_FEE:
174
+ return money ? (_jsx(Money, { money: money, className: className })) : null;
175
+ default:
176
+ // UNKNOWN_RATE_TYPE or undefined
177
+ return null;
178
+ }
179
+ };
180
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.servicePrice, customElement: children, customElementProps: childProps, children: renderDefaultPrice() }));
181
+ } }));
182
+ });
183
+ Price.displayName = 'Service.Price';
184
+ /**
185
+ * Displays the service tagline with customizable rendering.
186
+ *
187
+ * @component
188
+ * @example
189
+ * ```tsx
190
+ * // Default usage
191
+ * <Service.Tagline className="text-lg text-secondary-foreground" />
192
+ * ```
193
+ *
194
+ * @example
195
+ * ```tsx
196
+ * // asChild with primitive element
197
+ * <Service.Tagline asChild>
198
+ * <p className="italic text-secondary-foreground" />
199
+ * </Service.Tagline>
200
+ * ```
201
+ *
202
+ * @example
203
+ * ```tsx
204
+ * // asChild with custom component accessing tagline data
205
+ * <Service.Tagline asChild>
206
+ * {React.forwardRef(({tagline, ...props}, ref) => (
207
+ * <blockquote ref={ref} {...props} className="border-l-4 pl-4">
208
+ * "{tagline}"
209
+ * </blockquote>
210
+ * ))}
211
+ * </Service.Tagline>
212
+ * ```
213
+ */
214
+ export const Tagline = React.forwardRef((props, ref) => {
215
+ const { asChild, children, className } = props;
216
+ return (_jsx(CoreService.Info, { children: ({ tagline }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceTagline, customElement: children, customElementProps: { tagline }, children: tagline ? _jsx("p", { children: tagline }) : null })) }));
217
+ });
218
+ Tagline.displayName = 'Service.Tagline';
219
+ /**
220
+ * Displays the service duration in minutes (raw number) with customizable rendering.
221
+ * Returns the duration as a numeric value representing minutes.
222
+ *
223
+ * @component
224
+ * @example
225
+ * ```tsx
226
+ * // Default usage - displays raw minutes
227
+ * <Service.DurationInMinutes className="text-base text-secondary-foreground" />
228
+ * ```
229
+ *
230
+ * @example
231
+ * ```tsx
232
+ * // asChild with primitive element
233
+ * <Service.DurationInMinutes asChild>
234
+ * <span className="font-semibold" />
235
+ * </Service.DurationInMinutes>
236
+ * ```
237
+ *
238
+ * @example
239
+ * ```tsx
240
+ * // asChild with custom component to format duration
241
+ * <Service.DurationInMinutes asChild>
242
+ * {React.forwardRef(({duration, ...props}, ref) => (
243
+ * <span ref={ref} {...props}>
244
+ * {duration} minutes
245
+ * </span>
246
+ * ))}
247
+ * </Service.DurationInMinutes>
248
+ * ```
249
+ */
250
+ export const DurationInMinutes = React.forwardRef((props, ref) => {
251
+ const { asChild, children, className } = props;
252
+ return (_jsx(CoreService.Info, { children: ({ duration }) => {
253
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceDurationInMinutes, customElement: children, customElementProps: { duration }, children: duration ? _jsx("span", { children: duration }) : null }));
254
+ } }));
255
+ });
256
+ DurationInMinutes.displayName = 'Service.DurationInMinutes';
257
+ /**
258
+ * Displays the service type (APPOINTMENT, CLASS, COURSE, etc.) with customizable rendering.
259
+ *
260
+ * @component
261
+ * @example
262
+ * ```tsx
263
+ * // Default usage
264
+ * <Service.Type className="text-sm text-secondary-foreground uppercase" />
265
+ * ```
266
+ *
267
+ * @example
268
+ * ```tsx
269
+ * // asChild with badge styling
270
+ * <Service.Type asChild>
271
+ * <span className="px-2 py-1 bg-secondary text-secondary-foreground rounded-full text-xs" />
272
+ * </Service.Type>
273
+ * ```
274
+ *
275
+ * @example
276
+ * ```tsx
277
+ * // asChild with custom component to format type
278
+ * <Service.Type asChild>
279
+ * {React.forwardRef(({type, ...props}, ref) => {
280
+ * const label = {
281
+ * APPOINTMENT: '1-on-1',
282
+ * CLASS: 'Group Class',
283
+ * COURSE: 'Course',
284
+ * }[type] || type;
285
+ * return <span ref={ref} {...props}>{label}</span>;
286
+ * })}
287
+ * </Service.Type>
288
+ * ```
289
+ */
290
+ export const Type = React.forwardRef((props, ref) => {
291
+ const { asChild, children, className } = props;
292
+ return (_jsx(CoreService.Info, { children: ({ type }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceType, customElement: children, customElementProps: { type }, children: type ? _jsx("span", { children: type }) : null })) }));
293
+ });
294
+ Type.displayName = 'Service.Type';
295
+ /**
296
+ * Displays the service category with customizable rendering.
297
+ *
298
+ * @component
299
+ * @example
300
+ * ```tsx
301
+ * // Default usage
302
+ * <Service.Category className="text-sm text-secondary-foreground" />
303
+ * ```
304
+ *
305
+ * @example
306
+ * ```tsx
307
+ * // asChild with link styling
308
+ * <Service.Category asChild>
309
+ * <a href="#" className="text-primary hover:underline" />
310
+ * </Service.Category>
311
+ * ```
312
+ *
313
+ * @example
314
+ * ```tsx
315
+ * // asChild with custom component accessing category data
316
+ * <Service.Category asChild>
317
+ * {React.forwardRef(({category, ...props}, ref) => (
318
+ * <a
319
+ * ref={ref}
320
+ * {...props}
321
+ * href={`/category/${category._id}`}
322
+ * className="text-primary hover:underline"
323
+ * >
324
+ * {category.name}
325
+ * </a>
326
+ * ))}
327
+ * </Service.Category>
328
+ * ```
329
+ */
330
+ export const Category = React.forwardRef((props, ref) => {
331
+ const { asChild, children, className } = props;
332
+ return (_jsx(CoreService.Info, { children: ({ category }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceCategory, customElement: children, customElementProps: { category }, children: category?.name ? _jsx("span", { children: category.name }) : null })) }));
333
+ });
334
+ Category.displayName = 'Service.Category';
335
+ /**
336
+ * Container for service locations list (Container Level).
337
+ * Follows the 3-level List/Options/Repeater pattern.
338
+ * Provides context and doesn't render when there are no locations.
339
+ *
340
+ * @component
341
+ * @example
342
+ * ```tsx
343
+ * // Basic usage with 3-level pattern
344
+ * <Service.Locations>
345
+ * <Service.Locations.List emptyState={<div>No locations available</div>}>
346
+ * <Service.Locations.LocationRepeater>
347
+ * {({ location }) => (
348
+ * <div>
349
+ * <span>{location.name}</span>
350
+ * <Address.Root address={location.address}>
351
+ * <Address.Formatted />
352
+ * </Address.Root>
353
+ * </div>
354
+ * )}
355
+ * </Service.Locations.LocationRepeater>
356
+ * </Service.Locations.List>
357
+ * </Service.Locations>
358
+ *
359
+ * // With asChild pattern for custom container
360
+ * <Service.Locations asChild>
361
+ * <ul className="locations-list">
362
+ * <Service.Locations.List>
363
+ * <Service.Locations.LocationRepeater>
364
+ * {({ location }) => <li>{location.name}</li>}
365
+ * </Service.Locations.LocationRepeater>
366
+ * </Service.Locations.List>
367
+ * </ul>
368
+ * </Service.Locations>
369
+ * ```
370
+ */
371
+ const LocationsBase = React.forwardRef((props, ref) => {
372
+ const { asChild, children, className, ...attributes } = props;
373
+ return (_jsx(CoreService.Info, { children: ({ locations }) => {
374
+ // Container level: don't render if no locations
375
+ if (!locations || locations.length === 0) {
376
+ return null;
377
+ }
378
+ // Map locations to include required id field for GenericList
379
+ const items = locations.map((location, index) => ({
380
+ ...location,
381
+ id: location._id || String(index),
382
+ }));
383
+ return (_jsx(GenericList.Root, { items: items, children: _jsx(AsChildSlot, { ...attributes, ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceLocations, customElement: children, children: _jsx("div", { children: React.isValidElement(children) ? children : null }) }) }));
384
+ } }));
385
+ });
386
+ LocationsBase.displayName = 'Service.Locations';
387
+ /**
388
+ * List container for locations with empty state support (List Container Level).
389
+ * Renders emptyState when no locations, otherwise renders children.
390
+ *
391
+ * @component
392
+ * @example
393
+ * ```tsx
394
+ * <Service.Locations.List emptyState={<div>No locations available</div>}>
395
+ * <Service.Locations.LocationRepeater />
396
+ * </Service.Locations.List>
397
+ * ```
398
+ */
399
+ export const LocationsList = React.forwardRef((props, ref) => {
400
+ const { children, emptyState, className, ...otherProps } = props;
401
+ return (_jsx(GenericList.Items, { ref: ref, emptyState: emptyState, className: className, ...otherProps, children: children }));
402
+ });
403
+ LocationsList.displayName = 'Service.Locations.List';
404
+ /**
405
+ * Repeater component that renders Location.Root for each location.
406
+ * Supports asChild pattern for custom rendering.
407
+ *
408
+ * @component
409
+ * @example
410
+ * ```tsx
411
+ * // Standard usage - renders Location.Root for each location
412
+ * <Service.Locations.LocationRepeater>
413
+ * <span className="location-badge">📍</span>
414
+ * </Service.Locations.LocationRepeater>
415
+ *
416
+ * // AsChild usage with custom numbered list
417
+ * <Service.Locations.LocationRepeater asChild>
418
+ * {({ items, itemWrapper }) => (
419
+ * <ol>
420
+ * {items.map((item, index) => (
421
+ * <li key={item.id}>
422
+ * {index + 1}. {itemWrapper({ item, index, children: null })}
423
+ * </li>
424
+ * ))}
425
+ * </ol>
426
+ * )}
427
+ * </Service.Locations.LocationRepeater>
428
+ * ```
429
+ */
430
+ export const LocationRepeater = React.forwardRef((props, ref) => {
431
+ const { children, asChild } = props;
432
+ // Custom itemWrapper that renders Location.Root for each location
433
+ const locationItemWrapper = ({ item: location, children: itemChildren, }) => {
434
+ return (_jsx(Location.Root, { location: location, "data-testid": TestIds.serviceLocationRepeater, children: itemChildren || undefined }, location.id));
435
+ };
436
+ return (_jsx(GenericList.Repeater, { ref: ref, itemWrapper: locationItemWrapper, asChild: asChild, children: children }));
437
+ });
438
+ LocationRepeater.displayName = 'Service.Locations.LocationRepeater';
439
+ /**
440
+ * Container for service staff members list (Container Level).
441
+ * Follows the 3-level List/Options/Repeater pattern.
442
+ * Provides context and doesn't render when there are no staff members.
443
+ *
444
+ * @component
445
+ * @example
446
+ * ```tsx
447
+ * // Basic usage with 3-level pattern
448
+ * <Service.StaffMembers>
449
+ * <Service.StaffMembers.List emptyState={<div>No staff assigned</div>}>
450
+ * <Service.StaffMembers.StaffMemberRepeater>
451
+ * {({ staffMember }) => (
452
+ * <div className="staff-card">
453
+ * <img src={staffMember.mainMedia?.image} alt={staffMember.name} />
454
+ * <span>{staffMember.name}</span>
455
+ * </div>
456
+ * )}
457
+ * </Service.StaffMembers.StaffMemberRepeater>
458
+ * </Service.StaffMembers.List>
459
+ * </Service.StaffMembers>
460
+ *
461
+ * // With asChild pattern for custom container
462
+ * <Service.StaffMembers asChild>
463
+ * <ul className="staff-list">
464
+ * <Service.StaffMembers.List>
465
+ * <Service.StaffMembers.StaffMemberRepeater>
466
+ * {({ staffMember }) => <li>{staffMember.name}</li>}
467
+ * </Service.StaffMembers.StaffMemberRepeater>
468
+ * </Service.StaffMembers.List>
469
+ * </ul>
470
+ * </Service.StaffMembers>
471
+ * ```
472
+ */
473
+ export const StaffMembersBase = React.forwardRef((props, ref) => {
474
+ const { asChild, children, className, ...attributes } = props;
475
+ return (_jsx(CoreService.Info, { children: ({ staff }) => {
476
+ // Container level: don't render if no staff members
477
+ if (!staff || staff.length === 0) {
478
+ return null;
479
+ }
480
+ // Map staff to include required id field for GenericList
481
+ const items = staff.map((member, index) => ({
482
+ ...member,
483
+ id: member._id || String(index),
484
+ }));
485
+ return (_jsx(GenericList.Root, { items: items, children: _jsx(AsChildSlot, { ...attributes, ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceStaffMembers, customElement: children, children: _jsx("div", { children: React.isValidElement(children) ? children : null }) }) }));
486
+ } }));
487
+ });
488
+ StaffMembersBase.displayName = 'Service.StaffMembers';
489
+ /**
490
+ * List container for staff members with empty state support (List Container Level).
491
+ * Renders emptyState when no staff members, otherwise renders children.
492
+ *
493
+ * @component
494
+ * @example
495
+ * ```tsx
496
+ * <Service.StaffMembers.List emptyState={<div>No staff assigned</div>}>
497
+ * <Service.StaffMembers.StaffMemberRepeater />
498
+ * </Service.StaffMembers.List>
499
+ * ```
500
+ */
501
+ export const StaffMembersList = React.forwardRef((props, ref) => {
502
+ const { children, emptyState, className, ...otherProps } = props;
503
+ return (_jsx(GenericList.Items, { ref: ref, emptyState: emptyState, className: className, ...otherProps, children: children }));
504
+ });
505
+ StaffMembersList.displayName = 'Service.StaffMembers.List';
506
+ /**
507
+ * Repeater component that renders staff member name for each staff member.
508
+ * Supports asChild pattern for custom rendering.
509
+ *
510
+ * @component
511
+ * @example
512
+ * ```tsx
513
+ * // Standard usage - renders staff member names
514
+ * <Service.StaffMembers.StaffMemberRepeater>
515
+ * <span className="staff-badge">👤</span>
516
+ * </Service.StaffMembers.StaffMemberRepeater>
517
+ *
518
+ * // AsChild usage with custom numbered list
519
+ * <Service.StaffMembers.StaffMemberRepeater asChild>
520
+ * {({ items, itemWrapper }) => (
521
+ * <ol>
522
+ * {items.map((item, index) => (
523
+ * <li key={item.id}>
524
+ * {index + 1}. {itemWrapper({ item, index, children: null })}
525
+ * </li>
526
+ * ))}
527
+ * </ol>
528
+ * )}
529
+ * </Service.StaffMembers.StaffMemberRepeater>
530
+ * ```
531
+ */
532
+ export const StaffMemberRepeater = React.forwardRef((props, ref) => {
533
+ const { children, asChild } = props;
534
+ // Custom itemWrapper that renders staff member name
535
+ const staffMemberItemWrapper = ({ item: member, children: itemChildren, }) => {
536
+ return (_jsxs(React.Fragment, { children: [_jsx("span", { "data-testid": TestIds.serviceStaffMemberRepeater, children: member.name }), itemChildren] }, member.id));
537
+ };
538
+ // When asChild is true, provide custom render props
539
+ if (asChild && typeof children === 'function') {
540
+ return (_jsx(GenericList.Repeater, { ref: ref, asChild: true, itemWrapper: staffMemberItemWrapper, children: (genericProps) => {
541
+ const renderProps = {
542
+ items: genericProps.items,
543
+ itemWrapper: staffMemberItemWrapper,
544
+ };
545
+ return children(renderProps);
546
+ } }));
547
+ }
548
+ return (_jsx(GenericList.Repeater, { ref: ref, itemWrapper: staffMemberItemWrapper, children: children }));
549
+ });
550
+ StaffMemberRepeater.displayName = 'Service.StaffMembers.StaffMemberRepeater';
551
+ /**
552
+ * Displays the service main media image using WixMediaImage.
553
+ *
554
+ * @component
555
+ * @example
556
+ * ```tsx
557
+ * // Default usage
558
+ * <Service.MainMedia className="w-full h-64 object-cover rounded-lg" />
559
+ * ```
560
+ *
561
+ * @example
562
+ * ```tsx
563
+ * // asChild with primitive img element
564
+ * <Service.MainMedia asChild>
565
+ * <img className="w-full h-64 object-cover shadow-lg" />
566
+ * </Service.MainMedia>
567
+ * ```
568
+ *
569
+ * @example
570
+ * ```tsx
571
+ * // asChild with custom component accessing media data
572
+ * <Service.MainMedia asChild>
573
+ * {React.forwardRef(({media, ...props}, ref) => (
574
+ * <div ref={ref} {...props} className="relative aspect-video">
575
+ * <WixMediaImage media={media} className="w-full h-full object-cover" />
576
+ * <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/50 p-4">
577
+ * <Service.Name className="text-primary-foreground" />
578
+ * </div>
579
+ * </div>
580
+ * ))}
581
+ * </Service.MainMedia>
582
+ * ```
583
+ */
584
+ export const MainMedia = React.forwardRef((props, ref) => {
585
+ const { asChild, children, className } = props;
586
+ return (_jsx(CoreService.Info, { children: ({ mainMedia }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceMainMedia, customElement: children, customElementProps: { media: mainMedia }, children: mainMedia?.image ? _jsx(WixMediaImage, { media: mainMedia }) : null })) }));
587
+ });
588
+ MainMedia.displayName = 'Service.MainMedia';
589
+ /**
590
+ * Displays the service cover media image using WixMediaImage.
591
+ * The cover media is typically used as a hero/banner image.
592
+ *
593
+ * @component
594
+ * @example
595
+ * ```tsx
596
+ * // Default usage
597
+ * <Service.CoverMedia className="w-full h-96 object-cover" />
598
+ * ```
599
+ *
600
+ * @example
601
+ * ```tsx
602
+ * // asChild as a background image container
603
+ * <Service.CoverMedia asChild>
604
+ * <div className="w-full h-96 bg-cover bg-center" />
605
+ * </Service.CoverMedia>
606
+ * ```
607
+ *
608
+ * @example
609
+ * ```tsx
610
+ * // asChild with custom component accessing media data
611
+ * <Service.CoverMedia asChild>
612
+ * {React.forwardRef(({media, ...props}, ref) => (
613
+ * <header ref={ref} {...props} className="relative h-96">
614
+ * <WixMediaImage media={media} className="w-full h-full object-cover" />
615
+ * <div className="absolute inset-0 bg-black/40 flex items-center justify-center">
616
+ * <h1 className="text-4xl font-bold text-primary-foreground">
617
+ * <Service.Name />
618
+ * </h1>
619
+ * </div>
620
+ * </header>
621
+ * ))}
622
+ * </Service.CoverMedia>
623
+ * ```
624
+ */
625
+ export const CoverMedia = React.forwardRef((props, ref) => {
626
+ const { asChild, children, className } = props;
627
+ return (_jsx(CoreService.Info, { children: ({ coverMedia }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceCoverMedia, customElement: children, customElementProps: { media: coverMedia }, children: coverMedia?.image ? _jsx(WixMediaImage, { media: coverMedia }) : null })) }));
628
+ });
629
+ CoverMedia.displayName = 'Service.CoverMedia';
630
+ /**
631
+ * Container for service media items gallery.
632
+ * Renders a MediaGallery.Root with the service media items.
633
+ * Supports autoPlay and infinite scroll options for carousel behavior.
634
+ *
635
+ * @component
636
+ * @example
637
+ * ```tsx
638
+ * // Basic usage with MediaGallery components
639
+ * <Service.MediaItems>
640
+ * <MediaGallery.Thumbnails>
641
+ * <MediaGallery.ThumbnailRepeater>
642
+ * {({ mediaItem, isActive }) => (
643
+ * <img
644
+ * src={mediaItem.url}
645
+ * alt={mediaItem.altText}
646
+ * className={isActive ? 'active' : ''}
647
+ * />
648
+ * )}
649
+ * </MediaGallery.ThumbnailRepeater>
650
+ * </MediaGallery.Thumbnails>
651
+ * </Service.MediaItems>
652
+ *
653
+ * // With autoPlay carousel
654
+ * <Service.MediaItems
655
+ * autoPlay={{ direction: 'forward', intervalMs: 3000 }}
656
+ * infinite
657
+ * >
658
+ * <MediaGallery.MainImage />
659
+ * <MediaGallery.Actions.Previous />
660
+ * <MediaGallery.Actions.Next />
661
+ * </Service.MediaItems>
662
+ *
663
+ * // Full gallery with thumbnails and navigation
664
+ * <Service.MediaItems infinite>
665
+ * <div className="gallery-container">
666
+ * <MediaGallery.MainImage className="main-image" />
667
+ * <div className="controls">
668
+ * <MediaGallery.Actions.Previous>Prev</MediaGallery.Actions.Previous>
669
+ * <MediaGallery.Actions.Next>Next</MediaGallery.Actions.Next>
670
+ * </div>
671
+ * <MediaGallery.Thumbnails className="thumbnails" />
672
+ * </div>
673
+ * </Service.MediaItems>
674
+ * ```
675
+ */
676
+ export const MediaItems = React.forwardRef((props, ref) => {
677
+ const { children, infinite, autoPlay } = props;
678
+ return (_jsx(CoreService.Info, { children: ({ mediaItems }) => {
679
+ if (!mediaItems || mediaItems.length === 0)
680
+ return null;
681
+ const mediaGalleryServiceConfig = {
682
+ media: mediaItems,
683
+ infinite,
684
+ autoPlay,
685
+ };
686
+ const attributes = {
687
+ 'data-testid': TestIds.serviceMediaItems,
688
+ };
689
+ return (_jsx("div", { ...attributes, ref: ref, children: _jsx(MediaGallery.Root, { mediaGalleryServiceConfig: mediaGalleryServiceConfig, children: children }) }));
690
+ } }));
691
+ });
692
+ MediaItems.displayName = 'Service.MediaItems';
693
+ /**
694
+ * Provides direct access to service object. Should be used only in rare cases.
695
+ *
696
+ * @component
697
+ * @example
698
+ * ```tsx
699
+ * <Service.Raw>
700
+ * {({ service, isLoading, error }) => (
701
+ * <div>
702
+ * {isLoading && <div>Loading...</div>}
703
+ * {error && <div>Error: {error}</div>}
704
+ * {service && <pre>{JSON.stringify(service, null, 2)}</pre>}
705
+ * </div>
706
+ * )}
707
+ * </Service.Raw>
708
+ * ```
709
+ */
710
+ export const Raw = React.forwardRef((props, ref) => {
711
+ const { asChild, children, className } = props;
712
+ return (_jsx(CoreService.Service, { children: ({ service, isLoading, error }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceRaw, customElement: children, customElementProps: { service, isLoading, error }, children: _jsxs("div", { children: [isLoading && _jsx("div", { children: "Loading..." }), error && _jsxs("div", { children: ["Error: ", error] }), service && _jsx("pre", { children: JSON.stringify(service, null, 2) })] }) })) }));
713
+ });
714
+ Raw.displayName = 'Service.Raw';
91
715
  /**
92
716
  * Actions namespace with individual action components
93
717
  */
@@ -107,25 +731,36 @@ export var Actions;
107
731
  * <Button variant="primary">Select Service</Button>
108
732
  * </Service.Actions.Select>
109
733
  *
110
- * // With onClicked callback for navigation
111
- * <Service.Actions.Select
112
- * asChild
113
- * onClicked={(event, service) => {
114
- * router.push(`/booking/time-slots/${service._id}`);
115
- * }}
116
- * >
117
- * <Button variant="primary">Select Service</Button>
734
+ * // Using render prop pattern to access bookable state
735
+ * <Service.Actions.Select>
736
+ * {({ bookable, selected, onClick }) => (
737
+ * <button onClick={onClick} disabled={!bookable}>
738
+ * {selected ? 'Selected' : 'Select Service'}
739
+ * </button>
740
+ * )}
118
741
  * </Service.Actions.Select>
119
742
  * ```
120
743
  */
121
744
  Actions.Select = React.forwardRef((props, ref) => {
122
- const { asChild, children, className, label = 'Select Service', onClicked, } = props;
123
- return (_jsx(CoreService.Actions, { children: ({ select, service }) => {
124
- const handleClick = (event) => {
125
- select();
126
- onClicked?.(event, service);
127
- };
128
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceActionSelect, customElement: children, customElementProps: { onClick: handleClick }, children: _jsx("button", { onClick: handleClick, children: label }) }));
745
+ const { asChild, children, className, label = 'Select Service' } = props;
746
+ return (_jsx(CoreService.Actions, { children: ({ select, rawService, bookable, selected, requiresAvailability, }) => {
747
+ // If children is a function, call it with bookable, selected, requiresAvailability and other props
748
+ if (typeof children === 'function') {
749
+ return children({
750
+ bookable,
751
+ selected,
752
+ requiresAvailability,
753
+ onClick: select,
754
+ service: rawService,
755
+ });
756
+ }
757
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceActionSelect, "data-selected": selected, "data-bookable": bookable, "data-requires-availability": requiresAvailability, customElement: children, customElementProps: {
758
+ onClick: select,
759
+ disabled: !bookable,
760
+ selected,
761
+ bookable,
762
+ requiresAvailability,
763
+ }, children: _jsx("button", { onClick: select, disabled: !bookable, "data-selected": selected, "data-bookable": bookable, "data-requires-availability": requiresAvailability, children: label }) }));
129
764
  } }));
130
765
  });
131
766
  Actions.Select.displayName = 'Service.Actions.Select';
@@ -143,23 +778,38 @@ export var Actions;
143
778
  * <Button variant="secondary">Add Service</Button>
144
779
  * </Service.Actions.Add>
145
780
  *
146
- * // With onClicked callback for tracking
147
- * <Service.Actions.Add
148
- * label="Add to Cart"
149
- * onClicked={(event, service) => {
150
- * analytics.track('service_added', { serviceId: service._id });
151
- * }}
152
- * />
781
+ * // Using render prop pattern to access bookable state
782
+ * <Service.Actions.Add>
783
+ * {({ bookable, selected, onClick }) => (
784
+ * <button onClick={onClick} disabled={!bookable}>
785
+ * {selected ? 'Added' : 'Add to Booking'}
786
+ * </button>
787
+ * )}
788
+ * </Service.Actions.Add>
153
789
  * ```
154
790
  */
155
791
  Actions.Add = React.forwardRef((props, ref) => {
156
- const { asChild, children, className, label = 'Add Service', onClicked, } = props;
157
- return (_jsx(CoreService.Actions, { children: ({ add, service }) => {
158
- const handleClick = (event) => {
159
- add();
160
- onClicked?.(event, service);
161
- };
162
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceActionAdd, customElement: children, customElementProps: { onClick: handleClick }, children: _jsx("button", { onClick: handleClick, children: label }) }));
792
+ const { asChild, children, className, label = 'Add Service' } = props;
793
+ return (_jsx(CoreService.Actions, { children: ({ add, rawService, bookable, selected, requiresAvailability }) => {
794
+ // If children is a function, call it with bookable, selected, requiresAvailability and other props
795
+ if (typeof children === 'function') {
796
+ return children({
797
+ bookable,
798
+ selected,
799
+ requiresAvailability,
800
+ onClick: add,
801
+ service: rawService,
802
+ });
803
+ }
804
+ // Disabled when already selected (can't add again) or not bookable
805
+ const isDisabled = selected || !bookable;
806
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceActionAdd, "data-selected": selected, "data-bookable": bookable, "data-requires-availability": requiresAvailability, customElement: children, customElementProps: {
807
+ onClick: add,
808
+ disabled: isDisabled,
809
+ selected,
810
+ bookable,
811
+ requiresAvailability,
812
+ }, children: _jsx("button", { onClick: add, disabled: isDisabled, "data-selected": selected, "data-bookable": bookable, "data-requires-availability": requiresAvailability, children: label }) }));
163
813
  } }));
164
814
  });
165
815
  Actions.Add.displayName = 'Service.Actions.Add';
@@ -177,26 +827,389 @@ export var Actions;
177
827
  * <Button variant="destructive">Remove Service</Button>
178
828
  * </Service.Actions.Remove>
179
829
  *
180
- * // With onClicked callback for notifications
181
- * <Service.Actions.Remove
182
- * asChild
183
- * onClicked={(event, service) => {
184
- * toast.success(`${service.name} removed from selection`);
185
- * }}
186
- * >
187
- * <IconButton icon="trash" />
830
+ * // Using render prop pattern to access bookable state
831
+ * <Service.Actions.Remove>
832
+ * {({ bookable, selected, onClick }) => (
833
+ * <button onClick={onClick} disabled={!bookable}>
834
+ * Remove Service
835
+ * </button>
836
+ * )}
188
837
  * </Service.Actions.Remove>
189
838
  * ```
190
839
  */
191
840
  Actions.Remove = React.forwardRef((props, ref) => {
192
- const { asChild, children, className, label = 'Remove Service', onClicked, } = props;
193
- return (_jsx(CoreService.Actions, { children: ({ remove, service }) => {
194
- const handleClick = (event) => {
195
- remove();
196
- onClicked?.(event, service);
197
- };
198
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceActionRemove, customElement: children, customElementProps: { onClick: handleClick }, children: _jsx("button", { onClick: handleClick, children: label }) }));
841
+ const { asChild, children, className, label = 'Remove Service' } = props;
842
+ return (_jsx(CoreService.Actions, { children: ({ remove, rawService, bookable, selected, requiresAvailability, }) => {
843
+ // If children is a function, call it with bookable, selected, requiresAvailability and other props
844
+ if (typeof children === 'function') {
845
+ return children({
846
+ bookable,
847
+ selected,
848
+ requiresAvailability,
849
+ onClick: remove,
850
+ service: rawService,
851
+ });
852
+ }
853
+ // Disabled when not selected (nothing to remove) or not bookable
854
+ const isDisabled = !selected || !bookable;
855
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceActionRemove, "data-selected": selected, "data-bookable": bookable, "data-requires-availability": requiresAvailability, customElement: children, customElementProps: {
856
+ onClick: remove,
857
+ disabled: isDisabled,
858
+ selected,
859
+ bookable,
860
+ requiresAvailability,
861
+ }, children: _jsx("button", { onClick: remove, disabled: isDisabled, "data-selected": selected, "data-bookable": bookable, "data-requires-availability": requiresAvailability, children: label }) }));
199
862
  } }));
200
863
  });
201
864
  Actions.Remove.displayName = 'Service.Actions.Remove';
202
865
  })(Actions || (Actions = {}));
866
+ /**
867
+ * Displays latestCancellationInMinutes as a raw number.
868
+ * Headless component - contains zero formatting logic.
869
+ *
870
+ * Maps to: bookingPolicy.cancellationPolicy.latestCancellationInMinutes
871
+ *
872
+ * @component
873
+ * @example
874
+ * ```tsx
875
+ * // Default usage - displays raw number only (or null if disabled/undefined)
876
+ * <Service.Policy.LatestCancellationInMinutes className="text-foreground" />
877
+ * // Renders: <span>30</span> or null
878
+ * ```
879
+ *
880
+ * @example
881
+ * ```tsx
882
+ * // Consumer adds formatting logic via asChild
883
+ * <Service.Policy.LatestCancellationInMinutes asChild>
884
+ * {({ cancellationPolicyEnabled, limitLatestCancellationEnabled, latestCancellationInMinutes }) => (
885
+ * cancellationPolicyEnabled ? (
886
+ * <div className="policy-card">
887
+ * {limitLatestCancellationEnabled
888
+ * ? `Cancel up to ${latestCancellationInMinutes} minutes before`
889
+ * : 'Free cancellation anytime'
890
+ * }
891
+ * </div>
892
+ * ) : null
893
+ * )}
894
+ * </Service.Policy.LatestCancellationInMinutes>
895
+ * ```
896
+ */
897
+ export const PolicyLatestCancellationInMinutes = React.forwardRef((props, ref) => {
898
+ const { asChild, children, className } = props;
899
+ return (_jsx(CoreService.Policy, { children: ({ cancellationPolicyEnabled, limitLatestCancellationEnabled, latestCancellationInMinutes, }) => {
900
+ // Prepare data for asChild pattern - ALWAYS pass to consumer
901
+ const childProps = {
902
+ cancellationPolicyEnabled,
903
+ limitLatestCancellationEnabled,
904
+ latestCancellationInMinutes,
905
+ };
906
+ // ALWAYS return AsChildSlot (never return null early)
907
+ // This allows consumer to override null behavior via asChild
908
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.servicePolicyLatestCancellationInMinutes, customElement: children, customElementProps: childProps, children: cancellationPolicyEnabled &&
909
+ latestCancellationInMinutes !== undefined ? (_jsx("span", { children: latestCancellationInMinutes })) : null }));
910
+ } }));
911
+ });
912
+ PolicyLatestCancellationInMinutes.displayName =
913
+ 'Service.Policy.LatestCancellationInMinutes';
914
+ /**
915
+ * Displays latestBookingInMinutes as a raw number.
916
+ * Headless component - contains zero formatting logic.
917
+ *
918
+ * Maps to: bookingPolicy.limitLateBookingPolicy.latestBookingInMinutes
919
+ *
920
+ * @component
921
+ * @example
922
+ * ```tsx
923
+ * // Default usage
924
+ * <Service.Policy.LatestBookingInMinutes className="text-foreground" />
925
+ * ```
926
+ */
927
+ export const PolicyLatestBookingInMinutes = React.forwardRef((props, ref) => {
928
+ const { asChild, children, className } = props;
929
+ return (_jsx(CoreService.Policy, { children: ({ latestBookingPolicyEnabled, latestBookingInMinutes }) => {
930
+ const childProps = {
931
+ latestBookingPolicyEnabled,
932
+ latestBookingInMinutes,
933
+ };
934
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.servicePolicyLatestBookingInMinutes, customElement: children, customElementProps: childProps, children: latestBookingPolicyEnabled &&
935
+ latestBookingInMinutes !== undefined ? (_jsx("span", { children: latestBookingInMinutes })) : null }));
936
+ } }));
937
+ });
938
+ PolicyLatestBookingInMinutes.displayName =
939
+ 'Service.Policy.LatestBookingInMinutes';
940
+ /**
941
+ * Displays earliestBookingInMinutes as a raw number.
942
+ * Headless component - contains zero formatting logic.
943
+ *
944
+ * Maps to: bookingPolicy.limitEarlyBookingPolicy.earliestBookingInMinutes
945
+ *
946
+ * @component
947
+ * @example
948
+ * ```tsx
949
+ * // Default usage
950
+ * <Service.Policy.EarliestBookingInMinutes className="text-foreground" />
951
+ * ```
952
+ */
953
+ export const PolicyEarliestBookingInMinutes = React.forwardRef((props, ref) => {
954
+ const { asChild, children, className } = props;
955
+ return (_jsx(CoreService.Policy, { children: ({ earliestBookingPolicyEnabled, earliestBookingInMinutes }) => {
956
+ const childProps = {
957
+ earliestBookingPolicyEnabled,
958
+ earliestBookingInMinutes,
959
+ };
960
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.servicePolicyEarliestBookingInMinutes, customElement: children, customElementProps: childProps, children: earliestBookingPolicyEnabled &&
961
+ earliestBookingInMinutes !== undefined ? (_jsx("span", { children: earliestBookingInMinutes })) : null }));
962
+ } }));
963
+ });
964
+ PolicyEarliestBookingInMinutes.displayName =
965
+ 'Service.Policy.EarliestBookingInMinutes';
966
+ /**
967
+ * Displays latestRescheduleInMinutes as a raw number.
968
+ * Headless component - contains zero formatting logic.
969
+ *
970
+ * Maps to: bookingPolicy.reschedulePolicy.latestRescheduleInMinutes
971
+ *
972
+ * @component
973
+ * @example
974
+ * ```tsx
975
+ * // Default usage
976
+ * <Service.Policy.LatestRescheduleInMinutes className="text-foreground" />
977
+ * ```
978
+ */
979
+ export const PolicyLatestRescheduleInMinutes = React.forwardRef((props, ref) => {
980
+ const { asChild, children, className } = props;
981
+ return (_jsx(CoreService.Policy, { children: ({ reschedulePolicyEnabled, limitLatestRescheduleEnabled, latestRescheduleInMinutes, }) => {
982
+ const childProps = {
983
+ reschedulePolicyEnabled,
984
+ limitLatestRescheduleEnabled,
985
+ latestRescheduleInMinutes,
986
+ };
987
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.servicePolicyLatestRescheduleInMinutes, customElement: children, customElementProps: childProps, children: reschedulePolicyEnabled &&
988
+ latestRescheduleInMinutes !== undefined ? (_jsx("span", { children: latestRescheduleInMinutes })) : null }));
989
+ } }));
990
+ });
991
+ PolicyLatestRescheduleInMinutes.displayName =
992
+ 'Service.Policy.LatestRescheduleInMinutes';
993
+ /**
994
+ * Displays waitlist capacity as a raw number.
995
+ * Headless component - contains zero formatting logic.
996
+ *
997
+ * Maps to: bookingPolicy.waitlistPolicy.capacity
998
+ *
999
+ * @component
1000
+ * @example
1001
+ * ```tsx
1002
+ * // Default usage
1003
+ * <Service.Policy.WaitlistCapacity className="text-foreground" />
1004
+ * ```
1005
+ */
1006
+ export const PolicyWaitlistCapacity = React.forwardRef((props, ref) => {
1007
+ const { asChild, children, className } = props;
1008
+ return (_jsx(CoreService.Policy, { children: ({ waitlistPolicyEnabled, waitlistCapacity }) => {
1009
+ const childProps = {
1010
+ waitlistPolicyEnabled,
1011
+ waitlistCapacity,
1012
+ };
1013
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.servicePolicyWaitlistCapacity, customElement: children, customElementProps: childProps, children: waitlistPolicyEnabled && waitlistCapacity !== undefined ? (_jsx("span", { children: waitlistCapacity })) : null }));
1014
+ } }));
1015
+ });
1016
+ PolicyWaitlistCapacity.displayName = 'Service.Policy.WaitlistCapacity';
1017
+ /**
1018
+ * Displays whether booking after start is enabled.
1019
+ * Headless component - contains zero formatting logic.
1020
+ *
1021
+ * Maps to: bookingPolicy.bookAfterStartPolicy.enabled
1022
+ *
1023
+ * @component
1024
+ * @example
1025
+ * ```tsx
1026
+ * // Default usage
1027
+ * <Service.Policy.BookAfterStartEnabled className="text-foreground" />
1028
+ * ```
1029
+ */
1030
+ export const PolicyBookAfterStartEnabled = React.forwardRef((props, ref) => {
1031
+ const { asChild, children, className } = props;
1032
+ return (_jsx(CoreService.Policy, { children: ({ bookAfterStartEnabled }) => {
1033
+ const childProps = {
1034
+ bookAfterStartEnabled,
1035
+ };
1036
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.servicePolicyBookAfterStartEnabled, customElement: children, customElementProps: childProps, children: bookAfterStartEnabled ? (_jsx("span", { children: bookAfterStartEnabled ? 'true' : 'false' })) : null }));
1037
+ } }));
1038
+ });
1039
+ PolicyBookAfterStartEnabled.displayName =
1040
+ 'Service.Policy.BookAfterStartEnabled';
1041
+ /**
1042
+ * Displays maximum participants per booking as a raw number.
1043
+ * Headless component - contains zero formatting logic.
1044
+ *
1045
+ * Maps to: bookingPolicy.participantsPolicy.maxParticipantsPerBooking
1046
+ *
1047
+ * @component
1048
+ * @example
1049
+ * ```tsx
1050
+ * // Default usage
1051
+ * <Service.Policy.MaxParticipantsPerBooking className="text-foreground" />
1052
+ * ```
1053
+ */
1054
+ export const PolicyMaxParticipantsPerBooking = React.forwardRef((props, ref) => {
1055
+ const { asChild, children, className } = props;
1056
+ return (_jsx(CoreService.Policy, { children: ({ maxParticipantsPerBooking }) => {
1057
+ const childProps = {
1058
+ maxParticipantsPerBooking,
1059
+ };
1060
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.servicePolicyMaxParticipantsPerBooking, customElement: children, customElementProps: childProps, children: maxParticipantsPerBooking !== undefined ? (_jsx("span", { children: maxParticipantsPerBooking })) : null }));
1061
+ } }));
1062
+ });
1063
+ PolicyMaxParticipantsPerBooking.displayName =
1064
+ 'Service.Policy.MaxParticipantsPerBooking';
1065
+ /**
1066
+ * Policy namespace containing all policy sub-components
1067
+ */
1068
+ export var Policy;
1069
+ (function (Policy) {
1070
+ Policy.LatestCancellationInMinutes = PolicyLatestCancellationInMinutes;
1071
+ Policy.LatestBookingInMinutes = PolicyLatestBookingInMinutes;
1072
+ Policy.EarliestBookingInMinutes = PolicyEarliestBookingInMinutes;
1073
+ Policy.LatestRescheduleInMinutes = PolicyLatestRescheduleInMinutes;
1074
+ Policy.WaitlistCapacity = PolicyWaitlistCapacity;
1075
+ Policy.BookAfterStartEnabled = PolicyBookAfterStartEnabled;
1076
+ Policy.MaxParticipantsPerBooking = PolicyMaxParticipantsPerBooking;
1077
+ })(Policy || (Policy = {}));
1078
+ /**
1079
+ * Displays the first session start date as a raw Date value.
1080
+ * Headless component - contains zero formatting logic.
1081
+ *
1082
+ * @component
1083
+ * @example
1084
+ * ```tsx
1085
+ * // Default usage - displays ISO string
1086
+ * <Service.Schedule.FirstSessionStart className="text-foreground" />
1087
+ *
1088
+ * // With custom formatting
1089
+ * <Service.Schedule.FirstSessionStart asChild>
1090
+ * {({ firstSessionStart }) => (
1091
+ * firstSessionStart ? (
1092
+ * <span>Starts: {formatDate(firstSessionStart)}</span>
1093
+ * ) : null
1094
+ * )}
1095
+ * </Service.Schedule.FirstSessionStart>
1096
+ * ```
1097
+ */
1098
+ export const ScheduleFirstSessionStart = React.forwardRef((props, ref) => {
1099
+ const { asChild, children, className } = props;
1100
+ return (_jsx(CoreService.Schedule, { children: ({ schedule }) => {
1101
+ const firstSessionStart = schedule?.firstSessionStart;
1102
+ const childProps = {
1103
+ firstSessionStart,
1104
+ };
1105
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceScheduleFirstSessionStart, customElement: children, customElementProps: childProps, children: firstSessionStart ? (_jsx("span", { children: firstSessionStart.toISOString() })) : null }));
1106
+ } }));
1107
+ });
1108
+ ScheduleFirstSessionStart.displayName = 'Service.Schedule.FirstSessionStart';
1109
+ /**
1110
+ * Displays the last session end date as a raw Date value.
1111
+ * Headless component - contains zero formatting logic.
1112
+ *
1113
+ * @component
1114
+ * @example
1115
+ * ```tsx
1116
+ * // Default usage - displays ISO string
1117
+ * <Service.Schedule.LastSessionEnd className="text-foreground" />
1118
+ *
1119
+ * // With custom formatting
1120
+ * <Service.Schedule.LastSessionEnd asChild>
1121
+ * {({ lastSessionEnd }) => (
1122
+ * lastSessionEnd ? (
1123
+ * <span>Ends: {formatDate(lastSessionEnd)}</span>
1124
+ * ) : null
1125
+ * )}
1126
+ * </Service.Schedule.LastSessionEnd>
1127
+ * ```
1128
+ */
1129
+ export const ScheduleLastSessionEnd = React.forwardRef((props, ref) => {
1130
+ const { asChild, children, className } = props;
1131
+ return (_jsx(CoreService.Schedule, { children: ({ schedule }) => {
1132
+ const lastSessionEnd = schedule?.lastSessionEnd;
1133
+ const childProps = {
1134
+ lastSessionEnd,
1135
+ };
1136
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceScheduleLastSessionEnd, customElement: children, customElementProps: childProps, children: lastSessionEnd ? (_jsx("span", { children: lastSessionEnd.toISOString() })) : null }));
1137
+ } }));
1138
+ });
1139
+ ScheduleLastSessionEnd.displayName = 'Service.Schedule.LastSessionEnd';
1140
+ /**
1141
+ * Displays the session duration in minutes as a raw number.
1142
+ * Headless component - contains zero formatting logic.
1143
+ *
1144
+ * @component
1145
+ * @example
1146
+ * ```tsx
1147
+ * // Default usage - displays raw minutes
1148
+ * <Service.Schedule.SessionDuration className="text-foreground" />
1149
+ *
1150
+ * // With custom formatting
1151
+ * <Service.Schedule.SessionDuration asChild>
1152
+ * {({ sessionDuration }) => (
1153
+ * sessionDuration ? (
1154
+ * <span>{formatDuration(sessionDuration)}</span>
1155
+ * ) : null
1156
+ * )}
1157
+ * </Service.Schedule.SessionDuration>
1158
+ * ```
1159
+ */
1160
+ export const ScheduleSessionDuration = React.forwardRef((props, ref) => {
1161
+ const { asChild, children, className } = props;
1162
+ return (_jsx(CoreService.Schedule, { children: ({ schedule }) => {
1163
+ const sessionDuration = schedule?.availabilityConstraints?.sessionDurations?.[0];
1164
+ const childProps = {
1165
+ sessionDuration,
1166
+ };
1167
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceScheduleSessionDuration, customElement: children, customElementProps: childProps, children: sessionDuration !== undefined ? (_jsx("span", { children: sessionDuration })) : null }));
1168
+ } }));
1169
+ });
1170
+ ScheduleSessionDuration.displayName = 'Service.Schedule.SessionDuration';
1171
+ /**
1172
+ * Schedule namespace containing all schedule sub-components
1173
+ */
1174
+ export var Schedule;
1175
+ (function (Schedule) {
1176
+ Schedule.FirstSessionStart = ScheduleFirstSessionStart;
1177
+ Schedule.LastSessionEnd = ScheduleLastSessionEnd;
1178
+ Schedule.SessionDuration = ScheduleSessionDuration;
1179
+ })(Schedule || (Schedule = {}));
1180
+ /**
1181
+ * Displays the service default capacity with customizable rendering.
1182
+ * Only renders for CLASS or COURSE service types.
1183
+ *
1184
+ * @component
1185
+ * @example
1186
+ * ```tsx
1187
+ * // Default usage
1188
+ * <Service.DefaultCapacity className="text-sm" />
1189
+ *
1190
+ * // asChild with custom component
1191
+ * <Service.DefaultCapacity asChild>
1192
+ * {({ defaultCapacity, isClassOrCourse }) => isClassOrCourse ? (
1193
+ * <span>Max participants: {defaultCapacity}</span>
1194
+ * ) : null}
1195
+ * </Service.DefaultCapacity>
1196
+ * ```
1197
+ */
1198
+ export const DefaultCapacity = React.forwardRef((props, ref) => {
1199
+ const { asChild, children, className } = props;
1200
+ return (_jsx(CoreService.Info, { children: ({ type, defaultCapacity }) => {
1201
+ const isClassOrCourse = type === 'CLASS' || type === 'COURSE';
1202
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.serviceDefaultCapacity, customElement: children, customElementProps: { defaultCapacity, isClassOrCourse }, children: isClassOrCourse && defaultCapacity ? (_jsxs("span", { children: ["Max ", defaultCapacity, " participants"] })) : null }));
1203
+ } }));
1204
+ });
1205
+ DefaultCapacity.displayName = 'Service.DefaultCapacity';
1206
+ // Create Locations with nested components
1207
+ export const Locations = Object.assign(LocationsBase, {
1208
+ List: LocationsList,
1209
+ LocationRepeater,
1210
+ });
1211
+ // Create StaffMembers with nested components
1212
+ export const StaffMembers = Object.assign(StaffMembersBase, {
1213
+ List: StaffMembersList,
1214
+ StaffMemberRepeater,
1215
+ });