@wix/headless-bookings 0.0.36 → 0.0.37
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.
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Manages time slot fetching, selection, and state
|
|
4
4
|
*/
|
|
5
5
|
import { implementService } from '@wix/services-definitions';
|
|
6
|
-
import { SignalsServiceDefinition
|
|
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/fetch-availability/index.js';
|
|
9
9
|
import { BookingServiceDefinition, ANY_STAFF_MEMBER_ID, } from '../booking/booking.js';
|
|
@@ -15,31 +15,6 @@ function getClientTimezone() {
|
|
|
15
15
|
? Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
16
16
|
: undefined;
|
|
17
17
|
}
|
|
18
|
-
/** Compare two string arrays for equality (order-independent) */
|
|
19
|
-
function arraysEqual(a, b) {
|
|
20
|
-
if (a.length !== b.length)
|
|
21
|
-
return false;
|
|
22
|
-
const sortedA = [...a].sort();
|
|
23
|
-
const sortedB = [...b].sort();
|
|
24
|
-
return sortedA.every((val, i) => val === sortedB[i]);
|
|
25
|
-
}
|
|
26
|
-
/** Create a memoized value that only updates when IDs change */
|
|
27
|
-
function createMemoizedByIds({ fallback, getValue, extractId, }) {
|
|
28
|
-
let cachedIds = [];
|
|
29
|
-
let cachedValue = fallback;
|
|
30
|
-
return () => {
|
|
31
|
-
const value = getValue();
|
|
32
|
-
const items = value ?? [];
|
|
33
|
-
const newIds = items.length && typeof items[0] === 'string'
|
|
34
|
-
? items
|
|
35
|
-
: items.map(extractId);
|
|
36
|
-
if (!arraysEqual(newIds, cachedIds)) {
|
|
37
|
-
cachedIds = newIds;
|
|
38
|
-
cachedValue = items.length ? value : fallback;
|
|
39
|
-
}
|
|
40
|
-
return cachedValue;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
18
|
// ============================================================================
|
|
44
19
|
// Service Implementation
|
|
45
20
|
// ============================================================================
|
|
@@ -59,30 +34,25 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
59
34
|
end: config.endDate,
|
|
60
35
|
});
|
|
61
36
|
const cursorSignal = signalsService.signal(undefined);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}));
|
|
71
|
-
const filtersSignal = signalsService.computed(() => {
|
|
37
|
+
// Helper functions using peek() - no subscriptions created
|
|
38
|
+
const getServices = () => {
|
|
39
|
+
const serviceSelections = bookingService.serviceSelections.peek();
|
|
40
|
+
return serviceSelections.length
|
|
41
|
+
? serviceSelections.map((s) => s.service)
|
|
42
|
+
: (config.services ?? []);
|
|
43
|
+
};
|
|
44
|
+
const getFilters = () => {
|
|
72
45
|
const baseFilters = config.filters ?? {};
|
|
73
|
-
const selectedLocation = bookingService.location.
|
|
46
|
+
const selectedLocation = bookingService.location.peek();
|
|
74
47
|
const location = selectedLocation
|
|
75
|
-
? {
|
|
76
|
-
id: selectedLocation._id,
|
|
77
|
-
locationType: selectedLocation.type,
|
|
78
|
-
}
|
|
48
|
+
? { id: selectedLocation._id, locationType: selectedLocation.type }
|
|
79
49
|
: baseFilters.location;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
};
|
|
85
|
-
}
|
|
50
|
+
const selectedResources = bookingService.serviceSelections.peek()[0]?.resources ?? [];
|
|
51
|
+
const staffMemberIds = selectedResources.length
|
|
52
|
+
? selectedResources.map((r) => r._id)
|
|
53
|
+
: baseFilters.staffMemberIds;
|
|
54
|
+
return { ...baseFilters, location, staffMemberIds };
|
|
55
|
+
};
|
|
86
56
|
const fetchTimeSlots = async () => {
|
|
87
57
|
isLoadingSignal.set(true);
|
|
88
58
|
errorSignal.set(null);
|
|
@@ -90,12 +60,12 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
90
60
|
timeSlotsSignal.set([]);
|
|
91
61
|
try {
|
|
92
62
|
const response = await fetchAvailability({
|
|
93
|
-
services:
|
|
94
|
-
timeZone: bookingService.timezone.
|
|
95
|
-
viewMode: viewModeSignal.
|
|
96
|
-
fromLocalDate: dateRangeSignal.
|
|
97
|
-
toLocalDate: dateRangeSignal.
|
|
98
|
-
...
|
|
63
|
+
services: getServices(),
|
|
64
|
+
timeZone: bookingService.timezone.peek(),
|
|
65
|
+
viewMode: viewModeSignal.peek(),
|
|
66
|
+
fromLocalDate: dateRangeSignal.peek().start,
|
|
67
|
+
toLocalDate: dateRangeSignal.peek().end,
|
|
68
|
+
...getFilters(),
|
|
99
69
|
});
|
|
100
70
|
timeSlotsSignal.set(response.timeSlots);
|
|
101
71
|
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
@@ -143,13 +113,13 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
143
113
|
* Updates the booking service with the selected resource
|
|
144
114
|
*/
|
|
145
115
|
const selectStaffMember = (staffMemberId) => {
|
|
146
|
-
const selectedTimeSlot = bookingService.serviceSelections.
|
|
116
|
+
const selectedTimeSlot = bookingService.serviceSelections.peek()[0]?.timeSlot;
|
|
147
117
|
if (!selectedTimeSlot)
|
|
148
118
|
return;
|
|
149
119
|
const getStaffName = (id) => {
|
|
150
120
|
if (id === ANY_STAFF_MEMBER_ID)
|
|
151
121
|
return 'Any staff member';
|
|
152
|
-
const slots = timeSlotsSignal.
|
|
122
|
+
const slots = timeSlotsSignal.peek();
|
|
153
123
|
return (slots
|
|
154
124
|
.flatMap((slot) => slot.availableResources[0].resources)
|
|
155
125
|
.find((r) => r._id === id)?.name ?? undefined);
|
|
@@ -171,7 +141,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
171
141
|
* Action: Clear staff selection while keeping the selected time slot
|
|
172
142
|
*/
|
|
173
143
|
const clearStaffSelection = () => {
|
|
174
|
-
const selectedTimeSlot = bookingService.serviceSelections.
|
|
144
|
+
const selectedTimeSlot = bookingService.serviceSelections.peek()[0]?.timeSlot;
|
|
175
145
|
if (!selectedTimeSlot)
|
|
176
146
|
return;
|
|
177
147
|
bookingService.actions.updateItem({
|
|
@@ -182,7 +152,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
182
152
|
* Action: Load more slots (pagination)
|
|
183
153
|
*/
|
|
184
154
|
const loadMore = async () => {
|
|
185
|
-
const cursor = cursorSignal.
|
|
155
|
+
const cursor = cursorSignal.peek();
|
|
186
156
|
if (!cursor) {
|
|
187
157
|
return;
|
|
188
158
|
}
|
|
@@ -190,16 +160,16 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
190
160
|
errorSignal.set(null);
|
|
191
161
|
try {
|
|
192
162
|
const response = await fetchAvailability({
|
|
193
|
-
services:
|
|
194
|
-
fromLocalDate: dateRangeSignal.
|
|
195
|
-
toLocalDate: dateRangeSignal.
|
|
196
|
-
timeZone: bookingService.timezone.
|
|
197
|
-
viewMode: viewModeSignal.
|
|
163
|
+
services: getServices(),
|
|
164
|
+
fromLocalDate: dateRangeSignal.peek().start,
|
|
165
|
+
toLocalDate: dateRangeSignal.peek().end,
|
|
166
|
+
timeZone: bookingService.timezone.peek(),
|
|
167
|
+
viewMode: viewModeSignal.peek(),
|
|
198
168
|
cursor,
|
|
199
|
-
...
|
|
169
|
+
...getFilters(),
|
|
200
170
|
});
|
|
201
171
|
const newSlots = response.timeSlots;
|
|
202
|
-
timeSlotsSignal.set([...timeSlotsSignal.
|
|
172
|
+
timeSlotsSignal.set([...timeSlotsSignal.peek(), ...newSlots]);
|
|
203
173
|
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
204
174
|
cursorSignal.set(nextCursor);
|
|
205
175
|
hasMoreSignal.set(!!nextCursor);
|
|
@@ -218,7 +188,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
218
188
|
* Action: Update date range (effect will handle refetch)
|
|
219
189
|
*/
|
|
220
190
|
const setDateRange = (newStartDate, newEndDate) => {
|
|
221
|
-
const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.
|
|
191
|
+
const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.peek();
|
|
222
192
|
if (currentStartDate.getTime() === newStartDate.getTime() &&
|
|
223
193
|
currentEndDate.getTime() === newEndDate.getTime()) {
|
|
224
194
|
return;
|
|
@@ -227,9 +197,10 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
227
197
|
};
|
|
228
198
|
let isFirstRun = true;
|
|
229
199
|
signalsService.effect(() => {
|
|
230
|
-
|
|
200
|
+
// Dependencies - trigger refetch when these change
|
|
231
201
|
dateRangeSignal.get();
|
|
232
202
|
bookingService.timezone.get();
|
|
203
|
+
bookingService.location.get();
|
|
233
204
|
if (isFirstRun) {
|
|
234
205
|
isFirstRun = false;
|
|
235
206
|
if (config.timeSlots) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Manages time slot fetching, selection, and state
|
|
4
4
|
*/
|
|
5
5
|
import { implementService } from '@wix/services-definitions';
|
|
6
|
-
import { SignalsServiceDefinition
|
|
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/fetch-availability/index.js';
|
|
9
9
|
import { BookingServiceDefinition, ANY_STAFF_MEMBER_ID, } from '../booking/booking.js';
|
|
@@ -15,31 +15,6 @@ function getClientTimezone() {
|
|
|
15
15
|
? Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
16
16
|
: undefined;
|
|
17
17
|
}
|
|
18
|
-
/** Compare two string arrays for equality (order-independent) */
|
|
19
|
-
function arraysEqual(a, b) {
|
|
20
|
-
if (a.length !== b.length)
|
|
21
|
-
return false;
|
|
22
|
-
const sortedA = [...a].sort();
|
|
23
|
-
const sortedB = [...b].sort();
|
|
24
|
-
return sortedA.every((val, i) => val === sortedB[i]);
|
|
25
|
-
}
|
|
26
|
-
/** Create a memoized value that only updates when IDs change */
|
|
27
|
-
function createMemoizedByIds({ fallback, getValue, extractId, }) {
|
|
28
|
-
let cachedIds = [];
|
|
29
|
-
let cachedValue = fallback;
|
|
30
|
-
return () => {
|
|
31
|
-
const value = getValue();
|
|
32
|
-
const items = value ?? [];
|
|
33
|
-
const newIds = items.length && typeof items[0] === 'string'
|
|
34
|
-
? items
|
|
35
|
-
: items.map(extractId);
|
|
36
|
-
if (!arraysEqual(newIds, cachedIds)) {
|
|
37
|
-
cachedIds = newIds;
|
|
38
|
-
cachedValue = items.length ? value : fallback;
|
|
39
|
-
}
|
|
40
|
-
return cachedValue;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
18
|
// ============================================================================
|
|
44
19
|
// Service Implementation
|
|
45
20
|
// ============================================================================
|
|
@@ -59,30 +34,25 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
59
34
|
end: config.endDate,
|
|
60
35
|
});
|
|
61
36
|
const cursorSignal = signalsService.signal(undefined);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}));
|
|
71
|
-
const filtersSignal = signalsService.computed(() => {
|
|
37
|
+
// Helper functions using peek() - no subscriptions created
|
|
38
|
+
const getServices = () => {
|
|
39
|
+
const serviceSelections = bookingService.serviceSelections.peek();
|
|
40
|
+
return serviceSelections.length
|
|
41
|
+
? serviceSelections.map((s) => s.service)
|
|
42
|
+
: (config.services ?? []);
|
|
43
|
+
};
|
|
44
|
+
const getFilters = () => {
|
|
72
45
|
const baseFilters = config.filters ?? {};
|
|
73
|
-
const selectedLocation = bookingService.location.
|
|
46
|
+
const selectedLocation = bookingService.location.peek();
|
|
74
47
|
const location = selectedLocation
|
|
75
|
-
? {
|
|
76
|
-
id: selectedLocation._id,
|
|
77
|
-
locationType: selectedLocation.type,
|
|
78
|
-
}
|
|
48
|
+
? { id: selectedLocation._id, locationType: selectedLocation.type }
|
|
79
49
|
: baseFilters.location;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
};
|
|
85
|
-
}
|
|
50
|
+
const selectedResources = bookingService.serviceSelections.peek()[0]?.resources ?? [];
|
|
51
|
+
const staffMemberIds = selectedResources.length
|
|
52
|
+
? selectedResources.map((r) => r._id)
|
|
53
|
+
: baseFilters.staffMemberIds;
|
|
54
|
+
return { ...baseFilters, location, staffMemberIds };
|
|
55
|
+
};
|
|
86
56
|
const fetchTimeSlots = async () => {
|
|
87
57
|
isLoadingSignal.set(true);
|
|
88
58
|
errorSignal.set(null);
|
|
@@ -90,12 +60,12 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
90
60
|
timeSlotsSignal.set([]);
|
|
91
61
|
try {
|
|
92
62
|
const response = await fetchAvailability({
|
|
93
|
-
services:
|
|
94
|
-
timeZone: bookingService.timezone.
|
|
95
|
-
viewMode: viewModeSignal.
|
|
96
|
-
fromLocalDate: dateRangeSignal.
|
|
97
|
-
toLocalDate: dateRangeSignal.
|
|
98
|
-
...
|
|
63
|
+
services: getServices(),
|
|
64
|
+
timeZone: bookingService.timezone.peek(),
|
|
65
|
+
viewMode: viewModeSignal.peek(),
|
|
66
|
+
fromLocalDate: dateRangeSignal.peek().start,
|
|
67
|
+
toLocalDate: dateRangeSignal.peek().end,
|
|
68
|
+
...getFilters(),
|
|
99
69
|
});
|
|
100
70
|
timeSlotsSignal.set(response.timeSlots);
|
|
101
71
|
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
@@ -143,13 +113,13 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
143
113
|
* Updates the booking service with the selected resource
|
|
144
114
|
*/
|
|
145
115
|
const selectStaffMember = (staffMemberId) => {
|
|
146
|
-
const selectedTimeSlot = bookingService.serviceSelections.
|
|
116
|
+
const selectedTimeSlot = bookingService.serviceSelections.peek()[0]?.timeSlot;
|
|
147
117
|
if (!selectedTimeSlot)
|
|
148
118
|
return;
|
|
149
119
|
const getStaffName = (id) => {
|
|
150
120
|
if (id === ANY_STAFF_MEMBER_ID)
|
|
151
121
|
return 'Any staff member';
|
|
152
|
-
const slots = timeSlotsSignal.
|
|
122
|
+
const slots = timeSlotsSignal.peek();
|
|
153
123
|
return (slots
|
|
154
124
|
.flatMap((slot) => slot.availableResources[0].resources)
|
|
155
125
|
.find((r) => r._id === id)?.name ?? undefined);
|
|
@@ -171,7 +141,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
171
141
|
* Action: Clear staff selection while keeping the selected time slot
|
|
172
142
|
*/
|
|
173
143
|
const clearStaffSelection = () => {
|
|
174
|
-
const selectedTimeSlot = bookingService.serviceSelections.
|
|
144
|
+
const selectedTimeSlot = bookingService.serviceSelections.peek()[0]?.timeSlot;
|
|
175
145
|
if (!selectedTimeSlot)
|
|
176
146
|
return;
|
|
177
147
|
bookingService.actions.updateItem({
|
|
@@ -182,7 +152,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
182
152
|
* Action: Load more slots (pagination)
|
|
183
153
|
*/
|
|
184
154
|
const loadMore = async () => {
|
|
185
|
-
const cursor = cursorSignal.
|
|
155
|
+
const cursor = cursorSignal.peek();
|
|
186
156
|
if (!cursor) {
|
|
187
157
|
return;
|
|
188
158
|
}
|
|
@@ -190,16 +160,16 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
190
160
|
errorSignal.set(null);
|
|
191
161
|
try {
|
|
192
162
|
const response = await fetchAvailability({
|
|
193
|
-
services:
|
|
194
|
-
fromLocalDate: dateRangeSignal.
|
|
195
|
-
toLocalDate: dateRangeSignal.
|
|
196
|
-
timeZone: bookingService.timezone.
|
|
197
|
-
viewMode: viewModeSignal.
|
|
163
|
+
services: getServices(),
|
|
164
|
+
fromLocalDate: dateRangeSignal.peek().start,
|
|
165
|
+
toLocalDate: dateRangeSignal.peek().end,
|
|
166
|
+
timeZone: bookingService.timezone.peek(),
|
|
167
|
+
viewMode: viewModeSignal.peek(),
|
|
198
168
|
cursor,
|
|
199
|
-
...
|
|
169
|
+
...getFilters(),
|
|
200
170
|
});
|
|
201
171
|
const newSlots = response.timeSlots;
|
|
202
|
-
timeSlotsSignal.set([...timeSlotsSignal.
|
|
172
|
+
timeSlotsSignal.set([...timeSlotsSignal.peek(), ...newSlots]);
|
|
203
173
|
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
204
174
|
cursorSignal.set(nextCursor);
|
|
205
175
|
hasMoreSignal.set(!!nextCursor);
|
|
@@ -218,7 +188,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
218
188
|
* Action: Update date range (effect will handle refetch)
|
|
219
189
|
*/
|
|
220
190
|
const setDateRange = (newStartDate, newEndDate) => {
|
|
221
|
-
const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.
|
|
191
|
+
const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.peek();
|
|
222
192
|
if (currentStartDate.getTime() === newStartDate.getTime() &&
|
|
223
193
|
currentEndDate.getTime() === newEndDate.getTime()) {
|
|
224
194
|
return;
|
|
@@ -227,9 +197,10 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
227
197
|
};
|
|
228
198
|
let isFirstRun = true;
|
|
229
199
|
signalsService.effect(() => {
|
|
230
|
-
|
|
200
|
+
// Dependencies - trigger refetch when these change
|
|
231
201
|
dateRangeSignal.get();
|
|
232
202
|
bookingService.timezone.get();
|
|
203
|
+
bookingService.location.get();
|
|
233
204
|
if (isFirstRun) {
|
|
234
205
|
isFirstRun = false;
|
|
235
206
|
if (config.timeSlots) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-bookings",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.37",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"groupId": "com.wixpress.headless-components"
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
|
-
"falconPackageHash": "
|
|
75
|
+
"falconPackageHash": "20f4812ace043e766a30c4a89597f6ca06981a999aff716744e5b9fc"
|
|
76
76
|
}
|