@wix/headless-bookings 0.0.35 → 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.
- 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 +72 -74
- 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 +72 -74
- 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 & {
|
|
@@ -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';
|
|
@@ -29,73 +29,45 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
29
29
|
const hasMoreSignal = signalsService.signal(false);
|
|
30
30
|
// Internal only - viewMode is auto-detected based on service types
|
|
31
31
|
const viewModeSignal = signalsService.signal(ViewMode.CLASSIC);
|
|
32
|
-
const
|
|
33
|
-
|
|
32
|
+
const dateRangeSignal = signalsService.signal({
|
|
33
|
+
start: config.startDate,
|
|
34
|
+
end: config.endDate,
|
|
35
|
+
});
|
|
34
36
|
const cursorSignal = signalsService.signal(undefined);
|
|
35
|
-
|
|
36
|
-
* Helper: Get services from config or BookingService
|
|
37
|
-
*/
|
|
37
|
+
// Helper functions using peek() - no subscriptions created
|
|
38
38
|
const getServices = () => {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
.map((s) => s.service)
|
|
42
|
-
|
|
43
|
-
return selectedServices;
|
|
44
|
-
}
|
|
45
|
-
return config.services ?? [];
|
|
39
|
+
const serviceSelections = bookingService.serviceSelections.peek();
|
|
40
|
+
return serviceSelections.length
|
|
41
|
+
? serviceSelections.map((s) => s.service)
|
|
42
|
+
: (config.services ?? []);
|
|
46
43
|
};
|
|
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
44
|
const getFilters = () => {
|
|
52
45
|
const baseFilters = config.filters ?? {};
|
|
53
|
-
const
|
|
54
|
-
.get()[0]
|
|
55
|
-
?.resources?.map((resource) => resource._id);
|
|
56
|
-
const staffMemberIds = selectedStaffMembersIds?.length
|
|
57
|
-
? selectedStaffMembersIds
|
|
58
|
-
: baseFilters.staffMemberIds;
|
|
59
|
-
const selectedLocation = bookingService.location.get();
|
|
46
|
+
const selectedLocation = bookingService.location.peek();
|
|
60
47
|
const location = selectedLocation
|
|
61
|
-
? {
|
|
62
|
-
id: selectedLocation._id,
|
|
63
|
-
locationType: selectedLocation.type,
|
|
64
|
-
}
|
|
48
|
+
? { id: selectedLocation._id, locationType: selectedLocation.type }
|
|
65
49
|
: baseFilters.location;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
staffMemberIds
|
|
70
|
-
};
|
|
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 };
|
|
71
55
|
};
|
|
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;
|
|
56
|
+
const fetchTimeSlots = async () => {
|
|
82
57
|
isLoadingSignal.set(true);
|
|
83
58
|
errorSignal.set(null);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
59
|
+
cursorSignal.set(undefined);
|
|
60
|
+
timeSlotsSignal.set([]);
|
|
87
61
|
try {
|
|
88
62
|
const response = await fetchAvailability({
|
|
89
63
|
services: getServices(),
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
cursor,
|
|
64
|
+
timeZone: bookingService.timezone.peek(),
|
|
65
|
+
viewMode: viewModeSignal.peek(),
|
|
66
|
+
fromLocalDate: dateRangeSignal.peek().start,
|
|
67
|
+
toLocalDate: dateRangeSignal.peek().end,
|
|
95
68
|
...getFilters(),
|
|
96
69
|
});
|
|
97
|
-
|
|
98
|
-
timeSlotsSignal.set(isLoadMore ? [...timeSlotsSignal.get(), ...newSlots] : newSlots);
|
|
70
|
+
timeSlotsSignal.set(response.timeSlots);
|
|
99
71
|
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
100
72
|
cursorSignal.set(nextCursor);
|
|
101
73
|
hasMoreSignal.set(!!nextCursor);
|
|
@@ -103,9 +75,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
103
75
|
catch (error) {
|
|
104
76
|
const message = error instanceof Error
|
|
105
77
|
? error.message
|
|
106
|
-
:
|
|
107
|
-
? 'Failed to load more time slots'
|
|
108
|
-
: 'Failed to load time slots';
|
|
78
|
+
: 'Failed to load time slots';
|
|
109
79
|
errorSignal.set(message);
|
|
110
80
|
}
|
|
111
81
|
finally {
|
|
@@ -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,33 +152,62 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
182
152
|
* Action: Load more slots (pagination)
|
|
183
153
|
*/
|
|
184
154
|
const loadMore = async () => {
|
|
185
|
-
|
|
155
|
+
const cursor = cursorSignal.peek();
|
|
156
|
+
if (!cursor) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
isLoadingSignal.set(true);
|
|
160
|
+
errorSignal.set(null);
|
|
161
|
+
try {
|
|
162
|
+
const response = await fetchAvailability({
|
|
163
|
+
services: getServices(),
|
|
164
|
+
fromLocalDate: dateRangeSignal.peek().start,
|
|
165
|
+
toLocalDate: dateRangeSignal.peek().end,
|
|
166
|
+
timeZone: bookingService.timezone.peek(),
|
|
167
|
+
viewMode: viewModeSignal.peek(),
|
|
168
|
+
cursor,
|
|
169
|
+
...getFilters(),
|
|
170
|
+
});
|
|
171
|
+
const newSlots = response.timeSlots;
|
|
172
|
+
timeSlotsSignal.set([...timeSlotsSignal.peek(), ...newSlots]);
|
|
173
|
+
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
174
|
+
cursorSignal.set(nextCursor);
|
|
175
|
+
hasMoreSignal.set(!!nextCursor);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
const message = error instanceof Error
|
|
179
|
+
? error.message
|
|
180
|
+
: 'Failed to load more time slots';
|
|
181
|
+
errorSignal.set(message);
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
isLoadingSignal.set(false);
|
|
185
|
+
}
|
|
186
186
|
};
|
|
187
187
|
/**
|
|
188
|
-
* Action: Update date range
|
|
188
|
+
* Action: Update date range (effect will handle refetch)
|
|
189
189
|
*/
|
|
190
190
|
const setDateRange = (newStartDate, newEndDate) => {
|
|
191
|
-
const currentStartDate =
|
|
192
|
-
const currentEndDate = endDateSignal.get();
|
|
191
|
+
const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.peek();
|
|
193
192
|
if (currentStartDate.getTime() === newStartDate.getTime() &&
|
|
194
193
|
currentEndDate.getTime() === newEndDate.getTime()) {
|
|
195
194
|
return;
|
|
196
195
|
}
|
|
197
|
-
|
|
198
|
-
endDateSignal.set(newEndDate);
|
|
196
|
+
dateRangeSignal.set({ start: newStartDate, end: newEndDate });
|
|
199
197
|
};
|
|
200
|
-
const resourcesSignal = signalsService.computed(() => {
|
|
201
|
-
return bookingService.serviceSelections.get()[0]?.resources;
|
|
202
|
-
});
|
|
203
198
|
let isFirstRun = true;
|
|
204
199
|
signalsService.effect(() => {
|
|
200
|
+
// Dependencies - trigger refetch when these change
|
|
201
|
+
dateRangeSignal.get();
|
|
205
202
|
bookingService.timezone.get();
|
|
206
203
|
bookingService.location.get();
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
204
|
+
if (isFirstRun) {
|
|
205
|
+
isFirstRun = false;
|
|
206
|
+
if (config.timeSlots) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
210
209
|
}
|
|
211
|
-
|
|
210
|
+
fetchTimeSlots();
|
|
212
211
|
});
|
|
213
212
|
// Return service API
|
|
214
213
|
return {
|
|
@@ -218,8 +217,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
218
217
|
error: errorSignal,
|
|
219
218
|
hasMore: hasMoreSignal,
|
|
220
219
|
// Configuration Signals (viewMode is internal only)
|
|
221
|
-
|
|
222
|
-
endDate: endDateSignal,
|
|
220
|
+
dateRange: dateRangeSignal,
|
|
223
221
|
// Actions
|
|
224
222
|
actions: {
|
|
225
223
|
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 & {
|
|
@@ -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';
|
|
@@ -29,73 +29,45 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
29
29
|
const hasMoreSignal = signalsService.signal(false);
|
|
30
30
|
// Internal only - viewMode is auto-detected based on service types
|
|
31
31
|
const viewModeSignal = signalsService.signal(ViewMode.CLASSIC);
|
|
32
|
-
const
|
|
33
|
-
|
|
32
|
+
const dateRangeSignal = signalsService.signal({
|
|
33
|
+
start: config.startDate,
|
|
34
|
+
end: config.endDate,
|
|
35
|
+
});
|
|
34
36
|
const cursorSignal = signalsService.signal(undefined);
|
|
35
|
-
|
|
36
|
-
* Helper: Get services from config or BookingService
|
|
37
|
-
*/
|
|
37
|
+
// Helper functions using peek() - no subscriptions created
|
|
38
38
|
const getServices = () => {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
.map((s) => s.service)
|
|
42
|
-
|
|
43
|
-
return selectedServices;
|
|
44
|
-
}
|
|
45
|
-
return config.services ?? [];
|
|
39
|
+
const serviceSelections = bookingService.serviceSelections.peek();
|
|
40
|
+
return serviceSelections.length
|
|
41
|
+
? serviceSelections.map((s) => s.service)
|
|
42
|
+
: (config.services ?? []);
|
|
46
43
|
};
|
|
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
44
|
const getFilters = () => {
|
|
52
45
|
const baseFilters = config.filters ?? {};
|
|
53
|
-
const
|
|
54
|
-
.get()[0]
|
|
55
|
-
?.resources?.map((resource) => resource._id);
|
|
56
|
-
const staffMemberIds = selectedStaffMembersIds?.length
|
|
57
|
-
? selectedStaffMembersIds
|
|
58
|
-
: baseFilters.staffMemberIds;
|
|
59
|
-
const selectedLocation = bookingService.location.get();
|
|
46
|
+
const selectedLocation = bookingService.location.peek();
|
|
60
47
|
const location = selectedLocation
|
|
61
|
-
? {
|
|
62
|
-
id: selectedLocation._id,
|
|
63
|
-
locationType: selectedLocation.type,
|
|
64
|
-
}
|
|
48
|
+
? { id: selectedLocation._id, locationType: selectedLocation.type }
|
|
65
49
|
: baseFilters.location;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
staffMemberIds
|
|
70
|
-
};
|
|
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 };
|
|
71
55
|
};
|
|
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;
|
|
56
|
+
const fetchTimeSlots = async () => {
|
|
82
57
|
isLoadingSignal.set(true);
|
|
83
58
|
errorSignal.set(null);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
59
|
+
cursorSignal.set(undefined);
|
|
60
|
+
timeSlotsSignal.set([]);
|
|
87
61
|
try {
|
|
88
62
|
const response = await fetchAvailability({
|
|
89
63
|
services: getServices(),
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
cursor,
|
|
64
|
+
timeZone: bookingService.timezone.peek(),
|
|
65
|
+
viewMode: viewModeSignal.peek(),
|
|
66
|
+
fromLocalDate: dateRangeSignal.peek().start,
|
|
67
|
+
toLocalDate: dateRangeSignal.peek().end,
|
|
95
68
|
...getFilters(),
|
|
96
69
|
});
|
|
97
|
-
|
|
98
|
-
timeSlotsSignal.set(isLoadMore ? [...timeSlotsSignal.get(), ...newSlots] : newSlots);
|
|
70
|
+
timeSlotsSignal.set(response.timeSlots);
|
|
99
71
|
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
100
72
|
cursorSignal.set(nextCursor);
|
|
101
73
|
hasMoreSignal.set(!!nextCursor);
|
|
@@ -103,9 +75,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
103
75
|
catch (error) {
|
|
104
76
|
const message = error instanceof Error
|
|
105
77
|
? error.message
|
|
106
|
-
:
|
|
107
|
-
? 'Failed to load more time slots'
|
|
108
|
-
: 'Failed to load time slots';
|
|
78
|
+
: 'Failed to load time slots';
|
|
109
79
|
errorSignal.set(message);
|
|
110
80
|
}
|
|
111
81
|
finally {
|
|
@@ -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,33 +152,62 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
182
152
|
* Action: Load more slots (pagination)
|
|
183
153
|
*/
|
|
184
154
|
const loadMore = async () => {
|
|
185
|
-
|
|
155
|
+
const cursor = cursorSignal.peek();
|
|
156
|
+
if (!cursor) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
isLoadingSignal.set(true);
|
|
160
|
+
errorSignal.set(null);
|
|
161
|
+
try {
|
|
162
|
+
const response = await fetchAvailability({
|
|
163
|
+
services: getServices(),
|
|
164
|
+
fromLocalDate: dateRangeSignal.peek().start,
|
|
165
|
+
toLocalDate: dateRangeSignal.peek().end,
|
|
166
|
+
timeZone: bookingService.timezone.peek(),
|
|
167
|
+
viewMode: viewModeSignal.peek(),
|
|
168
|
+
cursor,
|
|
169
|
+
...getFilters(),
|
|
170
|
+
});
|
|
171
|
+
const newSlots = response.timeSlots;
|
|
172
|
+
timeSlotsSignal.set([...timeSlotsSignal.peek(), ...newSlots]);
|
|
173
|
+
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
174
|
+
cursorSignal.set(nextCursor);
|
|
175
|
+
hasMoreSignal.set(!!nextCursor);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
const message = error instanceof Error
|
|
179
|
+
? error.message
|
|
180
|
+
: 'Failed to load more time slots';
|
|
181
|
+
errorSignal.set(message);
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
isLoadingSignal.set(false);
|
|
185
|
+
}
|
|
186
186
|
};
|
|
187
187
|
/**
|
|
188
|
-
* Action: Update date range
|
|
188
|
+
* Action: Update date range (effect will handle refetch)
|
|
189
189
|
*/
|
|
190
190
|
const setDateRange = (newStartDate, newEndDate) => {
|
|
191
|
-
const currentStartDate =
|
|
192
|
-
const currentEndDate = endDateSignal.get();
|
|
191
|
+
const { start: currentStartDate, end: currentEndDate } = dateRangeSignal.peek();
|
|
193
192
|
if (currentStartDate.getTime() === newStartDate.getTime() &&
|
|
194
193
|
currentEndDate.getTime() === newEndDate.getTime()) {
|
|
195
194
|
return;
|
|
196
195
|
}
|
|
197
|
-
|
|
198
|
-
endDateSignal.set(newEndDate);
|
|
196
|
+
dateRangeSignal.set({ start: newStartDate, end: newEndDate });
|
|
199
197
|
};
|
|
200
|
-
const resourcesSignal = signalsService.computed(() => {
|
|
201
|
-
return bookingService.serviceSelections.get()[0]?.resources;
|
|
202
|
-
});
|
|
203
198
|
let isFirstRun = true;
|
|
204
199
|
signalsService.effect(() => {
|
|
200
|
+
// Dependencies - trigger refetch when these change
|
|
201
|
+
dateRangeSignal.get();
|
|
205
202
|
bookingService.timezone.get();
|
|
206
203
|
bookingService.location.get();
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
204
|
+
if (isFirstRun) {
|
|
205
|
+
isFirstRun = false;
|
|
206
|
+
if (config.timeSlots) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
210
209
|
}
|
|
211
|
-
|
|
210
|
+
fetchTimeSlots();
|
|
212
211
|
});
|
|
213
212
|
// Return service API
|
|
214
213
|
return {
|
|
@@ -218,8 +217,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
218
217
|
error: errorSignal,
|
|
219
218
|
hasMore: hasMoreSignal,
|
|
220
219
|
// Configuration Signals (viewMode is internal only)
|
|
221
|
-
|
|
222
|
-
endDate: endDateSignal,
|
|
220
|
+
dateRange: dateRangeSignal,
|
|
223
221
|
// Actions
|
|
224
222
|
actions: {
|
|
225
223
|
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.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
|
}
|