@wix/headless-bookings 0.0.20 → 0.0.22

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 (37) hide show
  1. package/cjs/dist/api/{time-slot-list → fetch-availability}/index.d.ts +4 -3
  2. package/cjs/dist/api/{time-slot-list → fetch-availability}/index.js +36 -15
  3. package/cjs/dist/api/index.d.ts +1 -1
  4. package/cjs/dist/api/index.js +1 -1
  5. package/cjs/dist/react/booking/Booking.d.ts +2 -1
  6. package/cjs/dist/react/booking/Booking.js +1 -1
  7. package/cjs/dist/react/core/booking/Booking.d.ts +3 -2
  8. package/cjs/dist/react/core/booking/Booking.js +5 -2
  9. package/cjs/dist/react/core/time-slot-list/TimeSlot.d.ts +4 -4
  10. package/cjs/dist/react/core/time-slot-list/TimeSlot.js +17 -7
  11. package/cjs/dist/react/core/time-slot-list/TimeSlotList.js +3 -2
  12. package/cjs/dist/react/time-slot-list/TimeSlot.d.ts +6 -6
  13. package/cjs/dist/react/time-slot-list/TimeSlot.js +6 -6
  14. package/cjs/dist/react/time-slot-list/TimeSlotList.d.ts +4 -4
  15. package/cjs/dist/services/booking/booking.d.ts +6 -0
  16. package/cjs/dist/services/booking/booking.js +21 -1
  17. package/cjs/dist/services/time-slot-list/time-slot-list.def.d.ts +12 -4
  18. package/cjs/dist/services/time-slot-list/time-slot-list.js +37 -34
  19. package/dist/api/{time-slot-list → fetch-availability}/index.d.ts +4 -3
  20. package/dist/api/{time-slot-list → fetch-availability}/index.js +36 -15
  21. package/dist/api/index.d.ts +1 -1
  22. package/dist/api/index.js +1 -1
  23. package/dist/react/booking/Booking.d.ts +2 -1
  24. package/dist/react/booking/Booking.js +1 -1
  25. package/dist/react/core/booking/Booking.d.ts +3 -2
  26. package/dist/react/core/booking/Booking.js +5 -2
  27. package/dist/react/core/time-slot-list/TimeSlot.d.ts +4 -4
  28. package/dist/react/core/time-slot-list/TimeSlot.js +17 -7
  29. package/dist/react/core/time-slot-list/TimeSlotList.js +3 -2
  30. package/dist/react/time-slot-list/TimeSlot.d.ts +6 -6
  31. package/dist/react/time-slot-list/TimeSlot.js +6 -6
  32. package/dist/react/time-slot-list/TimeSlotList.d.ts +4 -4
  33. package/dist/services/booking/booking.d.ts +6 -0
  34. package/dist/services/booking/booking.js +21 -1
  35. package/dist/services/time-slot-list/time-slot-list.def.d.ts +12 -4
  36. package/dist/services/time-slot-list/time-slot-list.js +37 -34
  37. package/package.json +2 -2
@@ -6,7 +6,11 @@
6
6
  import { defineService, implementService } from '@wix/services-definitions';
7
7
  import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
8
8
  // ============================================================================
9
- // Types
9
+ // Constants
10
+ // ============================================================================
11
+ export const ANY_STAFF_MEMBER_ID = '__any_staff_member__';
12
+ // ============================================================================
13
+ // Helpers
10
14
  // ============================================================================
11
15
  /**
12
16
  * Generates a unique instance ID for service selections
@@ -14,6 +18,14 @@ import { SignalsServiceDefinition, } from '@wix/services-definitions/core-servic
14
18
  function generateInstanceId() {
15
19
  return crypto.randomUUID();
16
20
  }
21
+ /**
22
+ * Gets the client's timezone from Intl API
23
+ */
24
+ function getClientTimezone() {
25
+ return typeof Intl !== 'undefined'
26
+ ? Intl.DateTimeFormat().resolvedOptions().timeZone
27
+ : undefined;
28
+ }
17
29
  /**
18
30
  * Service definition for Booking
19
31
  */
@@ -55,6 +67,7 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
55
67
  }));
56
68
  const serviceSelectionsSignal = signalsService.signal(initialItemsWithIds);
57
69
  const locationSignal = signalsService.signal(config.location || null);
70
+ const timezoneSignal = signalsService.signal(config.timezone ?? getClientTimezone());
58
71
  const formSubmissionSignal = signalsService.signal(config.formSubmission || null);
59
72
  // ========================================================================
60
73
  // Actions Implementation
@@ -142,6 +155,12 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
142
155
  clearLocation() {
143
156
  locationSignal.set(null);
144
157
  },
158
+ // Timezone actions
159
+ setTimezone(timezone) {
160
+ if (timezoneSignal.get() !== timezone) {
161
+ timezoneSignal.set(timezone);
162
+ }
163
+ },
145
164
  // Form Submission actions
146
165
  setFormSubmission(values) {
147
166
  formSubmissionSignal.set(values);
@@ -159,6 +178,7 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
159
178
  return {
160
179
  serviceSelections: serviceSelectionsSignal,
161
180
  location: locationSignal,
181
+ timezone: timezoneSignal,
162
182
  formSubmission: formSubmissionSignal,
163
183
  actions,
164
184
  };
@@ -65,11 +65,21 @@ export interface TimeSlotsFilters {
65
65
  * Default: 1000. Max: 1000.
66
66
  * @example
67
67
  * ```ts
68
- * // Load 6 slots at a time
69
- * filters: { slotsPerPage: 6 }
68
+ * // Load 10 slots at a time
69
+ * filters: { slotsPerPage: 10 }
70
70
  * ```
71
71
  */
72
72
  slotsPerPage?: number;
73
+ /**
74
+ * Maximum number of time slots to return per day.
75
+ * Useful for fetching availability indicators (set to 1 to get one slot per day).
76
+ * @example
77
+ * ```ts
78
+ * // Get one slot per day for calendar indicators
79
+ * filters: { timeSlotsPerDay: 1 }
80
+ * ```
81
+ */
82
+ timeSlotsPerDay?: number;
73
83
  }
74
84
  export interface TimeSlotListServiceConfig {
75
85
  /** Services to fetch time slots for */
@@ -92,7 +102,6 @@ export interface TimeSlotListServiceActions {
92
102
  clearPreferences: () => void;
93
103
  loadMore: () => Promise<void>;
94
104
  setDateRange: (start: Date, end: Date) => void;
95
- setTimezone: (tz: string) => void;
96
105
  }
97
106
  export interface TimeSlotListServiceAPI {
98
107
  timeSlots: Signal<TimeSlot[]>;
@@ -102,7 +111,6 @@ export interface TimeSlotListServiceAPI {
102
111
  hasMore: Signal<boolean>;
103
112
  startDate: Signal<Date>;
104
113
  endDate: Signal<Date>;
105
- timezone: Signal<string | undefined>;
106
114
  actions: TimeSlotListServiceActions;
107
115
  }
108
116
  export declare const TimeSlotListServiceDefinition: string & {
@@ -4,10 +4,9 @@
4
4
  */
5
5
  import { implementService } from '@wix/services-definitions';
6
6
  import { SignalsServiceDefinition } from '@wix/services-definitions/core-services/signals';
7
- import { format } from 'date-fns';
8
7
  import { TimeSlotListServiceDefinition, ViewMode, } from './time-slot-list.def.js';
9
- import { fetchAvailability } from '../../api/time-slot-list/index.js';
10
- import { BookingServiceDefinition, } from '../booking/booking.js';
8
+ import { fetchAvailability } from '../../api/fetch-availability/index.js';
9
+ import { BookingServiceDefinition, ANY_STAFF_MEMBER_ID, } from '../booking/booking.js';
11
10
  // ============================================================================
12
11
  // Helpers
13
12
  // ============================================================================
@@ -19,13 +18,6 @@ function getClientTimezone() {
19
18
  ? Intl.DateTimeFormat().resolvedOptions().timeZone
20
19
  : undefined;
21
20
  }
22
- /**
23
- * Formats a Date to local ISO string (YYYY-MM-DDThh:mm:ss)
24
- */
25
- function toLocalDateString(date) {
26
- const pattern = "yyyy-MM-dd'T'HH:mm:ss";
27
- return format(date, pattern);
28
- }
29
21
  /**
30
22
  * Merges new grouped slots with existing ones
31
23
  * Slots with the same start time are merged
@@ -68,7 +60,6 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
68
60
  const viewModeSignal = signalsService.signal(ViewMode.CLASSIC);
69
61
  const startDateSignal = signalsService.signal(config.startDate || currentDate);
70
62
  const endDateSignal = signalsService.signal(config.endDate || getDefaultEndDate());
71
- const timezoneSignal = signalsService.signal(config.timezone ?? getClientTimezone());
72
63
  const cursorSignal = signalsService.signal(undefined);
73
64
  /**
74
65
  * Helper: Get services from config or BookingService
@@ -84,9 +75,9 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
84
75
  const getFilters = () => {
85
76
  // Get BookingService selections (these take priority)
86
77
  const location = bookingService.location.get();
87
- const selections = bookingService.serviceSelections.get();
88
- const selectedStaffMemberIds = selections
89
- .map(({ resources }) => resources.map((r) => r._id))
78
+ const serviceSelections = bookingService.serviceSelections.get();
79
+ const selectedStaffMemberIds = serviceSelections
80
+ .map((serviceSelection) => serviceSelection.resources.map((r) => r._id))
90
81
  .flat();
91
82
  const selectedLocation = location ? { _id: location.id } : null;
92
83
  // Merge config filters with BookingService selections (selections win)
@@ -114,9 +105,9 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
114
105
  try {
115
106
  const response = await fetchAvailability({
116
107
  services: selectedServices,
117
- fromLocalDate: toLocalDateString(startDateSignal.get()),
118
- toLocalDate: toLocalDateString(endDateSignal.get()),
119
- timeZone: timezoneSignal.get(),
108
+ fromLocalDate: startDateSignal.get(),
109
+ toLocalDate: endDateSignal.get(),
110
+ timeZone: bookingService.timezone.get(),
120
111
  viewMode: viewModeSignal.get(),
121
112
  cursor: cursorSignal.get(),
122
113
  ...filters,
@@ -243,6 +234,18 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
243
234
  const currentSelections = selectionSignal.get();
244
235
  if (!currentSelections?.slot)
245
236
  return;
237
+ if (staffMemberId === ANY_STAFF_MEMBER_ID) {
238
+ bookingService.actions.updateItem({
239
+ resources: [
240
+ {
241
+ _id: ANY_STAFF_MEMBER_ID,
242
+ name: 'Any staff member',
243
+ },
244
+ ],
245
+ });
246
+ selectionSignal.set({ ...currentSelections, staffMemberId });
247
+ return;
248
+ }
246
249
  const slot = currentSelections.slot;
247
250
  // Find all locations that have this staff member
248
251
  const locationsWithThisStaff = slot.staffByLocation.filter((loc) => loc.staffMembers.some((s) => s.id === staffMemberId));
@@ -300,16 +303,14 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
300
303
  if (!cursor || isLoadingSignal.get())
301
304
  return;
302
305
  const selectedServices = getServices();
303
- if (selectedServices.length === 0)
304
- return;
305
306
  const filters = getFilters();
306
307
  isLoadingSignal.set(true);
307
308
  try {
308
309
  const response = await fetchAvailability({
309
310
  services: selectedServices,
310
- fromLocalDate: toLocalDateString(startDateSignal.get()),
311
- toLocalDate: toLocalDateString(endDateSignal.get()),
312
- timeZone: timezoneSignal.get(),
311
+ fromLocalDate: startDateSignal.get(),
312
+ toLocalDate: endDateSignal.get(),
313
+ timeZone: bookingService.timezone.get(),
313
314
  cursor,
314
315
  viewMode: viewModeSignal.get(),
315
316
  ...filters,
@@ -345,17 +346,21 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
345
346
  endDateSignal.set(newEndDate);
346
347
  fetchTimeSlots();
347
348
  };
348
- /**
349
- * Action: Update timezone and refetch slots
350
- */
351
- const setTimezone = (newTimezone) => {
352
- timezoneSignal.set(newTimezone);
353
- fetchTimeSlots();
354
- };
355
349
  // Auto-fetch on initialization if no pre-loaded data provided
356
- if (!config.timeSlots && config.services && config.services.length > 0) {
350
+ if (!config.timeSlots) {
357
351
  fetchTimeSlots();
358
352
  }
353
+ // Subscribe to timezone changes from BookingService to auto-refetch
354
+ // NOTE: This should depend ONLY on bookingService.timezone.
355
+ let firstTimezoneEffectRun = true;
356
+ signalsService.effect(() => {
357
+ bookingService.timezone.get();
358
+ if (firstTimezoneEffectRun) {
359
+ firstTimezoneEffectRun = false;
360
+ return;
361
+ }
362
+ fetchTimeSlots();
363
+ });
359
364
  // Return service API
360
365
  return {
361
366
  // State Signals
@@ -367,7 +372,6 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
367
372
  // Configuration Signals (viewMode is internal only)
368
373
  startDate: startDateSignal,
369
374
  endDate: endDateSignal,
370
- timezone: timezoneSignal,
371
375
  // Actions
372
376
  actions: {
373
377
  selectSlot,
@@ -376,7 +380,6 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
376
380
  clearPreferences,
377
381
  loadMore,
378
382
  setDateRange,
379
- setTimezone,
380
383
  },
381
384
  };
382
385
  });
@@ -408,8 +411,8 @@ export async function loadTimeSlotListConfig(options) {
408
411
  try {
409
412
  const response = await fetchAvailability({
410
413
  services: options.services || [],
411
- fromLocalDate: toLocalDateString(startDate),
412
- toLocalDate: toLocalDateString(endDate),
414
+ fromLocalDate: startDate,
415
+ toLocalDate: endDate,
413
416
  timeZone: options.timezone ?? getClientTimezone(),
414
417
  ...options.filters,
415
418
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-bookings",
3
- "version": "0.0.20",
3
+ "version": "0.0.22",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -70,5 +70,5 @@
70
70
  "groupId": "com.wixpress.headless-components"
71
71
  }
72
72
  },
73
- "falconPackageHash": "25ba2b137a6e9ae4a9d267c7eeede1474665fe843f2999d663f593e4"
73
+ "falconPackageHash": "0404aa644e83255c1641503e68142fb002318cef90f00e879d419ac9"
74
74
  }