@wix/headless-bookings 0.0.35 → 0.0.36
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/react/core/time-slot-list/TimeSlotList.js +3 -2
- package/cjs/dist/services/time-slot-list/index.d.ts +1 -1
- package/cjs/dist/services/time-slot-list/time-slot-list.def.d.ts +5 -2
- package/cjs/dist/services/time-slot-list/time-slot-list.js +93 -66
- package/dist/react/core/time-slot-list/TimeSlotList.js +3 -2
- package/dist/services/time-slot-list/index.d.ts +1 -1
- package/dist/services/time-slot-list/time-slot-list.def.d.ts +5 -2
- package/dist/services/time-slot-list/time-slot-list.js +93 -66
- package/package.json +2 -2
|
@@ -106,9 +106,10 @@ export function ListData(props) {
|
|
|
106
106
|
*/
|
|
107
107
|
export function DateRange(props) {
|
|
108
108
|
const service = useService(TimeSlotListServiceDefinition);
|
|
109
|
+
const dateRange = service.dateRange.get();
|
|
109
110
|
return props.children({
|
|
110
|
-
startDate:
|
|
111
|
-
endDate:
|
|
111
|
+
startDate: dateRange.start,
|
|
112
|
+
endDate: dateRange.end,
|
|
112
113
|
setDateRange: service.actions.setDateRange,
|
|
113
114
|
});
|
|
114
115
|
}
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* TimeSlotList Service
|
|
3
3
|
* Main export file for time slot list service
|
|
4
4
|
*/
|
|
5
|
-
export { TimeSlotListServiceDefinition, ViewMode, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type TimeSlotsFilters, } from './time-slot-list.def.js';
|
|
5
|
+
export { TimeSlotListServiceDefinition, ViewMode, type DateRange, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type TimeSlotsFilters, } from './time-slot-list.def.js';
|
|
6
6
|
export { TimeSlotListService, loadTimeSlotListConfig, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list.js';
|
|
@@ -77,13 +77,16 @@ export interface TimeSlotListServiceActions {
|
|
|
77
77
|
loadMore: () => Promise<void>;
|
|
78
78
|
setDateRange: (start: Date, end: Date) => void;
|
|
79
79
|
}
|
|
80
|
+
export interface DateRange {
|
|
81
|
+
start: Date;
|
|
82
|
+
end: Date;
|
|
83
|
+
}
|
|
80
84
|
export interface TimeSlotListServiceAPI {
|
|
81
85
|
timeSlots: Signal<TimeSlot[]>;
|
|
82
86
|
isLoading: Signal<boolean>;
|
|
83
87
|
error: Signal<string | null>;
|
|
84
88
|
hasMore: Signal<boolean>;
|
|
85
|
-
|
|
86
|
-
endDate: Signal<Date>;
|
|
89
|
+
dateRange: Signal<DateRange>;
|
|
87
90
|
actions: TimeSlotListServiceActions;
|
|
88
91
|
}
|
|
89
92
|
export declare const TimeSlotListServiceDefinition: string & {
|
|
@@ -15,6 +15,31 @@ 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
|
+
}
|
|
18
43
|
// ============================================================================
|
|
19
44
|
// Service Implementation
|
|
20
45
|
// ============================================================================
|
|
@@ -29,33 +54,22 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
29
54
|
const hasMoreSignal = signalsService.signal(false);
|
|
30
55
|
// Internal only - viewMode is auto-detected based on service types
|
|
31
56
|
const viewModeSignal = signalsService.signal(ViewMode.CLASSIC);
|
|
32
|
-
const
|
|
33
|
-
|
|
57
|
+
const dateRangeSignal = signalsService.signal({
|
|
58
|
+
start: config.startDate,
|
|
59
|
+
end: config.endDate,
|
|
60
|
+
});
|
|
34
61
|
const cursorSignal = signalsService.signal(undefined);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return config.services ?? [];
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* Helper: Get filters combining config with booking overrides
|
|
49
|
-
* Uses location and resources from booking if they exist (not from the slot)
|
|
50
|
-
*/
|
|
51
|
-
const getFilters = () => {
|
|
62
|
+
const servicesSignal = signalsService.computed(createMemoizedByIds({
|
|
63
|
+
fallback: config.services ?? [],
|
|
64
|
+
getValue: () => bookingService.serviceSelections.get().map((s) => s.service),
|
|
65
|
+
extractId: (service) => service._id,
|
|
66
|
+
}));
|
|
67
|
+
const preSelectedStaffSignal = signalsService.computed(createMemoizedByIds({
|
|
68
|
+
fallback: config.filters?.staffMemberIds,
|
|
69
|
+
getValue: () => (bookingService.serviceSelections.get()[0]?.resources ?? []).map((resource) => resource._id),
|
|
70
|
+
}));
|
|
71
|
+
const filtersSignal = signalsService.computed(() => {
|
|
52
72
|
const baseFilters = config.filters ?? {};
|
|
53
|
-
const selectedStaffMembersIds = bookingService.serviceSelections
|
|
54
|
-
.get()[0]
|
|
55
|
-
?.resources?.map((resource) => resource._id);
|
|
56
|
-
const staffMemberIds = selectedStaffMembersIds?.length
|
|
57
|
-
? selectedStaffMembersIds
|
|
58
|
-
: baseFilters.staffMemberIds;
|
|
59
73
|
const selectedLocation = bookingService.location.get();
|
|
60
74
|
const location = selectedLocation
|
|
61
75
|
? {
|
|
@@ -66,36 +80,24 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
66
80
|
return {
|
|
67
81
|
...baseFilters,
|
|
68
82
|
location,
|
|
69
|
-
staffMemberIds,
|
|
83
|
+
staffMemberIds: preSelectedStaffSignal.get(),
|
|
70
84
|
};
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
* Fetch time slots from API
|
|
74
|
-
* If cursor is provided, appends to existing slots (pagination)
|
|
75
|
-
* If no cursor, resets state and fetches fresh slots
|
|
76
|
-
*/
|
|
77
|
-
const fetchTimeSlots = async ({ isLoadMore } = { isLoadMore: false }) => {
|
|
78
|
-
if (isLoadingSignal.get() || (isLoadMore && !cursorSignal.get())) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const cursor = isLoadMore ? cursorSignal.get() : undefined;
|
|
85
|
+
});
|
|
86
|
+
const fetchTimeSlots = async () => {
|
|
82
87
|
isLoadingSignal.set(true);
|
|
83
88
|
errorSignal.set(null);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
89
|
+
cursorSignal.set(undefined);
|
|
90
|
+
timeSlotsSignal.set([]);
|
|
87
91
|
try {
|
|
88
92
|
const response = await fetchAvailability({
|
|
89
|
-
services:
|
|
90
|
-
fromLocalDate: startDateSignal.get(),
|
|
91
|
-
toLocalDate: endDateSignal.get(),
|
|
93
|
+
services: servicesSignal.get(),
|
|
92
94
|
timeZone: bookingService.timezone.get(),
|
|
93
95
|
viewMode: viewModeSignal.get(),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
fromLocalDate: dateRangeSignal.get().start,
|
|
97
|
+
toLocalDate: dateRangeSignal.get().end,
|
|
98
|
+
...filtersSignal.get(),
|
|
96
99
|
});
|
|
97
|
-
|
|
98
|
-
timeSlotsSignal.set(isLoadMore ? [...timeSlotsSignal.get(), ...newSlots] : newSlots);
|
|
100
|
+
timeSlotsSignal.set(response.timeSlots);
|
|
99
101
|
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
100
102
|
cursorSignal.set(nextCursor);
|
|
101
103
|
hasMoreSignal.set(!!nextCursor);
|
|
@@ -103,9 +105,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
103
105
|
catch (error) {
|
|
104
106
|
const message = error instanceof Error
|
|
105
107
|
? error.message
|
|
106
|
-
:
|
|
107
|
-
? 'Failed to load more time slots'
|
|
108
|
-
: 'Failed to load time slots';
|
|
108
|
+
: 'Failed to load time slots';
|
|
109
109
|
errorSignal.set(message);
|
|
110
110
|
}
|
|
111
111
|
finally {
|
|
@@ -182,33 +182,61 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
182
182
|
* Action: Load more slots (pagination)
|
|
183
183
|
*/
|
|
184
184
|
const loadMore = async () => {
|
|
185
|
-
|
|
185
|
+
const cursor = cursorSignal.get();
|
|
186
|
+
if (!cursor) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
isLoadingSignal.set(true);
|
|
190
|
+
errorSignal.set(null);
|
|
191
|
+
try {
|
|
192
|
+
const response = await fetchAvailability({
|
|
193
|
+
services: servicesSignal.get(),
|
|
194
|
+
fromLocalDate: dateRangeSignal.get().start,
|
|
195
|
+
toLocalDate: dateRangeSignal.get().end,
|
|
196
|
+
timeZone: bookingService.timezone.get(),
|
|
197
|
+
viewMode: viewModeSignal.get(),
|
|
198
|
+
cursor,
|
|
199
|
+
...filtersSignal.get(),
|
|
200
|
+
});
|
|
201
|
+
const newSlots = response.timeSlots;
|
|
202
|
+
timeSlotsSignal.set([...timeSlotsSignal.get(), ...newSlots]);
|
|
203
|
+
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
204
|
+
cursorSignal.set(nextCursor);
|
|
205
|
+
hasMoreSignal.set(!!nextCursor);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
const message = error instanceof Error
|
|
209
|
+
? error.message
|
|
210
|
+
: 'Failed to load more time slots';
|
|
211
|
+
errorSignal.set(message);
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
isLoadingSignal.set(false);
|
|
215
|
+
}
|
|
186
216
|
};
|
|
187
217
|
/**
|
|
188
|
-
* Action: Update date range
|
|
218
|
+
* Action: Update date range (effect will handle refetch)
|
|
189
219
|
*/
|
|
190
220
|
const setDateRange = (newStartDate, newEndDate) => {
|
|
191
|
-
const currentStartDate =
|
|
192
|
-
const currentEndDate = endDateSignal.get();
|
|
221
|
+
const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.get();
|
|
193
222
|
if (currentStartDate.getTime() === newStartDate.getTime() &&
|
|
194
223
|
currentEndDate.getTime() === newEndDate.getTime()) {
|
|
195
224
|
return;
|
|
196
225
|
}
|
|
197
|
-
|
|
198
|
-
endDateSignal.set(newEndDate);
|
|
226
|
+
dateRangeSignal.set({ start: newStartDate, end: newEndDate });
|
|
199
227
|
};
|
|
200
|
-
const resourcesSignal = signalsService.computed(() => {
|
|
201
|
-
return bookingService.serviceSelections.get()[0]?.resources;
|
|
202
|
-
});
|
|
203
228
|
let isFirstRun = true;
|
|
204
229
|
signalsService.effect(() => {
|
|
230
|
+
filtersSignal.get();
|
|
231
|
+
dateRangeSignal.get();
|
|
205
232
|
bookingService.timezone.get();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
233
|
+
if (isFirstRun) {
|
|
234
|
+
isFirstRun = false;
|
|
235
|
+
if (config.timeSlots) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
210
238
|
}
|
|
211
|
-
|
|
239
|
+
fetchTimeSlots();
|
|
212
240
|
});
|
|
213
241
|
// Return service API
|
|
214
242
|
return {
|
|
@@ -218,8 +246,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
218
246
|
error: errorSignal,
|
|
219
247
|
hasMore: hasMoreSignal,
|
|
220
248
|
// Configuration Signals (viewMode is internal only)
|
|
221
|
-
|
|
222
|
-
endDate: endDateSignal,
|
|
249
|
+
dateRange: dateRangeSignal,
|
|
223
250
|
// Actions
|
|
224
251
|
actions: {
|
|
225
252
|
selectSlot,
|
|
@@ -106,9 +106,10 @@ export function ListData(props) {
|
|
|
106
106
|
*/
|
|
107
107
|
export function DateRange(props) {
|
|
108
108
|
const service = useService(TimeSlotListServiceDefinition);
|
|
109
|
+
const dateRange = service.dateRange.get();
|
|
109
110
|
return props.children({
|
|
110
|
-
startDate:
|
|
111
|
-
endDate:
|
|
111
|
+
startDate: dateRange.start,
|
|
112
|
+
endDate: dateRange.end,
|
|
112
113
|
setDateRange: service.actions.setDateRange,
|
|
113
114
|
});
|
|
114
115
|
}
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* TimeSlotList Service
|
|
3
3
|
* Main export file for time slot list service
|
|
4
4
|
*/
|
|
5
|
-
export { TimeSlotListServiceDefinition, ViewMode, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type TimeSlotsFilters, } from './time-slot-list.def.js';
|
|
5
|
+
export { TimeSlotListServiceDefinition, ViewMode, type DateRange, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type TimeSlotsFilters, } from './time-slot-list.def.js';
|
|
6
6
|
export { TimeSlotListService, loadTimeSlotListConfig, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list.js';
|
|
@@ -77,13 +77,16 @@ export interface TimeSlotListServiceActions {
|
|
|
77
77
|
loadMore: () => Promise<void>;
|
|
78
78
|
setDateRange: (start: Date, end: Date) => void;
|
|
79
79
|
}
|
|
80
|
+
export interface DateRange {
|
|
81
|
+
start: Date;
|
|
82
|
+
end: Date;
|
|
83
|
+
}
|
|
80
84
|
export interface TimeSlotListServiceAPI {
|
|
81
85
|
timeSlots: Signal<TimeSlot[]>;
|
|
82
86
|
isLoading: Signal<boolean>;
|
|
83
87
|
error: Signal<string | null>;
|
|
84
88
|
hasMore: Signal<boolean>;
|
|
85
|
-
|
|
86
|
-
endDate: Signal<Date>;
|
|
89
|
+
dateRange: Signal<DateRange>;
|
|
87
90
|
actions: TimeSlotListServiceActions;
|
|
88
91
|
}
|
|
89
92
|
export declare const TimeSlotListServiceDefinition: string & {
|
|
@@ -15,6 +15,31 @@ 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
|
+
}
|
|
18
43
|
// ============================================================================
|
|
19
44
|
// Service Implementation
|
|
20
45
|
// ============================================================================
|
|
@@ -29,33 +54,22 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
29
54
|
const hasMoreSignal = signalsService.signal(false);
|
|
30
55
|
// Internal only - viewMode is auto-detected based on service types
|
|
31
56
|
const viewModeSignal = signalsService.signal(ViewMode.CLASSIC);
|
|
32
|
-
const
|
|
33
|
-
|
|
57
|
+
const dateRangeSignal = signalsService.signal({
|
|
58
|
+
start: config.startDate,
|
|
59
|
+
end: config.endDate,
|
|
60
|
+
});
|
|
34
61
|
const cursorSignal = signalsService.signal(undefined);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return config.services ?? [];
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* Helper: Get filters combining config with booking overrides
|
|
49
|
-
* Uses location and resources from booking if they exist (not from the slot)
|
|
50
|
-
*/
|
|
51
|
-
const getFilters = () => {
|
|
62
|
+
const servicesSignal = signalsService.computed(createMemoizedByIds({
|
|
63
|
+
fallback: config.services ?? [],
|
|
64
|
+
getValue: () => bookingService.serviceSelections.get().map((s) => s.service),
|
|
65
|
+
extractId: (service) => service._id,
|
|
66
|
+
}));
|
|
67
|
+
const preSelectedStaffSignal = signalsService.computed(createMemoizedByIds({
|
|
68
|
+
fallback: config.filters?.staffMemberIds,
|
|
69
|
+
getValue: () => (bookingService.serviceSelections.get()[0]?.resources ?? []).map((resource) => resource._id),
|
|
70
|
+
}));
|
|
71
|
+
const filtersSignal = signalsService.computed(() => {
|
|
52
72
|
const baseFilters = config.filters ?? {};
|
|
53
|
-
const selectedStaffMembersIds = bookingService.serviceSelections
|
|
54
|
-
.get()[0]
|
|
55
|
-
?.resources?.map((resource) => resource._id);
|
|
56
|
-
const staffMemberIds = selectedStaffMembersIds?.length
|
|
57
|
-
? selectedStaffMembersIds
|
|
58
|
-
: baseFilters.staffMemberIds;
|
|
59
73
|
const selectedLocation = bookingService.location.get();
|
|
60
74
|
const location = selectedLocation
|
|
61
75
|
? {
|
|
@@ -66,36 +80,24 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
66
80
|
return {
|
|
67
81
|
...baseFilters,
|
|
68
82
|
location,
|
|
69
|
-
staffMemberIds,
|
|
83
|
+
staffMemberIds: preSelectedStaffSignal.get(),
|
|
70
84
|
};
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
* Fetch time slots from API
|
|
74
|
-
* If cursor is provided, appends to existing slots (pagination)
|
|
75
|
-
* If no cursor, resets state and fetches fresh slots
|
|
76
|
-
*/
|
|
77
|
-
const fetchTimeSlots = async ({ isLoadMore } = { isLoadMore: false }) => {
|
|
78
|
-
if (isLoadingSignal.get() || (isLoadMore && !cursorSignal.get())) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const cursor = isLoadMore ? cursorSignal.get() : undefined;
|
|
85
|
+
});
|
|
86
|
+
const fetchTimeSlots = async () => {
|
|
82
87
|
isLoadingSignal.set(true);
|
|
83
88
|
errorSignal.set(null);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
89
|
+
cursorSignal.set(undefined);
|
|
90
|
+
timeSlotsSignal.set([]);
|
|
87
91
|
try {
|
|
88
92
|
const response = await fetchAvailability({
|
|
89
|
-
services:
|
|
90
|
-
fromLocalDate: startDateSignal.get(),
|
|
91
|
-
toLocalDate: endDateSignal.get(),
|
|
93
|
+
services: servicesSignal.get(),
|
|
92
94
|
timeZone: bookingService.timezone.get(),
|
|
93
95
|
viewMode: viewModeSignal.get(),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
fromLocalDate: dateRangeSignal.get().start,
|
|
97
|
+
toLocalDate: dateRangeSignal.get().end,
|
|
98
|
+
...filtersSignal.get(),
|
|
96
99
|
});
|
|
97
|
-
|
|
98
|
-
timeSlotsSignal.set(isLoadMore ? [...timeSlotsSignal.get(), ...newSlots] : newSlots);
|
|
100
|
+
timeSlotsSignal.set(response.timeSlots);
|
|
99
101
|
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
100
102
|
cursorSignal.set(nextCursor);
|
|
101
103
|
hasMoreSignal.set(!!nextCursor);
|
|
@@ -103,9 +105,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
103
105
|
catch (error) {
|
|
104
106
|
const message = error instanceof Error
|
|
105
107
|
? error.message
|
|
106
|
-
:
|
|
107
|
-
? 'Failed to load more time slots'
|
|
108
|
-
: 'Failed to load time slots';
|
|
108
|
+
: 'Failed to load time slots';
|
|
109
109
|
errorSignal.set(message);
|
|
110
110
|
}
|
|
111
111
|
finally {
|
|
@@ -182,33 +182,61 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
182
182
|
* Action: Load more slots (pagination)
|
|
183
183
|
*/
|
|
184
184
|
const loadMore = async () => {
|
|
185
|
-
|
|
185
|
+
const cursor = cursorSignal.get();
|
|
186
|
+
if (!cursor) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
isLoadingSignal.set(true);
|
|
190
|
+
errorSignal.set(null);
|
|
191
|
+
try {
|
|
192
|
+
const response = await fetchAvailability({
|
|
193
|
+
services: servicesSignal.get(),
|
|
194
|
+
fromLocalDate: dateRangeSignal.get().start,
|
|
195
|
+
toLocalDate: dateRangeSignal.get().end,
|
|
196
|
+
timeZone: bookingService.timezone.get(),
|
|
197
|
+
viewMode: viewModeSignal.get(),
|
|
198
|
+
cursor,
|
|
199
|
+
...filtersSignal.get(),
|
|
200
|
+
});
|
|
201
|
+
const newSlots = response.timeSlots;
|
|
202
|
+
timeSlotsSignal.set([...timeSlotsSignal.get(), ...newSlots]);
|
|
203
|
+
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
204
|
+
cursorSignal.set(nextCursor);
|
|
205
|
+
hasMoreSignal.set(!!nextCursor);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
const message = error instanceof Error
|
|
209
|
+
? error.message
|
|
210
|
+
: 'Failed to load more time slots';
|
|
211
|
+
errorSignal.set(message);
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
isLoadingSignal.set(false);
|
|
215
|
+
}
|
|
186
216
|
};
|
|
187
217
|
/**
|
|
188
|
-
* Action: Update date range
|
|
218
|
+
* Action: Update date range (effect will handle refetch)
|
|
189
219
|
*/
|
|
190
220
|
const setDateRange = (newStartDate, newEndDate) => {
|
|
191
|
-
const currentStartDate =
|
|
192
|
-
const currentEndDate = endDateSignal.get();
|
|
221
|
+
const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.get();
|
|
193
222
|
if (currentStartDate.getTime() === newStartDate.getTime() &&
|
|
194
223
|
currentEndDate.getTime() === newEndDate.getTime()) {
|
|
195
224
|
return;
|
|
196
225
|
}
|
|
197
|
-
|
|
198
|
-
endDateSignal.set(newEndDate);
|
|
226
|
+
dateRangeSignal.set({ start: newStartDate, end: newEndDate });
|
|
199
227
|
};
|
|
200
|
-
const resourcesSignal = signalsService.computed(() => {
|
|
201
|
-
return bookingService.serviceSelections.get()[0]?.resources;
|
|
202
|
-
});
|
|
203
228
|
let isFirstRun = true;
|
|
204
229
|
signalsService.effect(() => {
|
|
230
|
+
filtersSignal.get();
|
|
231
|
+
dateRangeSignal.get();
|
|
205
232
|
bookingService.timezone.get();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
233
|
+
if (isFirstRun) {
|
|
234
|
+
isFirstRun = false;
|
|
235
|
+
if (config.timeSlots) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
210
238
|
}
|
|
211
|
-
|
|
239
|
+
fetchTimeSlots();
|
|
212
240
|
});
|
|
213
241
|
// Return service API
|
|
214
242
|
return {
|
|
@@ -218,8 +246,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
218
246
|
error: errorSignal,
|
|
219
247
|
hasMore: hasMoreSignal,
|
|
220
248
|
// Configuration Signals (viewMode is internal only)
|
|
221
|
-
|
|
222
|
-
endDate: endDateSignal,
|
|
249
|
+
dateRange: dateRangeSignal,
|
|
223
250
|
// Actions
|
|
224
251
|
actions: {
|
|
225
252
|
selectSlot,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-bookings",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.36",
|
|
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": "ecae9bfd11fe7e50bf6e5eeb5ad0b3e9763b64d5cfa1aa1477176483"
|
|
76
76
|
}
|