@wix/headless-bookings 0.0.24 → 0.0.26

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 (67) hide show
  1. package/cjs/dist/__mocks__/services.d.ts +75 -0
  2. package/cjs/dist/__mocks__/services.js +308 -0
  3. package/cjs/dist/api/fetch-availability/index.d.ts +5 -5
  4. package/cjs/dist/api/fetch-availability/index.js +9 -6
  5. package/cjs/dist/react/booking/Book.d.ts +49 -0
  6. package/cjs/dist/react/booking/Book.js +46 -0
  7. package/cjs/dist/react/booking/Booking.d.ts +18 -0
  8. package/cjs/dist/react/booking/Booking.js +19 -0
  9. package/cjs/dist/react/core/booking/Book.d.ts +41 -0
  10. package/cjs/dist/react/core/booking/Book.js +59 -0
  11. package/cjs/dist/react/core/time-slot-list/TimeSlot.d.ts +4 -4
  12. package/cjs/dist/react/core/time-slot-list/TimeSlot.js +3 -10
  13. package/cjs/dist/react/time-slot-list/TimeSlot.d.ts +3 -3
  14. package/cjs/dist/react/time-slot-list/TimeSlot.js +3 -3
  15. package/cjs/dist/react/time-slot-list/TimeSlotList.d.ts +5 -3
  16. package/cjs/dist/services/booking/book-action/bookAction.d.ts +21 -0
  17. package/cjs/dist/services/booking/book-action/bookAction.js +59 -0
  18. package/cjs/dist/services/booking/book-action/buildBookingRequest.d.ts +22 -0
  19. package/cjs/dist/services/booking/book-action/buildBookingRequest.js +81 -0
  20. package/cjs/dist/services/booking/book-action/buildCheckoutRequest.d.ts +26 -0
  21. package/cjs/dist/services/booking/book-action/buildCheckoutRequest.js +62 -0
  22. package/cjs/dist/services/booking/book-action/canBook.d.ts +23 -0
  23. package/cjs/dist/services/booking/book-action/canBook.js +19 -0
  24. package/cjs/dist/services/booking/book-action/index.d.ts +8 -0
  25. package/cjs/dist/services/booking/book-action/index.js +7 -0
  26. package/cjs/dist/services/booking/book-action/types.d.ts +50 -0
  27. package/cjs/dist/services/booking/book-action/types.js +4 -0
  28. package/cjs/dist/services/booking/booking.d.ts +2 -0
  29. package/cjs/dist/services/booking/booking.js +16 -0
  30. package/cjs/dist/services/index.d.ts +1 -0
  31. package/cjs/dist/services/index.js +1 -0
  32. package/cjs/dist/services/time-slot-list/time-slot-list.def.d.ts +10 -2
  33. package/cjs/dist/services/time-slot-list/time-slot-list.js +9 -29
  34. package/dist/__mocks__/services.d.ts +75 -0
  35. package/dist/__mocks__/services.js +308 -0
  36. package/dist/api/fetch-availability/index.d.ts +5 -5
  37. package/dist/api/fetch-availability/index.js +9 -6
  38. package/dist/react/booking/Book.d.ts +49 -0
  39. package/dist/react/booking/Book.js +46 -0
  40. package/dist/react/booking/Booking.d.ts +18 -0
  41. package/dist/react/booking/Booking.js +19 -0
  42. package/dist/react/core/booking/Book.d.ts +41 -0
  43. package/dist/react/core/booking/Book.js +59 -0
  44. package/dist/react/core/time-slot-list/TimeSlot.d.ts +4 -4
  45. package/dist/react/core/time-slot-list/TimeSlot.js +3 -10
  46. package/dist/react/time-slot-list/TimeSlot.d.ts +3 -3
  47. package/dist/react/time-slot-list/TimeSlot.js +3 -3
  48. package/dist/react/time-slot-list/TimeSlotList.d.ts +5 -3
  49. package/dist/services/booking/book-action/bookAction.d.ts +21 -0
  50. package/dist/services/booking/book-action/bookAction.js +59 -0
  51. package/dist/services/booking/book-action/buildBookingRequest.d.ts +22 -0
  52. package/dist/services/booking/book-action/buildBookingRequest.js +81 -0
  53. package/dist/services/booking/book-action/buildCheckoutRequest.d.ts +26 -0
  54. package/dist/services/booking/book-action/buildCheckoutRequest.js +62 -0
  55. package/dist/services/booking/book-action/canBook.d.ts +23 -0
  56. package/dist/services/booking/book-action/canBook.js +19 -0
  57. package/dist/services/booking/book-action/index.d.ts +8 -0
  58. package/dist/services/booking/book-action/index.js +7 -0
  59. package/dist/services/booking/book-action/types.d.ts +50 -0
  60. package/dist/services/booking/book-action/types.js +4 -0
  61. package/dist/services/booking/booking.d.ts +2 -0
  62. package/dist/services/booking/booking.js +16 -0
  63. package/dist/services/index.d.ts +1 -0
  64. package/dist/services/index.js +1 -0
  65. package/dist/services/time-slot-list/time-slot-list.def.d.ts +10 -2
  66. package/dist/services/time-slot-list/time-slot-list.js +9 -29
  67. package/package.json +4 -3
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Shared mock data for booking service tests
3
+ */
4
+ import type { Service } from '@wix/auto_sdk_bookings_services';
5
+ import type { ServiceServiceConfig } from '../services/service/service.js';
6
+ import type { ServiceListServiceConfig } from '../services/service-list/service-list.js';
7
+ import type { PagingMetadata } from '../api/query-services/index.js';
8
+ /**
9
+ * Booking app ID constant
10
+ */
11
+ export declare const BOOKING_APP_ID = "13d21c63-b5ec-5912-8397-c3a5ddb27a97";
12
+ /**
13
+ * Base mock service with all fields populated (APPOINTMENT type, FIXED pricing)
14
+ */
15
+ export declare const mockService: Service;
16
+ /**
17
+ * Mock service with VARIED pricing
18
+ */
19
+ export declare const mockServiceVariedPrice: Service;
20
+ /**
21
+ * Mock service with CUSTOM pricing
22
+ */
23
+ export declare const mockServiceCustomPrice: Service;
24
+ /**
25
+ * Mock service with NO_FEE pricing (free)
26
+ */
27
+ export declare const mockServiceNoFee: Service;
28
+ /**
29
+ * Mock CLASS type service
30
+ */
31
+ export declare const mockServiceClass: Service;
32
+ /**
33
+ * Mock COURSE type service
34
+ */
35
+ export declare const mockServiceCourse: Service;
36
+ /**
37
+ * Mock service without optional fields (minimal)
38
+ */
39
+ export declare const mockServiceMinimal: Service;
40
+ /**
41
+ * Array of mock services for list tests
42
+ */
43
+ export declare const mockServices: Service[];
44
+ /**
45
+ * Default paging metadata
46
+ */
47
+ export declare const mockPagingMetadata: PagingMetadata;
48
+ /**
49
+ * Paging metadata with more items available
50
+ */
51
+ export declare const mockPagingMetadataWithMore: PagingMetadata;
52
+ /**
53
+ * Empty paging metadata
54
+ */
55
+ export declare const mockPagingMetadataEmpty: PagingMetadata;
56
+ /**
57
+ * Service list config with services
58
+ */
59
+ export declare const mockServiceListConfig: ServiceListServiceConfig;
60
+ /**
61
+ * Service list config with hasNext for pagination testing
62
+ */
63
+ export declare const mockServiceListConfigWithMore: ServiceListServiceConfig;
64
+ /**
65
+ * Empty service list config
66
+ */
67
+ export declare const mockEmptyServiceListConfig: ServiceListServiceConfig;
68
+ /**
69
+ * Service config for single service
70
+ */
71
+ export declare const mockServiceConfig: ServiceServiceConfig;
72
+ /**
73
+ * Helper to create a large list of services for pagination tests
74
+ */
75
+ export declare function createLargeServiceList(count: number): Service[];
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Shared mock data for booking service tests
3
+ */
4
+ import { RateType, ServiceType } from '@wix/auto_sdk_bookings_services';
5
+ /**
6
+ * Booking app ID constant
7
+ */
8
+ export const BOOKING_APP_ID = '13d21c63-b5ec-5912-8397-c3a5ddb27a97';
9
+ /**
10
+ * Base mock service with all fields populated (APPOINTMENT type, FIXED pricing)
11
+ */
12
+ export const mockService = {
13
+ _id: 'test-service-id',
14
+ name: 'Test Service Name',
15
+ description: 'Test service description',
16
+ tagLine: 'Test tagline',
17
+ type: ServiceType.APPOINTMENT,
18
+ category: {
19
+ _id: 'category-1',
20
+ name: 'Test Category',
21
+ },
22
+ payment: {
23
+ rateType: RateType.FIXED,
24
+ fixed: {
25
+ price: {
26
+ value: '50.00',
27
+ currency: 'USD',
28
+ },
29
+ },
30
+ },
31
+ schedule: {
32
+ availabilityConstraints: {
33
+ sessionDurations: [60],
34
+ },
35
+ },
36
+ locations: [
37
+ {
38
+ _id: 'location-1',
39
+ type: 'BUSINESS',
40
+ business: {
41
+ _id: 'business-loc-1',
42
+ name: 'Main Office',
43
+ address: {
44
+ formatted: '123 Main St, City, ST 12345',
45
+ },
46
+ },
47
+ },
48
+ ],
49
+ media: {
50
+ mainMedia: {
51
+ image: 'wix:image://v1/test-image.jpg',
52
+ },
53
+ coverMedia: {
54
+ image: 'wix:image://v1/test-cover.jpg',
55
+ },
56
+ items: [
57
+ { image: 'wix:image://v1/image1.jpg' },
58
+ { image: 'wix:image://v1/image2.jpg' },
59
+ ],
60
+ },
61
+ bookingPolicy: {
62
+ cancellationPolicy: {
63
+ enabled: true,
64
+ limitLatestCancellation: true,
65
+ latestCancellationInMinutes: 60,
66
+ },
67
+ limitLateBookingPolicy: {
68
+ enabled: true,
69
+ latestBookingInMinutes: 30,
70
+ },
71
+ limitEarlyBookingPolicy: {
72
+ enabled: true,
73
+ earliestBookingInMinutes: 10080, // 7 days
74
+ },
75
+ reschedulePolicy: {
76
+ enabled: true,
77
+ limitLatestReschedule: true,
78
+ latestRescheduleInMinutes: 120,
79
+ },
80
+ waitlistPolicy: {
81
+ enabled: true,
82
+ capacity: 10,
83
+ },
84
+ bookAfterStartPolicy: {
85
+ enabled: true,
86
+ },
87
+ participantsPolicy: {
88
+ maxParticipantsPerBooking: 5,
89
+ },
90
+ },
91
+ defaultCapacity: 20,
92
+ };
93
+ /**
94
+ * Mock service with VARIED pricing
95
+ */
96
+ export const mockServiceVariedPrice = {
97
+ ...mockService,
98
+ _id: 'test-service-varied',
99
+ name: 'Varied Price Service',
100
+ payment: {
101
+ rateType: RateType.VARIED,
102
+ varied: {
103
+ defaultPrice: {
104
+ value: '50.00',
105
+ currency: 'USD',
106
+ },
107
+ minPrice: {
108
+ value: '30.00',
109
+ currency: 'USD',
110
+ },
111
+ maxPrice: {
112
+ value: '100.00',
113
+ currency: 'USD',
114
+ },
115
+ },
116
+ },
117
+ };
118
+ /**
119
+ * Mock service with CUSTOM pricing
120
+ */
121
+ export const mockServiceCustomPrice = {
122
+ ...mockService,
123
+ _id: 'test-service-custom',
124
+ name: 'Custom Price Service',
125
+ payment: {
126
+ rateType: RateType.CUSTOM,
127
+ custom: {
128
+ description: 'Contact for pricing',
129
+ },
130
+ },
131
+ };
132
+ /**
133
+ * Mock service with NO_FEE pricing (free)
134
+ */
135
+ export const mockServiceNoFee = {
136
+ ...mockService,
137
+ _id: 'test-service-no-fee',
138
+ name: 'Free Service',
139
+ payment: {
140
+ rateType: RateType.NO_FEE,
141
+ },
142
+ };
143
+ /**
144
+ * Mock CLASS type service
145
+ */
146
+ export const mockServiceClass = {
147
+ ...mockService,
148
+ _id: 'test-service-class',
149
+ name: 'Class Service',
150
+ type: ServiceType.CLASS,
151
+ defaultCapacity: 15,
152
+ };
153
+ /**
154
+ * Mock COURSE type service
155
+ */
156
+ export const mockServiceCourse = {
157
+ ...mockService,
158
+ _id: 'test-service-course',
159
+ name: 'Course Service',
160
+ type: ServiceType.COURSE,
161
+ defaultCapacity: 25,
162
+ schedule: {
163
+ ...mockService.schedule,
164
+ firstSessionStart: new Date('2024-01-15T09:00:00Z'),
165
+ lastSessionEnd: new Date('2024-03-15T17:00:00Z'),
166
+ },
167
+ };
168
+ /**
169
+ * Mock service without optional fields (minimal)
170
+ */
171
+ export const mockServiceMinimal = {
172
+ _id: 'test-service-minimal',
173
+ name: 'Minimal Service',
174
+ description: '',
175
+ };
176
+ /**
177
+ * Array of mock services for list tests
178
+ */
179
+ export const mockServices = [
180
+ {
181
+ _id: 'service-1',
182
+ name: 'Service One',
183
+ description: 'Description for service one',
184
+ type: ServiceType.APPOINTMENT,
185
+ payment: {
186
+ rateType: RateType.FIXED,
187
+ fixed: {
188
+ price: {
189
+ value: '50.00',
190
+ currency: 'USD',
191
+ },
192
+ },
193
+ },
194
+ },
195
+ {
196
+ _id: 'service-2',
197
+ name: 'Service Two',
198
+ description: 'Description for service two',
199
+ type: ServiceType.CLASS,
200
+ payment: {
201
+ rateType: RateType.FIXED,
202
+ fixed: {
203
+ price: {
204
+ value: '75.00',
205
+ currency: 'USD',
206
+ },
207
+ },
208
+ },
209
+ defaultCapacity: 15,
210
+ },
211
+ {
212
+ _id: 'service-3',
213
+ name: 'Service Three',
214
+ description: 'Description for service three',
215
+ type: ServiceType.COURSE,
216
+ payment: {
217
+ rateType: RateType.CUSTOM,
218
+ custom: {
219
+ description: 'Contact for pricing',
220
+ },
221
+ },
222
+ defaultCapacity: 25,
223
+ },
224
+ ];
225
+ /**
226
+ * Default paging metadata
227
+ */
228
+ export const mockPagingMetadata = {
229
+ limit: 10,
230
+ offset: 0,
231
+ count: 3,
232
+ hasNext: false,
233
+ };
234
+ /**
235
+ * Paging metadata with more items available
236
+ */
237
+ export const mockPagingMetadataWithMore = {
238
+ limit: 10,
239
+ offset: 0,
240
+ count: 10,
241
+ hasNext: true,
242
+ };
243
+ /**
244
+ * Empty paging metadata
245
+ */
246
+ export const mockPagingMetadataEmpty = {
247
+ limit: 10,
248
+ offset: 0,
249
+ count: 0,
250
+ hasNext: false,
251
+ };
252
+ /**
253
+ * Service list config with services
254
+ */
255
+ export const mockServiceListConfig = {
256
+ services: mockServices,
257
+ options: {
258
+ appId: BOOKING_APP_ID,
259
+ pagingMetadata: mockPagingMetadata,
260
+ },
261
+ };
262
+ /**
263
+ * Service list config with hasNext for pagination testing
264
+ */
265
+ export const mockServiceListConfigWithMore = {
266
+ services: mockServices,
267
+ options: {
268
+ appId: BOOKING_APP_ID,
269
+ pagingMetadata: mockPagingMetadataWithMore,
270
+ },
271
+ };
272
+ /**
273
+ * Empty service list config
274
+ */
275
+ export const mockEmptyServiceListConfig = {
276
+ services: [],
277
+ options: {
278
+ appId: BOOKING_APP_ID,
279
+ pagingMetadata: mockPagingMetadataEmpty,
280
+ },
281
+ };
282
+ /**
283
+ * Service config for single service
284
+ */
285
+ export const mockServiceConfig = {
286
+ service: mockService,
287
+ appId: BOOKING_APP_ID,
288
+ };
289
+ /**
290
+ * Helper to create a large list of services for pagination tests
291
+ */
292
+ export function createLargeServiceList(count) {
293
+ return Array.from({ length: count }, (_, i) => ({
294
+ _id: `service-${i}`,
295
+ name: `Service ${i}`,
296
+ description: `Description for service ${i}`,
297
+ type: ServiceType.APPOINTMENT,
298
+ payment: {
299
+ rateType: RateType.FIXED,
300
+ fixed: {
301
+ price: {
302
+ value: '50.00',
303
+ currency: 'USD',
304
+ },
305
+ },
306
+ },
307
+ }));
308
+ }
@@ -2,15 +2,15 @@
2
2
  * Time Slot List API
3
3
  * Fetches available time slots for booking services from Wix Bookings
4
4
  */
5
- import { type Location, type CursorPaging, type CursorPagingMetadata } from '@wix/auto_sdk_bookings_availability-time-slots';
5
+ import { type CursorPaging, type CursorPagingMetadata } from '@wix/auto_sdk_bookings_availability-time-slots';
6
6
  import { type Service } from '@wix/auto_sdk_bookings_services';
7
- import { ViewMode, type TimeSlot } from '../../services/time-slot-list/time-slot-list.def.js';
7
+ import { ViewMode, type TimeSlot, type Location } from '../../services/time-slot-list/time-slot-list.def.js';
8
8
  export interface FetchAvailabilityOptions {
9
9
  services: Service[];
10
10
  fromLocalDate?: string | null;
11
11
  toLocalDate?: string | null;
12
12
  timeZone?: string | null;
13
- locations?: Location[];
13
+ location?: Location;
14
14
  /** Filter by specific staff member IDs (passed as resourceIds to API) */
15
15
  staffMemberIds?: string[];
16
16
  cursorPaging?: CursorPaging;
@@ -52,12 +52,12 @@ export interface FetchAvailabilityResponse {
52
52
  * // response.timeSlots are grouped by time
53
53
  * ```
54
54
  */
55
- export declare function fetchAvailability({ services: selectedServices, viewMode, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, timeSlotsPerDay, }: {
55
+ export declare function fetchAvailability({ services: selectedServices, viewMode, fromLocalDate, toLocalDate, timeZone, location, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, timeSlotsPerDay, }: {
56
56
  services: Service[];
57
57
  fromLocalDate: Date;
58
58
  toLocalDate: Date;
59
59
  timeZone?: string;
60
- locations?: Location[];
60
+ location?: Location;
61
61
  staffMemberIds?: string[];
62
62
  cursor?: string;
63
63
  viewMode?: ViewMode;
@@ -115,7 +115,7 @@ function detectFlow(bookingServices, viewMode) {
115
115
  * // response.timeSlots are grouped by time
116
116
  * ```
117
117
  */
118
- export async function fetchAvailability({ services: selectedServices, viewMode = ViewMode.CLASSIC, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, timeSlotsPerDay, }) {
118
+ export async function fetchAvailability({ services: selectedServices, viewMode = ViewMode.CLASSIC, fromLocalDate, toLocalDate, timeZone, location, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, timeSlotsPerDay, }) {
119
119
  const flow = detectFlow(selectedServices, viewMode);
120
120
  const serviceIds = selectedServices.map((s) => s._id);
121
121
  // Convert Date objects to local ISO strings
@@ -138,6 +138,12 @@ export async function fetchAvailability({ services: selectedServices, viewMode =
138
138
  },
139
139
  ]
140
140
  : undefined;
141
+ const includeResourceTypeIds = isAnyStaffMemberSelected || isFetchAvailabileDays
142
+ ? undefined
143
+ : [STAFF_RESOURCE_TYPE_ID];
144
+ const locations = location
145
+ ? [{ _id: location.id, locationType: location.locationType }]
146
+ : undefined;
141
147
  const response = await listAvailabilityTimeSlots({
142
148
  serviceId: serviceIds[0],
143
149
  fromLocalDate: fromLocalDateStr,
@@ -148,9 +154,7 @@ export async function fetchAvailability({ services: selectedServices, viewMode =
148
154
  resourceTypes,
149
155
  bookable,
150
156
  bookingPolicyViolations,
151
- includeResourceTypeIds: isAnyStaffMemberSelected || isFetchAvailabileDays
152
- ? undefined
153
- : [STAFF_RESOURCE_TYPE_ID],
157
+ includeResourceTypeIds,
154
158
  timeSlotsPerDay,
155
159
  });
156
160
  return {
@@ -164,7 +168,7 @@ export async function fetchAvailability({ services: selectedServices, viewMode =
164
168
  }
165
169
  if (flow === FlowType.CLASS) {
166
170
  // Build eventFilter for location and staff filtering
167
- const locationIds = locations?.map((l) => l._id);
171
+ const locationIds = location ? [location.id] : undefined;
168
172
  const eventFilter = {};
169
173
  if (locationIds?.length) {
170
174
  eventFilter['location.id'] = locationIds;
@@ -185,7 +189,6 @@ export async function fetchAvailability({ services: selectedServices, viewMode =
185
189
  });
186
190
  return {
187
191
  timeSlots: groupTimeSlotsByTime(response.timeSlots || []),
188
- // Note: listEventTimeSlots uses pagingMetadata instead of cursorPagingMetadata
189
192
  cursorPagingMetadata: response.pagingMetadata,
190
193
  };
191
194
  }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Book - High-level headless component for the book action button
3
+ * Wraps core Book with AsChildSlot pattern for customization
4
+ */
5
+ import React from 'react';
6
+ import type { BookChildProps, BookResult, BookingError } from '../../services/booking/book-action/types.js';
7
+ /**
8
+ * Props for the high-level Book component
9
+ */
10
+ export interface BookProps {
11
+ asChild?: boolean;
12
+ children?: React.ReactNode | ((props: BookChildProps) => React.ReactNode);
13
+ className?: string;
14
+ label?: string;
15
+ loadingState?: string;
16
+ disabled?: boolean;
17
+ onCheckoutRequired?: (result: BookResult) => void;
18
+ onError?: (error: BookingError) => void;
19
+ }
20
+ /**
21
+ * Book action button component with AsChildSlot pattern
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * // Simple usage
26
+ * <Booking.Actions.Book />
27
+ *
28
+ * // With callbacks
29
+ * <Booking.Actions.Book
30
+ * onCheckoutRequired={({ checkoutId }) => router.push(`/checkout?id=${checkoutId}`)}
31
+ * onError={(error) => console.error(error.message)}
32
+ * />
33
+ *
34
+ * // With render function
35
+ * <Booking.Actions.Book onCheckoutRequired={handleCheckout}>
36
+ * {({ isLoading, error, canBook, onClick, disabled }) => (
37
+ * <button onClick={onClick} disabled={disabled}>
38
+ * {isLoading ? <Spinner /> : 'Book Now'}
39
+ * </button>
40
+ * )}
41
+ * </Booking.Actions.Book>
42
+ *
43
+ * // With asChild
44
+ * <Booking.Actions.Book asChild onCheckoutRequired={handleCheckout}>
45
+ * <CustomBookButton />
46
+ * </Booking.Actions.Book>
47
+ * ```
48
+ */
49
+ export declare const Book: React.ForwardRefExoticComponent<BookProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Book - High-level headless component for the book action button
4
+ * Wraps core Book with AsChildSlot pattern for customization
5
+ */
6
+ import React from 'react';
7
+ import { AsChildSlot } from '@wix/headless-utils/react';
8
+ import * as CoreBook from '../core/booking/Book.js';
9
+ var TestIds;
10
+ (function (TestIds) {
11
+ TestIds["bookingActionBook"] = "booking-action-book";
12
+ })(TestIds || (TestIds = {}));
13
+ /**
14
+ * Book action button component with AsChildSlot pattern
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * // Simple usage
19
+ * <Booking.Actions.Book />
20
+ *
21
+ * // With callbacks
22
+ * <Booking.Actions.Book
23
+ * onCheckoutRequired={({ checkoutId }) => router.push(`/checkout?id=${checkoutId}`)}
24
+ * onError={(error) => console.error(error.message)}
25
+ * />
26
+ *
27
+ * // With render function
28
+ * <Booking.Actions.Book onCheckoutRequired={handleCheckout}>
29
+ * {({ isLoading, error, canBook, onClick, disabled }) => (
30
+ * <button onClick={onClick} disabled={disabled}>
31
+ * {isLoading ? <Spinner /> : 'Book Now'}
32
+ * </button>
33
+ * )}
34
+ * </Booking.Actions.Book>
35
+ *
36
+ * // With asChild
37
+ * <Booking.Actions.Book asChild onCheckoutRequired={handleCheckout}>
38
+ * <CustomBookButton />
39
+ * </Booking.Actions.Book>
40
+ * ```
41
+ */
42
+ export const Book = React.forwardRef((props, ref) => {
43
+ const { asChild, children, className, label = 'Book Now', loadingState = 'Booking...', disabled, onCheckoutRequired, onError, } = props;
44
+ return (_jsx(CoreBook.Book, { onCheckoutRequired: onCheckoutRequired, onError: onError, disabled: disabled, children: (childProps) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.bookingActionBook, "data-in-progress": childProps.isLoading, customElement: children, customElementProps: childProps, children: _jsx("button", { onClick: childProps.onClick, disabled: childProps.disabled, children: childProps.isLoading ? loadingState : label }) })) }));
45
+ });
46
+ Book.displayName = 'Booking.Actions.Book';
@@ -34,3 +34,21 @@ export declare const Root: {
34
34
  (props: BookingRootProps): React.ReactNode;
35
35
  displayName: string;
36
36
  };
37
+ /**
38
+ * Actions namespace containing action components for booking
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * // Simple usage
43
+ * <Booking.Actions.Book />
44
+ *
45
+ * // With callbacks
46
+ * <Booking.Actions.Book
47
+ * onCheckoutRequired={({ checkoutId }) => router.push(`/checkout?id=${checkoutId}`)}
48
+ * onError={(error) => toast.error(error)}
49
+ * />
50
+ * ```
51
+ */
52
+ export declare const Actions: {
53
+ Book: React.ForwardRefExoticComponent<import("./Book.js").BookProps & React.RefAttributes<HTMLButtonElement>>;
54
+ };
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as CoreBooking from '../core/booking/Booking.js';
3
+ import { Book } from './Book.js';
3
4
  /**
4
5
  * Root component that provides booking context to the entire app.
5
6
  *
@@ -23,3 +24,21 @@ export const Root = (props) => {
23
24
  return (_jsx(CoreBooking.Root, { bookingServiceConfig: bookingServiceConfig, children: children }));
24
25
  };
25
26
  Root.displayName = 'Booking.Root';
27
+ /**
28
+ * Actions namespace containing action components for booking
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * // Simple usage
33
+ * <Booking.Actions.Book />
34
+ *
35
+ * // With callbacks
36
+ * <Booking.Actions.Book
37
+ * onCheckoutRequired={({ checkoutId }) => router.push(`/checkout?id=${checkoutId}`)}
38
+ * onError={(error) => toast.error(error)}
39
+ * />
40
+ * ```
41
+ */
42
+ export const Actions = {
43
+ Book,
44
+ };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Core Book - Provides book action data via render props
3
+ * NO AsChildSlot, NO asChild - that belongs in high-level component
4
+ */
5
+ import React from 'react';
6
+ import type { BookResult, BookingError } from '../../../services/booking/book-action/types.js';
7
+ /**
8
+ * Data exposed by Book component via render props
9
+ */
10
+ export interface BookData {
11
+ isLoading: boolean;
12
+ error: BookingError | null;
13
+ canBook: boolean;
14
+ onClick: () => Promise<void>;
15
+ disabled: boolean;
16
+ }
17
+ /**
18
+ * Props for core Book component
19
+ */
20
+ export interface BookProps {
21
+ children: (data: BookData) => React.ReactNode;
22
+ onCheckoutRequired?: (result: BookResult) => void;
23
+ onError?: (error: BookingError) => void;
24
+ disabled?: boolean;
25
+ }
26
+ /**
27
+ * Core Book component - provides book action data via render props.
28
+ * Used internally by higher-level Booking.Actions.Book component.
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * <Book onCheckoutRequired={handleCheckout}>
33
+ * {({ isLoading, error, canBook, onClick, disabled }) => (
34
+ * <button onClick={onClick} disabled={disabled}>
35
+ * {isLoading ? 'Booking...' : 'Book Now'}
36
+ * </button>
37
+ * )}
38
+ * </Book>
39
+ * ```
40
+ */
41
+ export declare function Book(props: BookProps): React.ReactNode;