@wix/headless-bookings 0.0.15 → 0.0.17
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.
- package/cjs/dist/api/time-slot-list/index.d.ts +21 -1
- package/cjs/dist/api/time-slot-list/index.js +46 -8
- package/cjs/dist/react/core/booking/Booking.d.ts +17 -9
- package/cjs/dist/react/core/booking/Booking.js +13 -7
- package/cjs/dist/react/core/time-slot-list/TimeSlot.d.ts +22 -18
- package/cjs/dist/react/core/time-slot-list/TimeSlot.js +41 -28
- package/cjs/dist/react/core/time-slot-list/TimeSlotList.d.ts +39 -7
- package/cjs/dist/react/core/time-slot-list/TimeSlotList.js +56 -11
- package/cjs/dist/react/time-slot-list/TimeSlot.js +3 -3
- package/cjs/dist/react/time-slot-list/TimeSlotList.d.ts +55 -12
- package/cjs/dist/react/time-slot-list/TimeSlotList.js +47 -17
- package/cjs/dist/services/booking/booking.d.ts +48 -30
- package/cjs/dist/services/booking/booking.js +74 -44
- package/cjs/dist/services/index.d.ts +3 -3
- package/cjs/dist/services/service/service.js +10 -7
- package/cjs/dist/services/time-slot-list/index.d.ts +1 -1
- package/cjs/dist/services/time-slot-list/time-slot-list.d.ts +3 -3
- package/cjs/dist/services/time-slot-list/time-slot-list.def.d.ts +23 -4
- package/cjs/dist/services/time-slot-list/time-slot-list.js +146 -65
- package/dist/api/time-slot-list/index.d.ts +21 -1
- package/dist/api/time-slot-list/index.js +46 -8
- package/dist/react/core/booking/Booking.d.ts +17 -9
- package/dist/react/core/booking/Booking.js +13 -7
- package/dist/react/core/time-slot-list/TimeSlot.d.ts +22 -18
- package/dist/react/core/time-slot-list/TimeSlot.js +41 -28
- package/dist/react/core/time-slot-list/TimeSlotList.d.ts +39 -7
- package/dist/react/core/time-slot-list/TimeSlotList.js +56 -11
- package/dist/react/time-slot-list/TimeSlot.js +3 -3
- package/dist/react/time-slot-list/TimeSlotList.d.ts +55 -12
- package/dist/react/time-slot-list/TimeSlotList.js +47 -17
- package/dist/services/booking/booking.d.ts +48 -30
- package/dist/services/booking/booking.js +74 -44
- package/dist/services/index.d.ts +3 -3
- package/dist/services/service/service.js +10 -7
- package/dist/services/time-slot-list/index.d.ts +1 -1
- package/dist/services/time-slot-list/time-slot-list.d.ts +3 -3
- package/dist/services/time-slot-list/time-slot-list.def.d.ts +23 -4
- package/dist/services/time-slot-list/time-slot-list.js +146 -65
- package/package.json +3 -2
|
@@ -6,7 +6,7 @@ import { implementService } from '@wix/services-definitions';
|
|
|
6
6
|
import { SignalsServiceDefinition } from '@wix/services-definitions/core-services/signals';
|
|
7
7
|
import { TimeSlotListServiceDefinition, ViewMode, } from './time-slot-list.def.js';
|
|
8
8
|
import { fetchAvailability } from '../../api/time-slot-list/index.js';
|
|
9
|
-
import { BookingServiceDefinition } from '../booking/booking.js';
|
|
9
|
+
import { BookingServiceDefinition, } from '../booking/booking.js';
|
|
10
10
|
// ============================================================================
|
|
11
11
|
// Helpers
|
|
12
12
|
// ============================================================================
|
|
@@ -68,7 +68,8 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
68
68
|
const isLoadingSignal = signalsService.signal(false);
|
|
69
69
|
const errorSignal = signalsService.signal(null);
|
|
70
70
|
const hasMoreSignal = signalsService.signal(false);
|
|
71
|
-
|
|
71
|
+
// Internal only - viewMode is auto-detected based on service types
|
|
72
|
+
const viewModeSignal = signalsService.signal(ViewMode.CLASSIC);
|
|
72
73
|
const startDateSignal = signalsService.signal(config.startDate || currentDate);
|
|
73
74
|
const endDateSignal = signalsService.signal(config.endDate || getDefaultEndDate());
|
|
74
75
|
const timezoneSignal = signalsService.signal(config.timezone ?? getClientTimezone());
|
|
@@ -79,7 +80,29 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
79
80
|
const getServices = () => {
|
|
80
81
|
if (config.services?.length)
|
|
81
82
|
return config.services;
|
|
82
|
-
return bookingService.
|
|
83
|
+
return bookingService.bookingItems.get().map((s) => s.service);
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Helper: Get filters from config or BookingService
|
|
87
|
+
*/
|
|
88
|
+
const getFilters = () => {
|
|
89
|
+
// Get BookingService selections (these take priority)
|
|
90
|
+
const location = bookingService.location.get();
|
|
91
|
+
const selections = bookingService.bookingItems.get();
|
|
92
|
+
const selectedStaffMemberIds = selections
|
|
93
|
+
.map(({ resources }) => resources.map((r) => r._id))
|
|
94
|
+
.flat();
|
|
95
|
+
const selectedLocation = location ? { _id: location.id } : null;
|
|
96
|
+
// Merge config filters with BookingService selections (selections win)
|
|
97
|
+
return {
|
|
98
|
+
...config.filters,
|
|
99
|
+
locations: selectedLocation
|
|
100
|
+
? [selectedLocation]
|
|
101
|
+
: config.filters?.locations,
|
|
102
|
+
staffMemberIds: selectedStaffMemberIds.length
|
|
103
|
+
? selectedStaffMemberIds
|
|
104
|
+
: config.filters?.staffMemberIds,
|
|
105
|
+
};
|
|
83
106
|
};
|
|
84
107
|
/**
|
|
85
108
|
* Fetch time slots from API
|
|
@@ -87,6 +110,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
87
110
|
*/
|
|
88
111
|
const fetchTimeSlots = async () => {
|
|
89
112
|
const selectedServices = getServices();
|
|
113
|
+
const filters = getFilters();
|
|
90
114
|
isLoadingSignal.set(true);
|
|
91
115
|
errorSignal.set(null);
|
|
92
116
|
cursorSignal.set(undefined);
|
|
@@ -97,6 +121,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
97
121
|
toLocalDate: toLocalDateString(endDateSignal.get()),
|
|
98
122
|
timeZone: timezoneSignal.get(),
|
|
99
123
|
viewMode: viewModeSignal.get(),
|
|
124
|
+
...filters,
|
|
100
125
|
});
|
|
101
126
|
timeSlotsSignal.set(response.timeSlots);
|
|
102
127
|
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
@@ -115,82 +140,139 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
115
140
|
};
|
|
116
141
|
/**
|
|
117
142
|
* Action: Select a time slot
|
|
118
|
-
*
|
|
119
|
-
*
|
|
143
|
+
* Auto-selects location/staff based on availability:
|
|
144
|
+
* - One location + one staff → auto-select both
|
|
145
|
+
* - One location + many staff → auto-select location only
|
|
146
|
+
* - Many locations + one staff → auto-select staff only
|
|
147
|
+
* - Many locations + many staff → don't auto-select anything
|
|
120
148
|
*/
|
|
121
149
|
const selectSlot = (slot) => {
|
|
122
150
|
const currentSelection = selectionSignal.get();
|
|
123
151
|
if (currentSelection?.slot?.id === slot.id)
|
|
124
152
|
return;
|
|
153
|
+
const timeSlot = {
|
|
154
|
+
startDate: slot.localStartDate,
|
|
155
|
+
endDate: slot.localEndDate,
|
|
156
|
+
totalCapacity: slot.totalCapacity ?? undefined,
|
|
157
|
+
remainingCapacity: slot.remainingCapacity ?? undefined,
|
|
158
|
+
scheduleId: slot.scheduleId ?? undefined,
|
|
159
|
+
eventInfo: slot.eventInfo ?? undefined,
|
|
160
|
+
};
|
|
125
161
|
const hasOneLocation = slot.staffByLocation.length === 1;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
bookingService.actions.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
// Collect all unique staff members across all locations
|
|
163
|
+
const allStaffMembers = slot.staffByLocation.flatMap((loc) => loc.staffMembers);
|
|
164
|
+
const uniqueStaffIds = new Set(allStaffMembers.map((s) => s.id));
|
|
165
|
+
const hasOneStaff = uniqueStaffIds.size === 1;
|
|
166
|
+
// Determine what to auto-select
|
|
167
|
+
let locationId = null;
|
|
168
|
+
let staffMemberId = null;
|
|
169
|
+
if (hasOneLocation) {
|
|
170
|
+
const { location } = slot.staffByLocation[0];
|
|
171
|
+
locationId = location.id;
|
|
172
|
+
bookingService.actions.setLocation(location);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
bookingService.actions.clearLocation();
|
|
176
|
+
}
|
|
177
|
+
if (hasOneStaff) {
|
|
178
|
+
const staffMember = allStaffMembers[0];
|
|
179
|
+
staffMemberId = staffMember.id;
|
|
180
|
+
bookingService.actions.updateItem({
|
|
181
|
+
timeSlot,
|
|
182
|
+
resources: [
|
|
183
|
+
{
|
|
184
|
+
_id: staffMemberId,
|
|
185
|
+
name: staffMember.name,
|
|
186
|
+
},
|
|
187
|
+
],
|
|
146
188
|
});
|
|
147
189
|
}
|
|
148
190
|
else {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
staffMemberId: null,
|
|
191
|
+
bookingService.actions.updateItem({
|
|
192
|
+
timeSlot,
|
|
193
|
+
resources: [],
|
|
153
194
|
});
|
|
154
195
|
}
|
|
196
|
+
selectionSignal.set({
|
|
197
|
+
slot,
|
|
198
|
+
locationId,
|
|
199
|
+
staffMemberId,
|
|
200
|
+
});
|
|
155
201
|
};
|
|
156
202
|
/**
|
|
157
203
|
* Action: Select a location for the current slot
|
|
158
|
-
* Updates the booking service with the
|
|
204
|
+
* Updates the booking service with the location
|
|
205
|
+
* If the location has only one staff member, auto-selects that staff member too
|
|
159
206
|
*/
|
|
160
207
|
const selectLocation = (locationId) => {
|
|
161
208
|
const currentSelections = selectionSignal.get();
|
|
162
209
|
if (!currentSelections?.slot)
|
|
163
210
|
return;
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
if (
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
211
|
+
const slot = currentSelections.slot;
|
|
212
|
+
const locationEntry = slot.staffByLocation.find((loc) => loc.location.id === locationId);
|
|
213
|
+
if (!locationEntry)
|
|
214
|
+
return;
|
|
215
|
+
const { location, staffMembers } = locationEntry;
|
|
216
|
+
bookingService.actions.setLocation(location);
|
|
217
|
+
// If this location has only one staff member, auto-select them
|
|
218
|
+
const hasOneStaff = staffMembers.length === 1;
|
|
219
|
+
if (hasOneStaff) {
|
|
220
|
+
const staffMember = staffMembers[0];
|
|
221
|
+
selectionSignal.set({
|
|
222
|
+
...currentSelections,
|
|
223
|
+
locationId,
|
|
224
|
+
staffMemberId: staffMember.id,
|
|
225
|
+
});
|
|
226
|
+
bookingService.actions.updateItem({
|
|
227
|
+
resources: [
|
|
228
|
+
{
|
|
229
|
+
_id: staffMember.id,
|
|
230
|
+
name: staffMember.name,
|
|
231
|
+
},
|
|
232
|
+
],
|
|
170
233
|
});
|
|
171
234
|
}
|
|
235
|
+
else {
|
|
236
|
+
selectionSignal.set({ ...currentSelections, locationId });
|
|
237
|
+
}
|
|
172
238
|
};
|
|
173
239
|
/**
|
|
174
240
|
* Action: Select a staff member for the current slot
|
|
175
|
-
* Updates the booking service with the selected resource
|
|
241
|
+
* Updates the booking service with the selected resource
|
|
242
|
+
* If the staff member is only available at one location, auto-selects that location too
|
|
176
243
|
*/
|
|
177
244
|
const selectStaffMember = (staffMemberId) => {
|
|
178
245
|
const currentSelections = selectionSignal.get();
|
|
179
246
|
if (!currentSelections?.slot)
|
|
180
247
|
return;
|
|
181
|
-
selectionSignal.set({ ...currentSelections, staffMemberId });
|
|
182
248
|
const slot = currentSelections.slot;
|
|
183
|
-
// Find
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
249
|
+
// Find all locations that have this staff member
|
|
250
|
+
const locationsWithThisStaff = slot.staffByLocation.filter((loc) => loc.staffMembers.some((s) => s.id === staffMemberId));
|
|
251
|
+
// Find the staff member info
|
|
252
|
+
const staffMember = locationsWithThisStaff[0]?.staffMembers.find((s) => s.id === staffMemberId);
|
|
253
|
+
if (!staffMember)
|
|
254
|
+
return;
|
|
255
|
+
bookingService.actions.updateItem({
|
|
256
|
+
resources: [
|
|
257
|
+
{
|
|
258
|
+
_id: staffMember.id,
|
|
259
|
+
name: staffMember.name,
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
});
|
|
263
|
+
// If this staff member is only at one location, auto-select that location
|
|
264
|
+
const hasOneLocation = locationsWithThisStaff.length === 1;
|
|
265
|
+
if (hasOneLocation) {
|
|
266
|
+
const location = locationsWithThisStaff[0].location;
|
|
267
|
+
selectionSignal.set({
|
|
268
|
+
...currentSelections,
|
|
269
|
+
staffMemberId,
|
|
270
|
+
locationId: location.id,
|
|
271
|
+
});
|
|
272
|
+
bookingService.actions.setLocation(location);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
selectionSignal.set({ ...currentSelections, staffMemberId });
|
|
194
276
|
}
|
|
195
277
|
};
|
|
196
278
|
/**
|
|
@@ -206,15 +288,11 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
206
288
|
locationId: null,
|
|
207
289
|
staffMemberId: null,
|
|
208
290
|
});
|
|
209
|
-
// Clear
|
|
291
|
+
// Clear selections in booking service
|
|
210
292
|
bookingService.actions.clearLocation();
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
bookingService.actions.updateBookingSelection({
|
|
215
|
-
staff: undefined,
|
|
216
|
-
});
|
|
217
|
-
}
|
|
293
|
+
bookingService.actions.updateItem({
|
|
294
|
+
resources: [],
|
|
295
|
+
});
|
|
218
296
|
};
|
|
219
297
|
/**
|
|
220
298
|
* Action: Load more slots (pagination)
|
|
@@ -226,6 +304,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
226
304
|
const selectedServices = getServices();
|
|
227
305
|
if (selectedServices.length === 0)
|
|
228
306
|
return;
|
|
307
|
+
const filters = getFilters();
|
|
229
308
|
isLoadingSignal.set(true);
|
|
230
309
|
try {
|
|
231
310
|
const response = await fetchAvailability({
|
|
@@ -235,6 +314,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
235
314
|
timeZone: timezoneSignal.get(),
|
|
236
315
|
cursorPaging: { cursor },
|
|
237
316
|
viewMode: viewModeSignal.get(),
|
|
317
|
+
...filters,
|
|
238
318
|
});
|
|
239
319
|
// API returns grouped slots, merge with existing
|
|
240
320
|
const existing = timeSlotsSignal.get();
|
|
@@ -257,6 +337,12 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
257
337
|
* Action: Update date range and refetch slots
|
|
258
338
|
*/
|
|
259
339
|
const setDateRange = (newStartDate, newEndDate) => {
|
|
340
|
+
const currentStartDate = startDateSignal.get();
|
|
341
|
+
const currentEndDate = endDateSignal.get();
|
|
342
|
+
if (currentStartDate.getTime() === newStartDate.getTime() &&
|
|
343
|
+
currentEndDate.getTime() === newEndDate.getTime()) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
260
346
|
startDateSignal.set(newStartDate);
|
|
261
347
|
endDateSignal.set(newEndDate);
|
|
262
348
|
fetchTimeSlots();
|
|
@@ -280,8 +366,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
280
366
|
isLoading: isLoadingSignal,
|
|
281
367
|
error: errorSignal,
|
|
282
368
|
hasMore: hasMoreSignal,
|
|
283
|
-
// Configuration Signals
|
|
284
|
-
viewMode: viewModeSignal,
|
|
369
|
+
// Configuration Signals (viewMode is internal only)
|
|
285
370
|
startDate: startDateSignal,
|
|
286
371
|
endDate: endDateSignal,
|
|
287
372
|
timezone: timezoneSignal,
|
|
@@ -322,23 +407,19 @@ export async function loadTimeSlotListConfig(options) {
|
|
|
322
407
|
const now = new Date();
|
|
323
408
|
const startDate = options.startDate || now;
|
|
324
409
|
const endDate = options.endDate || getDefaultEndDate();
|
|
325
|
-
const viewMode = options.viewMode || ViewMode.CLASSIC;
|
|
326
|
-
// check for location and staff in config
|
|
327
410
|
try {
|
|
328
411
|
const response = await fetchAvailability({
|
|
329
412
|
services: options.services || [],
|
|
330
413
|
fromLocalDate: toLocalDateString(startDate),
|
|
331
414
|
toLocalDate: toLocalDateString(endDate),
|
|
332
415
|
timeZone: options.timezone ?? getClientTimezone(),
|
|
333
|
-
|
|
416
|
+
...options.filters,
|
|
334
417
|
});
|
|
335
418
|
const config = {
|
|
336
|
-
|
|
419
|
+
...options,
|
|
337
420
|
timeSlots: response.timeSlots || [],
|
|
338
421
|
startDate,
|
|
339
422
|
endDate,
|
|
340
|
-
timezone: options.timezone,
|
|
341
|
-
viewMode,
|
|
342
423
|
};
|
|
343
424
|
return { type: 'success', config };
|
|
344
425
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-bookings",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
+
"@wix/auto_sdk_bookings_event-time-slots": "^1.0.64",
|
|
36
37
|
"@wix/bookings": "^1.0.122",
|
|
37
38
|
"@wix/form-public": "^0.64.0",
|
|
38
39
|
"@wix/headless-media": "0.0.18",
|
|
@@ -68,5 +69,5 @@
|
|
|
68
69
|
"groupId": "com.wixpress.headless-components"
|
|
69
70
|
}
|
|
70
71
|
},
|
|
71
|
-
"falconPackageHash": "
|
|
72
|
+
"falconPackageHash": "8c910b633cde19227cb0209fec691701a57a7e53b577ee76971bfa2a"
|
|
72
73
|
}
|