@wix/headless-bookings 0.0.9 → 0.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 (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 +4 -4
  6. package/cjs/dist/react/core/booking/Booking.js +3 -3
  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 +50 -33
  22. package/cjs/dist/services/booking/booking.js +72 -38
  23. package/cjs/dist/services/index.d.ts +3 -2
  24. package/cjs/dist/services/index.js +3 -2
  25. package/cjs/dist/services/service/service.d.ts +71 -18
  26. package/cjs/dist/services/service/service.js +187 -52
  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 +4 -4
  38. package/dist/react/core/booking/Booking.js +3 -3
  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 +50 -33
  54. package/dist/services/booking/booking.js +72 -38
  55. package/dist/services/index.d.ts +3 -2
  56. package/dist/services/index.js +3 -2
  57. package/dist/services/service/service.d.ts +71 -18
  58. package/dist/services/service/service.js +187 -52
  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 +3 -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
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Core Location Component - Parses service location data
3
+ * Handles business, custom, and customer location types
4
+ * Converts location data to Address component format
5
+ */
6
+ import React from 'react';
7
+ import { services } from '@wix/bookings';
8
+ import type { AddressData as AddressDataType } from '@wix/headless-components/react';
9
+ /**
10
+ * Re-export AddressData type from Address component for convenience
11
+ */
12
+ export type AddressData = AddressDataType;
13
+ /**
14
+ * Location render props passed to children
15
+ */
16
+ export interface LocationRenderProps {
17
+ locationType: services.LocationType;
18
+ address: AddressData | null;
19
+ }
20
+ /**
21
+ * Props for the core Location component
22
+ */
23
+ export interface LocationProps {
24
+ location: NonNullable<services.Service['locations']>[number];
25
+ children: (props: LocationRenderProps) => React.ReactNode;
26
+ }
27
+ /**
28
+ * Core Location component that parses service location data
29
+ * and provides location type and address data to children
30
+ *
31
+ * @component
32
+ * @example
33
+ * ```tsx
34
+ * <CoreLocation location={serviceLocation}>
35
+ * {({ locationType, address }) => (
36
+ * <div>
37
+ * <span>Type: {locationType}</span>
38
+ * {address && <Address.Root address={address}><Address.Formatted /></Address.Root>}
39
+ * </div>
40
+ * )}
41
+ * </CoreLocation>
42
+ * ```
43
+ */
44
+ export declare function Location(props: LocationProps): React.ReactNode;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Core Location Component - Parses service location data
3
+ * Handles business, custom, and customer location types
4
+ * Converts location data to Address component format
5
+ */
6
+ import { services } from '@wix/bookings';
7
+ /**
8
+ * Parses location data into Address component format
9
+ */
10
+ function parseLocationAddress(location, locationType) {
11
+ // Handle customer location specially
12
+ if (locationType === services.LocationType.CUSTOMER) {
13
+ // For customer location, return an AddressData object with Customer's Place
14
+ return {
15
+ address: {
16
+ addressLine: "Customer's Place",
17
+ formattedAddress: "Customer's Place",
18
+ city: '',
19
+ subdivision: '',
20
+ postalCode: '',
21
+ country: '',
22
+ },
23
+ };
24
+ }
25
+ // Get the common address from the appropriate location type
26
+ const commonAddress = location.calculatedAddress ||
27
+ location.business?.address ||
28
+ location.custom?.address;
29
+ if (!commonAddress) {
30
+ return null;
31
+ }
32
+ // Convert CommonAddress to AddressData format (compatible with Address.Root from @wix/headless-components)
33
+ return {
34
+ address: {
35
+ addressLine: commonAddress.streetAddress?.name ||
36
+ commonAddress.addressLine ||
37
+ commonAddress.addressLine1 ||
38
+ '',
39
+ city: commonAddress.city || '',
40
+ subdivision: commonAddress.subdivision || '',
41
+ postalCode: commonAddress.postalCode || '',
42
+ country: commonAddress.country || '',
43
+ streetAddress: commonAddress.streetAddress
44
+ ? {
45
+ number: commonAddress.streetAddress.number,
46
+ name: commonAddress.streetAddress.name,
47
+ apt: commonAddress.streetAddress.apt,
48
+ }
49
+ : undefined,
50
+ },
51
+ };
52
+ }
53
+ // ==========================================
54
+ // Component
55
+ // ==========================================
56
+ /**
57
+ * Core Location component that parses service location data
58
+ * and provides location type and address data to children
59
+ *
60
+ * @component
61
+ * @example
62
+ * ```tsx
63
+ * <CoreLocation location={serviceLocation}>
64
+ * {({ locationType, address }) => (
65
+ * <div>
66
+ * <span>Type: {locationType}</span>
67
+ * {address && <Address.Root address={address}><Address.Formatted /></Address.Root>}
68
+ * </div>
69
+ * )}
70
+ * </CoreLocation>
71
+ * ```
72
+ */
73
+ export function Location(props) {
74
+ const { location, children } = props;
75
+ const locationType = location.type;
76
+ const address = parseLocationAddress(location, locationType);
77
+ return children({ locationType, address });
78
+ }
@@ -4,25 +4,18 @@
4
4
  */
5
5
  import React from 'react';
6
6
  import { type ServiceServiceConfig } from '../../../services/service/service.js';
7
- import type { services } from '@wix/bookings';
7
+ import { services } from '@wix/bookings';
8
+ import type { MoneyData } from '@wix/headless-components/react';
8
9
  /**
9
10
  * Props for Service.Root component
10
11
  */
11
12
  export interface RootProps {
12
- children: React.ReactNode;
13
+ children: React.ReactNode | ((props: {
14
+ selected: boolean;
15
+ bookable: boolean;
16
+ }) => React.ReactNode);
13
17
  serviceServiceConfig: ServiceServiceConfig;
14
18
  }
15
- /**
16
- * Root component that provides service context.
17
- *
18
- * @component
19
- * @example
20
- * ```tsx
21
- * <Service.Root serviceServiceConfig={{ service }}>
22
- * {children}
23
- * </Service.Root>
24
- * ```
25
- */
26
19
  export declare function Root(props: RootProps): React.ReactNode;
27
20
  /**
28
21
  * Props for Service render prop component
@@ -55,26 +48,101 @@ export interface InfoProps {
55
48
  children: (data: {
56
49
  name: string;
57
50
  description: string;
51
+ tagline?: string;
52
+ duration?: number;
53
+ type?: string;
54
+ category?: {
55
+ name?: string;
56
+ _id?: string;
57
+ };
58
+ locations?: Array<NonNullable<services.Service['locations']>[number]>;
59
+ staff?: Array<{
60
+ name?: string;
61
+ _id?: string;
62
+ }>;
63
+ media?: Array<{
64
+ url?: string;
65
+ _id?: string;
66
+ }>;
67
+ mainMedia?: {
68
+ image?: string;
69
+ };
70
+ coverMedia?: {
71
+ image?: string;
72
+ };
73
+ mediaItems?: Array<{
74
+ image?: string | null;
75
+ altText?: string | null;
76
+ }>;
77
+ price?: {
78
+ amount?: string;
79
+ formattedAmount?: string;
80
+ currency?: string;
81
+ };
82
+ defaultCapacity?: number;
83
+ selected: boolean;
58
84
  }) => React.ReactNode;
59
85
  }
60
86
  /**
61
87
  * Core component that provides access to service catalog info via render props.
62
- * Includes name and description for static catalog display.
88
+ * Includes name, description, tagline, duration, type, category, locations, staff, media, and price.
63
89
  *
64
90
  * @component
65
91
  * @example
66
92
  * ```tsx
67
93
  * <Service.Info>
68
- * {({ name, description }) => (
94
+ * {({ name, description, tagline, duration, type, category, locations, staff, media, price }) => (
69
95
  * <div>
70
96
  * <h1>{name}</h1>
71
97
  * <p>{description}</p>
98
+ * {tagline && <p>{tagline}</p>}
99
+ * {duration && <p>Duration: {duration} minutes</p>}
100
+ * {type && <p>Type: {type}</p>}
101
+ * {category && <p>Category: {category.name}</p>}
72
102
  * </div>
73
103
  * )}
74
104
  * </Service.Info>
75
105
  * ```
76
106
  */
77
107
  export declare function Info(props: InfoProps): React.ReactNode;
108
+ /**
109
+ * Props for Price render prop component
110
+ */
111
+ export interface PriceProps {
112
+ children: (data: {
113
+ servicePayment: services.Payment | undefined;
114
+ rateType: services.Payment['rateType'] | undefined;
115
+ money: MoneyData | null;
116
+ minPrice: MoneyData | null;
117
+ maxPrice: MoneyData | null;
118
+ priceDescription: string | null;
119
+ hasAddOns: boolean;
120
+ hasPricingPlans: boolean;
121
+ }) => React.ReactNode;
122
+ }
123
+ /**
124
+ * Core component that provides access to service price via render props.
125
+ * Returns comprehensive pricing data including money, rate type, and service capabilities.
126
+ *
127
+ * @component
128
+ * @example
129
+ * ```tsx
130
+ * <Service.Price>
131
+ * {({ money, rateType, minPrice, maxPrice, priceDescription, hasAddOns, hasPricingPlans }) => (
132
+ * rateType === 'FIXED' && money ? (
133
+ * <Money money={money} />
134
+ * ) : rateType === 'VARIED' && minPrice && maxPrice ? (
135
+ * <span><Money money={minPrice} /> - <Money money={maxPrice} /></span>
136
+ * ) : rateType === 'CUSTOM' && priceDescription ? (
137
+ * <span>{priceDescription}</span>
138
+ * ) : rateType === 'NO_FEE' ? (
139
+ * <span>Free</span>
140
+ * ) : null
141
+ * )}
142
+ * </Service.Price>
143
+ * ```
144
+ */
145
+ export declare function Price(props: PriceProps): React.ReactNode;
78
146
  /**
79
147
  * Props for Actions render prop component
80
148
  */
@@ -83,7 +151,10 @@ export interface ActionsProps {
83
151
  select: () => void;
84
152
  add: () => void;
85
153
  remove: () => void;
86
- service: services.Service;
154
+ rawService: services.Service;
155
+ bookable: boolean;
156
+ selected: boolean;
157
+ requiresAvailability: boolean;
87
158
  }) => React.ReactNode;
88
159
  }
89
160
  /**
@@ -93,7 +164,7 @@ export interface ActionsProps {
93
164
  * @example
94
165
  * ```tsx
95
166
  * <Service.Actions>
96
- * {({ select, add, remove, service }) => (
167
+ * {({ select, add, remove, rawService, requiresAvailability }) => (
97
168
  * <div>
98
169
  * <button onClick={select}>Select</button>
99
170
  * <button onClick={add}>Add</button>
@@ -104,3 +175,75 @@ export interface ActionsProps {
104
175
  * ```
105
176
  */
106
177
  export declare function Actions(props: ActionsProps): React.ReactNode;
178
+ /**
179
+ * Props for Policy render prop component
180
+ */
181
+ export interface PolicyProps {
182
+ children: (data: {
183
+ bookingPolicy: services.BookingPolicy | null;
184
+ cancellationPolicyEnabled: boolean;
185
+ limitLatestCancellationEnabled: boolean;
186
+ latestCancellationInMinutes: number | undefined;
187
+ latestBookingPolicyEnabled: boolean;
188
+ latestBookingInMinutes: number | undefined;
189
+ earliestBookingPolicyEnabled: boolean;
190
+ earliestBookingInMinutes: number | undefined;
191
+ reschedulePolicyEnabled: boolean;
192
+ limitLatestRescheduleEnabled: boolean;
193
+ latestRescheduleInMinutes: number | undefined;
194
+ waitlistPolicyEnabled: boolean;
195
+ waitlistCapacity: number | undefined;
196
+ bookAfterStartEnabled: boolean;
197
+ maxParticipantsPerBooking: number | undefined;
198
+ }) => React.ReactNode;
199
+ }
200
+ /**
201
+ * Core component that provides access to service booking policy via render props.
202
+ * Computes all enabled flags and values from the booking policy.
203
+ *
204
+ * @component
205
+ * @example
206
+ * ```tsx
207
+ * <Service.Policy>
208
+ * {({ bookingPolicy, cancellationPolicyEnabled, latestCancellationInMinutes }) => (
209
+ * cancellationPolicyEnabled ? (
210
+ * <div>Cancel up to {latestCancellationInMinutes} minutes before</div>
211
+ * ) : null
212
+ * )}
213
+ * </Service.Policy>
214
+ * ```
215
+ */
216
+ export declare function Policy(props: PolicyProps): React.ReactNode;
217
+ /**
218
+ * Props for Schedule render prop component
219
+ */
220
+ export interface ScheduleProps {
221
+ children: (data: {
222
+ schedule: services.Schedule | null;
223
+ isClassOrCourse: boolean;
224
+ }) => React.ReactNode;
225
+ }
226
+ /**
227
+ * Core component that provides access to service schedule via render props.
228
+ * Uses the SDK's Schedule type directly. Only renders for CLASS or COURSE service types.
229
+ *
230
+ * @component
231
+ * @example
232
+ * ```tsx
233
+ * <Service.Schedule>
234
+ * {({ schedule, isClassOrCourse }) => (
235
+ * schedule && isClassOrCourse ? (
236
+ * <div>
237
+ * {schedule.firstSessionStart && (
238
+ * <span>Starts: {new Date(schedule.firstSessionStart).toLocaleDateString()}</span>
239
+ * )}
240
+ * {schedule.lastSessionEnd && (
241
+ * <span>Ends: {new Date(schedule.lastSessionEnd).toLocaleDateString()}</span>
242
+ * )}
243
+ * </div>
244
+ * ) : null
245
+ * )}
246
+ * </Service.Schedule>
247
+ * ```
248
+ */
249
+ export declare function Schedule(props: ScheduleProps): React.ReactNode;
@@ -13,8 +13,17 @@ import { ServiceServiceDefinition, ServiceService, } from '../../../services/ser
13
13
  * </Service.Root>
14
14
  * ```
15
15
  */
16
+ function RootContent({ children, }) {
17
+ const serviceService = useService(ServiceServiceDefinition);
18
+ const selected = serviceService.selectedSignal.get();
19
+ const bookable = serviceService.bookableSignal.get();
20
+ if (typeof children === 'function') {
21
+ return children({ selected, bookable });
22
+ }
23
+ return children;
24
+ }
16
25
  export function Root(props) {
17
- return (_jsx(WixServices, { servicesMap: createServicesMap().addService(ServiceServiceDefinition, ServiceService, props.serviceServiceConfig), children: props.children }));
26
+ return (_jsx(WixServices, { servicesMap: createServicesMap().addService(ServiceServiceDefinition, ServiceService, props.serviceServiceConfig), children: _jsx(RootContent, { children: props.children }) }));
18
27
  }
19
28
  /**
20
29
  * Core component that provides access to full service data via render props.
@@ -31,23 +40,27 @@ export function Root(props) {
31
40
  */
32
41
  export function Service(props) {
33
42
  const serviceService = useService(ServiceServiceDefinition);
34
- const service = serviceService.service.get();
35
- const isLoading = serviceService.isLoading.get();
36
- const error = serviceService.error.get();
43
+ const service = serviceService.serviceSignal.get();
44
+ const isLoading = serviceService.isLoadingSignal.get();
45
+ const error = serviceService.errorSignal.get();
37
46
  return props.children({ service, isLoading, error });
38
47
  }
39
48
  /**
40
49
  * Core component that provides access to service catalog info via render props.
41
- * Includes name and description for static catalog display.
50
+ * Includes name, description, tagline, duration, type, category, locations, staff, media, and price.
42
51
  *
43
52
  * @component
44
53
  * @example
45
54
  * ```tsx
46
55
  * <Service.Info>
47
- * {({ name, description }) => (
56
+ * {({ name, description, tagline, duration, type, category, locations, staff, media, price }) => (
48
57
  * <div>
49
58
  * <h1>{name}</h1>
50
59
  * <p>{description}</p>
60
+ * {tagline && <p>{tagline}</p>}
61
+ * {duration && <p>Duration: {duration} minutes</p>}
62
+ * {type && <p>Type: {type}</p>}
63
+ * {category && <p>Category: {category.name}</p>}
51
64
  * </div>
52
65
  * )}
53
66
  * </Service.Info>
@@ -55,12 +68,99 @@ export function Service(props) {
55
68
  */
56
69
  export function Info(props) {
57
70
  const serviceService = useService(ServiceServiceDefinition);
58
- const service = serviceService.service.get();
71
+ const service = serviceService.serviceSignal.get();
72
+ const selected = serviceService.selectedSignal.get();
73
+ // Extract price from service.payment for Info component
74
+ const payment = service.payment;
75
+ let priceInfo = undefined;
76
+ if (payment?.rateType === 'FIXED' && payment.fixed?.price) {
77
+ const priceValue = payment.fixed.price.value;
78
+ const currency = payment.fixed.price.currency || 'USD';
79
+ priceInfo = {
80
+ amount: priceValue || undefined,
81
+ currency,
82
+ };
83
+ }
84
+ // Extract duration from schedule.availabilityConstraints.sessionDurations
85
+ // Use the first duration if multiple are available
86
+ const duration = service.schedule?.availabilityConstraints?.sessionDurations?.[0];
87
+ // Get staff members from staffMemberDetails.staffMembers
88
+ // (available when using conditionalFields: ['STAFF_MEMBER_DETAILS'] in queryServices)
89
+ const staff = service.staffMembers?.map((member) => ({
90
+ _id: member.staffMemberId,
91
+ name: member.name || undefined,
92
+ })) || [];
93
+ // Convert mainMedia to WixMediaImage format
94
+ // Handle both snake_case (main_media) and camelCase (mainMedia) from API
95
+ // Handle both string and object formats for image
96
+ const serviceMedia = service.media;
97
+ const mainMedia = {
98
+ image: serviceMedia?.mainMedia?.image,
99
+ };
100
+ // Convert coverMedia to WixMediaImage format
101
+ // Handle both snake_case (cover_media) and camelCase (coverMedia) from API
102
+ // Handle both string and object formats for image
103
+ const coverMedia = {
104
+ image: serviceMedia?.coverMedia?.image,
105
+ };
106
+ // Convert media items to MediaGallery format
107
+ // Handle both string and object formats for image
108
+ const mediaItems = service.media?.items?.map((item) => {
109
+ return {
110
+ image: item.image,
111
+ };
112
+ }) || [];
113
+ // Extract default capacity directly from service (convert null to undefined)
114
+ const defaultCapacity = service.defaultCapacity ?? undefined;
59
115
  return props.children({
60
116
  name: service.name || '',
61
117
  description: service.description || '',
118
+ tagline: service.tagLine || undefined,
119
+ duration: duration,
120
+ type: service.type,
121
+ category: service.category
122
+ ? {
123
+ name: service.category.name || undefined,
124
+ _id: service.category._id || undefined,
125
+ }
126
+ : undefined,
127
+ locations: service.locations,
128
+ staff: staff,
129
+ mainMedia: mainMedia,
130
+ coverMedia: coverMedia,
131
+ mediaItems: mediaItems.length > 0 ? mediaItems : undefined,
132
+ price: priceInfo,
133
+ defaultCapacity: defaultCapacity,
134
+ selected,
62
135
  });
63
136
  }
137
+ /**
138
+ * Core component that provides access to service price via render props.
139
+ * Returns comprehensive pricing data including money, rate type, and service capabilities.
140
+ *
141
+ * @component
142
+ * @example
143
+ * ```tsx
144
+ * <Service.Price>
145
+ * {({ money, rateType, minPrice, maxPrice, priceDescription, hasAddOns, hasPricingPlans }) => (
146
+ * rateType === 'FIXED' && money ? (
147
+ * <Money money={money} />
148
+ * ) : rateType === 'VARIED' && minPrice && maxPrice ? (
149
+ * <span><Money money={minPrice} /> - <Money money={maxPrice} /></span>
150
+ * ) : rateType === 'CUSTOM' && priceDescription ? (
151
+ * <span>{priceDescription}</span>
152
+ * ) : rateType === 'NO_FEE' ? (
153
+ * <span>Free</span>
154
+ * ) : null
155
+ * )}
156
+ * </Service.Price>
157
+ * ```
158
+ */
159
+ export function Price(props) {
160
+ const serviceService = useService(ServiceServiceDefinition);
161
+ const paymentData = serviceService.paymentDataSignal.get();
162
+ return props.children(paymentData);
163
+ }
64
164
  /**
65
165
  * Core component that provides booking selection actions via render props.
66
166
  *
@@ -68,7 +168,7 @@ export function Info(props) {
68
168
  * @example
69
169
  * ```tsx
70
170
  * <Service.Actions>
71
- * {({ select, add, remove, service }) => (
171
+ * {({ select, add, remove, rawService, requiresAvailability }) => (
72
172
  * <div>
73
173
  * <button onClick={select}>Select</button>
74
174
  * <button onClick={add}>Add</button>
@@ -80,11 +180,109 @@ export function Info(props) {
80
180
  */
81
181
  export function Actions(props) {
82
182
  const serviceService = useService(ServiceServiceDefinition);
83
- const service = serviceService.service.get();
183
+ const rawService = serviceService.serviceSignal.get();
184
+ const bookable = serviceService.bookableSignal.get();
185
+ const selected = serviceService.selectedSignal.get();
186
+ const requiresAvailability = serviceService.requiresAvailabilitySignal.get();
187
+ return props.children({
188
+ select: serviceService.actions.select,
189
+ add: serviceService.actions.add,
190
+ remove: serviceService.actions.remove,
191
+ rawService,
192
+ bookable,
193
+ selected,
194
+ requiresAvailability,
195
+ });
196
+ }
197
+ /**
198
+ * Core component that provides access to service booking policy via render props.
199
+ * Computes all enabled flags and values from the booking policy.
200
+ *
201
+ * @component
202
+ * @example
203
+ * ```tsx
204
+ * <Service.Policy>
205
+ * {({ bookingPolicy, cancellationPolicyEnabled, latestCancellationInMinutes }) => (
206
+ * cancellationPolicyEnabled ? (
207
+ * <div>Cancel up to {latestCancellationInMinutes} minutes before</div>
208
+ * ) : null
209
+ * )}
210
+ * </Service.Policy>
211
+ * ```
212
+ */
213
+ export function Policy(props) {
214
+ const serviceService = useService(ServiceServiceDefinition);
215
+ const service = serviceService.serviceSignal.get();
216
+ const bookingPolicy = service.bookingPolicy || null;
217
+ // Compute all flags here (not in headless components)
218
+ const cancellationPolicy = bookingPolicy?.cancellationPolicy;
219
+ const cancellationPolicyEnabled = cancellationPolicy?.enabled ?? false;
220
+ const limitLatestCancellationEnabled = cancellationPolicy?.limitLatestCancellation ?? false;
221
+ const latestCancellationInMinutes = cancellationPolicy?.latestCancellationInMinutes;
222
+ const lateBookingPolicy = bookingPolicy?.limitLateBookingPolicy;
223
+ const latestBookingPolicyEnabled = lateBookingPolicy?.enabled ?? false;
224
+ const latestBookingInMinutes = lateBookingPolicy?.latestBookingInMinutes;
225
+ const earlyBookingPolicy = bookingPolicy?.limitEarlyBookingPolicy;
226
+ const earliestBookingPolicyEnabled = earlyBookingPolicy?.enabled ?? false;
227
+ const earliestBookingInMinutes = earlyBookingPolicy?.earliestBookingInMinutes;
228
+ const reschedulePolicy = bookingPolicy?.reschedulePolicy;
229
+ const reschedulePolicyEnabled = reschedulePolicy?.enabled ?? false;
230
+ const limitLatestRescheduleEnabled = reschedulePolicy?.limitLatestReschedule ?? false;
231
+ const latestRescheduleInMinutes = reschedulePolicy?.latestRescheduleInMinutes;
232
+ const waitlistPolicy = bookingPolicy?.waitlistPolicy;
233
+ const waitlistPolicyEnabled = waitlistPolicy?.enabled ?? false;
234
+ const waitlistCapacity = waitlistPolicy?.capacity;
235
+ const bookAfterStartPolicy = bookingPolicy?.bookAfterStartPolicy;
236
+ const bookAfterStartEnabled = bookAfterStartPolicy?.enabled ?? false;
237
+ const participantsPolicy = bookingPolicy?.participantsPolicy;
238
+ const maxParticipantsPerBooking = participantsPolicy?.maxParticipantsPerBooking;
239
+ return props.children({
240
+ bookingPolicy,
241
+ cancellationPolicyEnabled,
242
+ limitLatestCancellationEnabled,
243
+ latestCancellationInMinutes,
244
+ latestBookingPolicyEnabled,
245
+ latestBookingInMinutes,
246
+ earliestBookingPolicyEnabled,
247
+ earliestBookingInMinutes,
248
+ reschedulePolicyEnabled,
249
+ limitLatestRescheduleEnabled,
250
+ latestRescheduleInMinutes,
251
+ waitlistPolicyEnabled,
252
+ waitlistCapacity,
253
+ bookAfterStartEnabled,
254
+ maxParticipantsPerBooking,
255
+ });
256
+ }
257
+ /**
258
+ * Core component that provides access to service schedule via render props.
259
+ * Uses the SDK's Schedule type directly. Only renders for CLASS or COURSE service types.
260
+ *
261
+ * @component
262
+ * @example
263
+ * ```tsx
264
+ * <Service.Schedule>
265
+ * {({ schedule, isClassOrCourse }) => (
266
+ * schedule && isClassOrCourse ? (
267
+ * <div>
268
+ * {schedule.firstSessionStart && (
269
+ * <span>Starts: {new Date(schedule.firstSessionStart).toLocaleDateString()}</span>
270
+ * )}
271
+ * {schedule.lastSessionEnd && (
272
+ * <span>Ends: {new Date(schedule.lastSessionEnd).toLocaleDateString()}</span>
273
+ * )}
274
+ * </div>
275
+ * ) : null
276
+ * )}
277
+ * </Service.Schedule>
278
+ * ```
279
+ */
280
+ export function Schedule(props) {
281
+ const serviceService = useService(ServiceServiceDefinition);
282
+ const service = serviceService.serviceSignal.get();
283
+ const isClassOrCourse = service.type === 'CLASS' || service.type === 'COURSE';
84
284
  return props.children({
85
- select: serviceService.select,
86
- add: serviceService.add,
87
- remove: serviceService.remove,
88
- service,
285
+ schedule: service.schedule || null,
286
+ isClassOrCourse,
89
287
  });
90
288
  }