@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
@@ -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
  });
@@ -52,10 +52,10 @@ 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, }: {
55
+ export declare function fetchAvailability({ services: selectedServices, viewMode, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, timeSlotsPerDay, }: {
56
56
  services: Service[];
57
- fromLocalDate: string;
58
- toLocalDate: string;
57
+ fromLocalDate: Date;
58
+ toLocalDate: Date;
59
59
  timeZone?: string;
60
60
  locations?: Location[];
61
61
  staffMemberIds?: string[];
@@ -68,4 +68,5 @@ export declare function fetchAvailability({ services: selectedServices, viewMode
68
68
  bookOnlineDisabled?: boolean;
69
69
  };
70
70
  slotsPerPage?: number;
71
+ timeSlotsPerDay?: number;
71
72
  }): Promise<FetchAvailabilityResponse>;
@@ -5,6 +5,8 @@
5
5
  import { listAvailabilityTimeSlots, } from '@wix/auto_sdk_bookings_availability-time-slots';
6
6
  import { listEventTimeSlots } from '@wix/auto_sdk_bookings_event-time-slots';
7
7
  import { ServiceType } from '@wix/auto_sdk_bookings_services';
8
+ import { format } from 'date-fns';
9
+ import { ANY_STAFF_MEMBER_ID } from '../../services/booking/booking.js';
8
10
  import { ViewMode, } from '../../services/time-slot-list/time-slot-list.def.js';
9
11
  // ============================================================================
10
12
  // Internal Types
@@ -17,6 +19,16 @@ var FlowType;
17
19
  })(FlowType || (FlowType = {}));
18
20
  const STAFF_RESOURCE_TYPE_ID = '1cd44cf8-756f-41c3-bd90-3e2ffcaf1155';
19
21
  // ============================================================================
22
+ // Helpers
23
+ // ============================================================================
24
+ /**
25
+ * Formats a Date to local ISO string (YYYY-MM-DDThh:mm:ss)
26
+ */
27
+ function toLocalDateString(date) {
28
+ const pattern = "yyyy-MM-dd'T'HH:mm:ss";
29
+ return format(date, pattern);
30
+ }
31
+ // ============================================================================
20
32
  // Grouping Functions
21
33
  // ============================================================================
22
34
  /**
@@ -103,11 +115,22 @@ function detectFlow(bookingServices, viewMode) {
103
115
  * // response.timeSlots are grouped by time
104
116
  * ```
105
117
  */
106
- export async function fetchAvailability({ services: selectedServices, viewMode = ViewMode.CLASSIC, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, }) {
118
+ export async function fetchAvailability({ services: selectedServices, viewMode = ViewMode.CLASSIC, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, timeSlotsPerDay, }) {
107
119
  const flow = detectFlow(selectedServices, viewMode);
108
120
  const serviceIds = selectedServices.map((s) => s._id);
121
+ // Convert Date objects to local ISO strings
122
+ const fromLocalDateStr = toLocalDateString(fromLocalDate);
123
+ const toLocalDateStr = toLocalDateString(toLocalDate);
124
+ const isFetchAvailabileDays = timeSlotsPerDay === 1;
125
+ const cursorPaging = isFetchAvailabileDays
126
+ ? undefined
127
+ : {
128
+ cursor,
129
+ limit: slotsPerPage,
130
+ };
109
131
  if (flow === FlowType.SINGLE_SERVICE) {
110
- const resourceTypes = staffMemberIds?.length
132
+ const isAnyStaffMemberSelected = staffMemberIds?.length === 1 && staffMemberIds[0] === ANY_STAFF_MEMBER_ID;
133
+ const resourceTypes = staffMemberIds?.length && !isAnyStaffMemberSelected
111
134
  ? [
112
135
  {
113
136
  resourceTypeId: STAFF_RESOURCE_TYPE_ID,
@@ -117,18 +140,18 @@ export async function fetchAvailability({ services: selectedServices, viewMode =
117
140
  : undefined;
118
141
  const response = await listAvailabilityTimeSlots({
119
142
  serviceId: serviceIds[0],
120
- fromLocalDate,
121
- toLocalDate,
143
+ fromLocalDate: fromLocalDateStr,
144
+ toLocalDate: toLocalDateStr,
122
145
  timeZone,
123
146
  locations,
124
- cursorPaging: {
125
- cursor,
126
- limit: slotsPerPage,
127
- },
147
+ cursorPaging,
128
148
  resourceTypes,
129
149
  bookable,
130
150
  bookingPolicyViolations,
131
- includeResourceTypeIds: [STAFF_RESOURCE_TYPE_ID],
151
+ includeResourceTypeIds: isAnyStaffMemberSelected || isFetchAvailabileDays
152
+ ? undefined
153
+ : [STAFF_RESOURCE_TYPE_ID],
154
+ timeSlotsPerDay,
132
155
  });
133
156
  return {
134
157
  timeSlots: groupTimeSlotsByTime(response.timeSlots || []),
@@ -150,17 +173,15 @@ export async function fetchAvailability({ services: selectedServices, viewMode =
150
173
  eventFilter['resources.id'] = { $hasSome: staffMemberIds };
151
174
  }
152
175
  const response = await listEventTimeSlots({
153
- fromLocalDate,
154
- toLocalDate,
176
+ fromLocalDate: fromLocalDateStr,
177
+ toLocalDate: toLocalDateStr,
155
178
  timeZone,
156
179
  serviceIds: serviceIds.length ? serviceIds : undefined,
157
- cursorPaging: {
158
- cursor,
159
- limit: slotsPerPage,
160
- },
180
+ cursorPaging,
161
181
  eventFilter: Object.keys(eventFilter).length ? eventFilter : undefined,
162
182
  includeNonBookable: bookable ? !bookable : undefined,
163
183
  bookingPolicyViolations,
184
+ maxSlotsPerDay: timeSlotsPerDay,
164
185
  });
165
186
  return {
166
187
  timeSlots: groupTimeSlotsByTime(response.timeSlots || []),
@@ -5,7 +5,7 @@
5
5
  * @module API
6
6
  */
7
7
  export { getServiceById, getServiceBySlug, queryServices, type PagingMetadata, type ServiceListFilters, type QueryServicesRequest, } from './query-services/index.js';
8
- export { fetchAvailability } from './time-slot-list/index.js';
8
+ export { fetchAvailability } from './fetch-availability/index.js';
9
9
  /**
10
10
  * PagingMetadata includes:
11
11
  * - limit: Number of items per page
package/dist/api/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * @module API
6
6
  */
7
7
  export { getServiceById, getServiceBySlug, queryServices, } from './query-services/index.js';
8
- export { fetchAvailability } from './time-slot-list/index.js';
8
+ export { fetchAvailability } from './fetch-availability/index.js';
9
9
  /**
10
10
  * PagingMetadata includes:
11
11
  * - limit: Number of items per page
@@ -4,12 +4,13 @@
4
4
  */
5
5
  import React from 'react';
6
6
  import type { BookingServiceConfig } from '../../services/booking/booking.js';
7
+ export type { BookingData, DataProps } from '../core/booking/Booking.js';
7
8
  /**
8
9
  * Props for Booking.Root component
9
10
  */
10
11
  export interface BookingRootProps {
11
12
  children: React.ReactNode;
12
- bookingServiceConfig: BookingServiceConfig;
13
+ bookingServiceConfig?: BookingServiceConfig;
13
14
  }
14
15
  /**
15
16
  * Root component that provides booking context to the entire app.
@@ -19,7 +19,7 @@ import * as CoreBooking from '../core/booking/Booking.js';
19
19
  * ```
20
20
  */
21
21
  export const Root = (props) => {
22
- const { children, bookingServiceConfig } = props;
22
+ const { children, bookingServiceConfig = {} } = props;
23
23
  return (_jsx(CoreBooking.Root, { bookingServiceConfig: bookingServiceConfig, children: children }));
24
24
  };
25
25
  Root.displayName = 'Booking.Root';
@@ -10,7 +10,7 @@ import type { FormValues } from '@wix/form-public';
10
10
  */
11
11
  export interface RootProps {
12
12
  children: React.ReactNode;
13
- bookingServiceConfig: BookingServiceConfig;
13
+ bookingServiceConfig?: BookingServiceConfig;
14
14
  }
15
15
  /**
16
16
  * Root component that provides booking context to the entire app.
@@ -35,6 +35,7 @@ export declare function Root(props: RootProps): React.ReactNode;
35
35
  export interface BookingData {
36
36
  serviceSelections: ServiceSelection[];
37
37
  location: Location | null;
38
+ timezone: string | undefined;
38
39
  formSubmission: FormValues | null;
39
40
  actions: BookingServiceActions;
40
41
  }
@@ -52,7 +53,7 @@ export interface DataProps {
52
53
  * @example
53
54
  * ```tsx
54
55
  * <Booking.Data>
55
- * {({ serviceSelections, location, formSubmission, actions }) => (
56
+ * {({ serviceSelections, location, timezone, formSubmission, actions }) => (
56
57
  * serviceSelections.length > 0 ? (
57
58
  * <div>{serviceSelections.map(item => item.service.name).join(', ')}</div>
58
59
  * ) : (
@@ -18,7 +18,8 @@ import { BookingServiceDefinition, BookingService, } from '../../../services/boo
18
18
  * ```
19
19
  */
20
20
  export function Root(props) {
21
- return (_jsx(WixServices, { servicesMap: createServicesMap().addService(BookingServiceDefinition, BookingService, props.bookingServiceConfig), children: props.children }));
21
+ const { bookingServiceConfig = {}, children } = props;
22
+ return (_jsx(WixServices, { servicesMap: createServicesMap().addService(BookingServiceDefinition, BookingService, bookingServiceConfig), children: children }));
22
23
  }
23
24
  /**
24
25
  * Core component that provides access to booking data via render props.
@@ -28,7 +29,7 @@ export function Root(props) {
28
29
  * @example
29
30
  * ```tsx
30
31
  * <Booking.Data>
31
- * {({ serviceSelections, location, formSubmission, actions }) => (
32
+ * {({ serviceSelections, location, timezone, formSubmission, actions }) => (
32
33
  * serviceSelections.length > 0 ? (
33
34
  * <div>{serviceSelections.map(item => item.service.name).join(', ')}</div>
34
35
  * ) : (
@@ -43,11 +44,13 @@ export function Data(props) {
43
44
  const bookingService = useService(BookingServiceDefinition);
44
45
  const serviceSelections = bookingService.serviceSelections.get();
45
46
  const location = bookingService.location.get();
47
+ const timezone = bookingService.timezone.get();
46
48
  const formSubmission = bookingService.formSubmission.get();
47
49
  const { actions } = bookingService;
48
50
  return children({
49
51
  serviceSelections,
50
52
  location,
53
+ timezone,
51
54
  formSubmission,
52
55
  actions,
53
56
  });
@@ -88,10 +88,10 @@ export declare function StaffMemberProvider(props: StaffMemberProviderProps): Re
88
88
  */
89
89
  export interface InfoProps {
90
90
  children: (data: {
91
- /** Raw localStartDate string from timeSlot */
92
- startDate: string | undefined;
93
- /** Raw localEndDate string from timeSlot */
94
- endDate: string | undefined;
91
+ /** Parsed localStartDate as Date (constructed from the raw localStartDate string) */
92
+ startDate: Date | undefined;
93
+ /** Parsed localEndDate as Date (constructed from the raw localEndDate string) */
94
+ endDate: Date | undefined;
95
95
  /** Calculated duration in minutes (null if dates unavailable) */
96
96
  durationInMinutes: number | null;
97
97
  /** Whether the timeSlot is bookable */
@@ -9,6 +9,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
9
9
  import React from 'react';
10
10
  import { useService } from '@wix/services-manager-react';
11
11
  import { TimeSlotListServiceDefinition, } from '../../../services/time-slot-list/time-slot-list.def.js';
12
+ import { ANY_STAFF_MEMBER_ID } from '../../../services/booking/booking.js';
12
13
  const TimeSlotContext = React.createContext(null);
13
14
  /**
14
15
  * Hook to access TimeSlot context
@@ -115,15 +116,17 @@ export function StaffMemberProvider(props) {
115
116
  */
116
117
  export function Info(props) {
117
118
  const { timeSlot, isSelected } = useTimeSlotContext();
118
- const startDate = timeSlot.localStartDate ?? undefined;
119
- const endDate = timeSlot.localEndDate ?? undefined;
119
+ const startDate = timeSlot.localStartDate
120
+ ? new Date(timeSlot.localStartDate)
121
+ : undefined;
122
+ const endDate = timeSlot.localEndDate
123
+ ? new Date(timeSlot.localEndDate)
124
+ : undefined;
120
125
  const bookable = timeSlot.bookable ?? false;
121
126
  // Calculate duration from start and end dates
122
127
  let durationInMinutes = null;
123
128
  if (startDate && endDate) {
124
- const startTime = new Date(startDate).getTime();
125
- const endTime = new Date(endDate).getTime();
126
- durationInMinutes = Math.round((endTime - startTime) / (1000 * 60));
129
+ durationInMinutes = Math.round((endDate.getTime() - startDate.getTime()) / (1000 * 60));
127
130
  }
128
131
  return props.children({
129
132
  startDate,
@@ -202,12 +205,13 @@ export function Locations(props) {
202
205
  const selection = service.selection.get();
203
206
  const selectedLocationId = selection?.locationId ?? null;
204
207
  const selectedStaffMemberId = selection?.staffMemberId ?? null;
208
+ const isAnyStaffMemberSelected = selectedStaffMemberId === ANY_STAFF_MEMBER_ID;
205
209
  // Build locations array from staffByLocation (names already included)
206
210
  const locations = timeSlot.staffByLocation.map(({ location, staffMembers }) => {
207
211
  // Check if this location is selectable based on selected staff
208
212
  // If no staff selected, all locations are selectable
209
213
  // If staff selected, only locations where that staff works are selectable
210
- const isSelectable = selectedStaffMemberId
214
+ const isSelectable = !isAnyStaffMemberSelected && selectedStaffMemberId
211
215
  ? staffMembers.some((s) => s.id === selectedStaffMemberId)
212
216
  : true;
213
217
  return {
@@ -311,12 +315,18 @@ export function StaffMembers(props) {
311
315
  isSelectable: !selectedLocationId ||
312
316
  entries.some((e) => e.locationId === selectedLocationId),
313
317
  }));
318
+ const anyStaffOption = {
319
+ id: ANY_STAFF_MEMBER_ID,
320
+ name: 'Any staff member',
321
+ isSelected: selectedStaffMemberId === ANY_STAFF_MEMBER_ID,
322
+ isSelectable: true,
323
+ };
314
324
  // Return null if only 1 staff member - nothing to choose from
315
325
  if (staffMembers.length <= 1) {
316
326
  return null;
317
327
  }
318
328
  return props.children({
319
- staffMembers,
329
+ staffMembers: [anyStaffOption, ...staffMembers],
320
330
  selectStaffMember: service.actions.selectStaffMember,
321
331
  });
322
332
  }
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useService, WixServices } from '@wix/services-manager-react';
3
3
  import { createServicesMap } from '@wix/services-manager';
4
4
  import { TimeSlotListServiceDefinition, TimeSlotListService, } from '../../../services/time-slot-list/index.js';
5
+ import { BookingServiceDefinition } from '../../../services/booking/booking.js';
5
6
  /**
6
7
  * Root component that provides TimeSlotListService context to its children.
7
8
  *
@@ -54,8 +55,8 @@ function getTimezoneDisplayName(timezone) {
54
55
  * ```
55
56
  */
56
57
  export function Timezone(props) {
57
- const service = useService(TimeSlotListServiceDefinition);
58
- const timezone = service.timezone.get();
58
+ const bookingService = useService(BookingServiceDefinition);
59
+ const timezone = bookingService.timezone.get();
59
60
  const displayName = getTimezoneDisplayName(timezone);
60
61
  return props.children({ timezone, displayName });
61
62
  }
@@ -16,18 +16,18 @@ export type { LocationData, StaffMemberData, } from '../core/time-slot-list/Time
16
16
  export interface StartDateProps {
17
17
  asChild?: boolean;
18
18
  children?: AsChildChildren<{
19
- startDate: string | undefined;
19
+ startDate: Date | undefined;
20
20
  }>;
21
21
  className?: string;
22
22
  }
23
23
  /**
24
- * Displays the time slot start date/time as a raw string.
24
+ * Displays the time slot start date/time as a Date.
25
25
  * Consumer is responsible for formatting.
26
26
  *
27
27
  * @component
28
28
  * @example
29
29
  * ```tsx
30
- * // Default usage - displays raw localStartDate string
30
+ * // Default usage - displays Date.toString()
31
31
  * <TimeSlot.StartDate className="text-foreground" />
32
32
  *
33
33
  * // With custom formatting
@@ -45,18 +45,18 @@ export declare const StartDate: React.ForwardRefExoticComponent<StartDateProps &
45
45
  export interface EndDateProps {
46
46
  asChild?: boolean;
47
47
  children?: AsChildChildren<{
48
- endDate: string | undefined;
48
+ endDate: Date | undefined;
49
49
  }>;
50
50
  className?: string;
51
51
  }
52
52
  /**
53
- * Displays the time slot end date/time as a raw string.
53
+ * Displays the time slot end date/time as a Date.
54
54
  * Consumer is responsible for formatting.
55
55
  *
56
56
  * @component
57
57
  * @example
58
58
  * ```tsx
59
- * // Default usage - displays raw localEndDate string
59
+ * // Default usage - displays Date.toString()
60
60
  * <TimeSlot.EndDate className="text-foreground" />
61
61
  *
62
62
  * // With custom formatting
@@ -34,13 +34,13 @@ var TestIds;
34
34
  TestIds["timeSlotStaffMemberActionSelect"] = "time-slot-staff-member-action-select";
35
35
  })(TestIds || (TestIds = {}));
36
36
  /**
37
- * Displays the time slot start date/time as a raw string.
37
+ * Displays the time slot start date/time as a Date.
38
38
  * Consumer is responsible for formatting.
39
39
  *
40
40
  * @component
41
41
  * @example
42
42
  * ```tsx
43
- * // Default usage - displays raw localStartDate string
43
+ * // Default usage - displays Date.toString()
44
44
  * <TimeSlot.StartDate className="text-foreground" />
45
45
  *
46
46
  * // With custom formatting
@@ -53,17 +53,17 @@ var TestIds;
53
53
  */
54
54
  export const StartDate = React.forwardRef((props, ref) => {
55
55
  const { asChild, children, className } = props;
56
- return (_jsx(CoreTimeSlot.Info, { children: ({ startDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotStartDate, customElement: children, customElementProps: { startDate }, children: _jsx("span", { children: startDate }) })) }));
56
+ return (_jsx(CoreTimeSlot.Info, { children: ({ startDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotStartDate, customElement: children, customElementProps: { startDate }, children: _jsx("span", { children: startDate?.toString() }) })) }));
57
57
  });
58
58
  StartDate.displayName = 'TimeSlot.StartDate';
59
59
  /**
60
- * Displays the time slot end date/time as a raw string.
60
+ * Displays the time slot end date/time as a Date.
61
61
  * Consumer is responsible for formatting.
62
62
  *
63
63
  * @component
64
64
  * @example
65
65
  * ```tsx
66
- * // Default usage - displays raw localEndDate string
66
+ * // Default usage - displays Date.toString()
67
67
  * <TimeSlot.EndDate className="text-foreground" />
68
68
  *
69
69
  * // With custom formatting
@@ -76,7 +76,7 @@ StartDate.displayName = 'TimeSlot.StartDate';
76
76
  */
77
77
  export const EndDate = React.forwardRef((props, ref) => {
78
78
  const { asChild, children, className } = props;
79
- return (_jsx(CoreTimeSlot.Info, { children: ({ endDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotEndDate, customElement: children, customElementProps: { endDate }, children: _jsx("span", { children: endDate }) })) }));
79
+ return (_jsx(CoreTimeSlot.Info, { children: ({ endDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotEndDate, customElement: children, customElementProps: { endDate }, children: _jsx("span", { children: endDate?.toString() }) })) }));
80
80
  });
81
81
  EndDate.displayName = 'TimeSlot.EndDate';
82
82
  /**
@@ -113,10 +113,10 @@ export interface TimeSlotRepeaterChildProps {
113
113
  isSelected: boolean;
114
114
  /** Whether the slot can be booked */
115
115
  bookable: boolean;
116
- /** Start date/time as ISO string */
117
- startDate: string | undefined;
118
- /** End date/time as ISO string */
119
- endDate: string | undefined;
116
+ /** Start date/time as Date (constructed from the raw localStartDate string) */
117
+ startDate: Date | undefined;
118
+ /** End date/time as Date (constructed from the raw localEndDate string) */
119
+ endDate: Date | undefined;
120
120
  /** Duration in minutes */
121
121
  durationInMinutes: number | null;
122
122
  /** The raw time slot data */
@@ -7,6 +7,7 @@ import { type Signal } from '@wix/services-definitions/core-services/signals';
7
7
  import type { Service } from '@wix/auto_sdk_bookings_services';
8
8
  import type { Resource, EventInfo } from '@wix/auto_sdk_bookings_availability-time-slots';
9
9
  import type { FormValues } from '@wix/form-public';
10
+ export declare const ANY_STAFF_MEMBER_ID = "__any_staff_member__";
10
11
  /**
11
12
  * Minimal time slot data for service selections
12
13
  */
@@ -57,6 +58,7 @@ export interface BookingServiceActions {
57
58
  getResourceById(resourceId: string, instanceId?: string): Resource | undefined;
58
59
  setLocation(location: Location | null): void;
59
60
  clearLocation(): void;
61
+ setTimezone(timezone: string): void;
60
62
  setFormSubmission(values: FormValues | null): void;
61
63
  clearFormSubmission(): void;
62
64
  clearAll(): void;
@@ -70,6 +72,8 @@ export interface BookingService {
70
72
  serviceSelections: Signal<ServiceSelection[]>;
71
73
  /** Signal containing the selected location */
72
74
  location: Signal<Location | null>;
75
+ /** Signal containing the selected timezone */
76
+ timezone: Signal<string | undefined>;
73
77
  /** Signal containing the form submission */
74
78
  formSubmission: Signal<FormValues | null>;
75
79
  /** Actions for modifying booking state */
@@ -92,6 +96,8 @@ export interface BookingServiceConfig {
92
96
  serviceSelections?: ServiceSelection[];
93
97
  /** Optional initial location */
94
98
  location?: Location | null;
99
+ /** Optional initial timezone */
100
+ timezone?: string;
95
101
  /** Optional initial form submission */
96
102
  formSubmission?: FormValues | null;
97
103
  }