@wix/headless-bookings 0.0.33 → 0.0.35
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/__mocks__/booking.d.ts +9 -3
- package/cjs/dist/__mocks__/booking.js +2 -2
- package/cjs/dist/react/core/booking/Booking.d.ts +3 -2
- package/cjs/dist/react/core/time-slot-list/TimeSlot.js +3 -4
- package/cjs/dist/react/payment/Payment.js +1 -5
- package/cjs/dist/react/service/Service.d.ts +39 -0
- package/cjs/dist/react/service/Service.js +58 -40
- package/cjs/dist/services/booking/book-action/bookAction.js +1 -1
- package/cjs/dist/services/booking/book-action/buildBookingRequest.js +1 -1
- package/cjs/dist/services/booking/book-action/types.d.ts +3 -2
- package/cjs/dist/services/booking/booking.d.ts +6 -10
- package/cjs/dist/services/index.d.ts +1 -1
- package/cjs/dist/services/time-slot-list/time-slot-list.js +76 -90
- package/dist/__mocks__/booking.d.ts +9 -3
- package/dist/__mocks__/booking.js +2 -2
- package/dist/react/core/booking/Booking.d.ts +3 -2
- package/dist/react/core/time-slot-list/TimeSlot.js +3 -4
- package/dist/react/payment/Payment.js +1 -5
- package/dist/react/service/Service.d.ts +39 -0
- package/dist/react/service/Service.js +58 -40
- package/dist/services/booking/book-action/bookAction.js +1 -1
- package/dist/services/booking/book-action/buildBookingRequest.js +1 -1
- package/dist/services/booking/book-action/types.d.ts +3 -2
- package/dist/services/booking/booking.d.ts +6 -10
- package/dist/services/index.d.ts +1 -1
- package/dist/services/time-slot-list/time-slot-list.js +76 -90
- package/package.json +2 -2
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Types for the Book action
|
|
3
3
|
*/
|
|
4
|
-
import type { ServiceSelection
|
|
4
|
+
import type { ServiceSelection } from '../booking.js';
|
|
5
5
|
import type { FormValues } from '@wix/form-public';
|
|
6
|
+
import type { Location as TimeSlotLocationType } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
6
7
|
/**
|
|
7
8
|
* Result returned from the book action
|
|
8
9
|
*/
|
|
@@ -21,7 +22,7 @@ export interface BookingError {
|
|
|
21
22
|
*/
|
|
22
23
|
export interface BookActionParams {
|
|
23
24
|
serviceSelections: ServiceSelection[];
|
|
24
|
-
location:
|
|
25
|
+
location: TimeSlotLocationType | null;
|
|
25
26
|
formSubmission: FormValues | null;
|
|
26
27
|
timezone?: string;
|
|
27
28
|
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Persists across page navigation in SPA until page refresh
|
|
5
5
|
*/
|
|
6
6
|
import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
7
|
-
import type { Service } from '@wix/auto_sdk_bookings_services';
|
|
8
|
-
import type { Resource, EventInfo } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
7
|
+
import type { Service, Location as ServiceLocationType } from '@wix/auto_sdk_bookings_services';
|
|
8
|
+
import type { Resource, EventInfo, Location as TimeSlotLocationType } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
9
9
|
import type { FormValues } from '@wix/form-public';
|
|
10
10
|
import type { BookResult } from './book-action/types.js';
|
|
11
11
|
export declare const ANY_STAFF_MEMBER_ID = "__any_staff_member__";
|
|
@@ -22,7 +22,7 @@ export interface ServiceSelectionTimeSlot {
|
|
|
22
22
|
/** Resources available in this time slot (populated when slot is selected) */
|
|
23
23
|
resources?: Resource[];
|
|
24
24
|
/** Location of this time slot (populated when slot is selected) */
|
|
25
|
-
location?:
|
|
25
|
+
location?: TimeSlotLocationType;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* ServiceSelection contains the service AND its related selections
|
|
@@ -39,10 +39,6 @@ export interface ServiceSelection {
|
|
|
39
39
|
/** Number of participants for this service selection */
|
|
40
40
|
totalParticipants?: number;
|
|
41
41
|
}
|
|
42
|
-
export interface Location {
|
|
43
|
-
id: string;
|
|
44
|
-
name?: string;
|
|
45
|
-
}
|
|
46
42
|
/**
|
|
47
43
|
* BookingService actions - setters only, no API calls
|
|
48
44
|
*/
|
|
@@ -61,7 +57,7 @@ export interface BookingServiceActions {
|
|
|
61
57
|
setResourceById(resourceId: string, resource: Resource, instanceId?: string): void;
|
|
62
58
|
/** Gets a resource by ID from a service selection. If no instanceId provided, uses the first item. */
|
|
63
59
|
getResourceById(resourceId: string, instanceId?: string): Resource | undefined;
|
|
64
|
-
setLocation(location:
|
|
60
|
+
setLocation(location: ServiceLocationType | null): void;
|
|
65
61
|
clearLocation(): void;
|
|
66
62
|
setTimezone(timezone: string): void;
|
|
67
63
|
setFormSubmission(values: FormValues | null): void;
|
|
@@ -77,7 +73,7 @@ export interface BookingService {
|
|
|
77
73
|
/** Signal containing the service selections (services with their related data) */
|
|
78
74
|
serviceSelections: Signal<ServiceSelection[]>;
|
|
79
75
|
/** Signal containing the selected location */
|
|
80
|
-
location: Signal<
|
|
76
|
+
location: Signal<ServiceLocationType | null>;
|
|
81
77
|
/** Signal containing the selected timezone */
|
|
82
78
|
timezone: Signal<string | undefined>;
|
|
83
79
|
/** Signal containing the form submission */
|
|
@@ -101,7 +97,7 @@ export interface BookingServiceConfig {
|
|
|
101
97
|
/** Optional initial service selections to pre-populate */
|
|
102
98
|
serviceSelections?: ServiceSelection[];
|
|
103
99
|
/** Optional initial location */
|
|
104
|
-
location?:
|
|
100
|
+
location?: ServiceLocationType | null;
|
|
105
101
|
/** Optional initial timezone */
|
|
106
102
|
timezone?: string;
|
|
107
103
|
/** Optional initial form submission */
|
package/dist/services/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Main export file for all service definitions and implementations
|
|
4
4
|
*/
|
|
5
5
|
export { ServiceService, ServiceServiceDefinition, type ServiceServiceAPI, type ServiceServiceConfig, type ServiceActions, type ServicePaymentData, loadServiceServiceInitialData, type SuccessServiceServiceConfigResult, type NotFoundServiceServiceConfigResult, } from './service/service.js';
|
|
6
|
-
export { BookingService, BookingServiceDefinition, type BookingServiceConfig, type
|
|
6
|
+
export { BookingService, BookingServiceDefinition, type BookingServiceConfig, type BookingServiceActions, type ServiceSelection, type ServiceSelectionTimeSlot, } from './booking/booking.js';
|
|
7
7
|
export { TimeSlotListService, TimeSlotListServiceDefinition, ViewMode, loadTimeSlotListConfig, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list/index.js';
|
|
8
8
|
export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListFilters, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
|
|
9
9
|
export { executeBookAction, canBook, buildBookingRequest, buildCheckoutRequest, type CanBookParams, type BookResult, type BookingError, type BookActionParams, type BookChildProps, type BookProps, type BuildCheckoutParams, } from './booking/book-action/index.js';
|
|
@@ -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 } from '@wix/services-definitions/core-services/signals';
|
|
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';
|
|
@@ -44,27 +44,58 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
44
44
|
}
|
|
45
45
|
return config.services ?? [];
|
|
46
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 = () => {
|
|
52
|
+
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
|
+
const selectedLocation = bookingService.location.get();
|
|
60
|
+
const location = selectedLocation
|
|
61
|
+
? {
|
|
62
|
+
id: selectedLocation._id,
|
|
63
|
+
locationType: selectedLocation.type,
|
|
64
|
+
}
|
|
65
|
+
: baseFilters.location;
|
|
66
|
+
return {
|
|
67
|
+
...baseFilters,
|
|
68
|
+
location,
|
|
69
|
+
staffMemberIds,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
47
72
|
/**
|
|
48
73
|
* Fetch time slots from API
|
|
49
|
-
*
|
|
74
|
+
* If cursor is provided, appends to existing slots (pagination)
|
|
75
|
+
* If no cursor, resets state and fetches fresh slots
|
|
50
76
|
*/
|
|
51
|
-
const fetchTimeSlots = async () => {
|
|
52
|
-
|
|
77
|
+
const fetchTimeSlots = async ({ isLoadMore } = { isLoadMore: false }) => {
|
|
78
|
+
if (isLoadingSignal.get() || (isLoadMore && !cursorSignal.get())) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const cursor = isLoadMore ? cursorSignal.get() : undefined;
|
|
53
82
|
isLoadingSignal.set(true);
|
|
54
83
|
errorSignal.set(null);
|
|
55
|
-
|
|
56
|
-
|
|
84
|
+
if (!isLoadMore) {
|
|
85
|
+
timeSlotsSignal.set([]);
|
|
86
|
+
}
|
|
57
87
|
try {
|
|
58
88
|
const response = await fetchAvailability({
|
|
59
|
-
services:
|
|
89
|
+
services: getServices(),
|
|
60
90
|
fromLocalDate: startDateSignal.get(),
|
|
61
91
|
toLocalDate: endDateSignal.get(),
|
|
62
92
|
timeZone: bookingService.timezone.get(),
|
|
63
93
|
viewMode: viewModeSignal.get(),
|
|
64
|
-
cursor
|
|
65
|
-
...
|
|
94
|
+
cursor,
|
|
95
|
+
...getFilters(),
|
|
66
96
|
});
|
|
67
|
-
|
|
97
|
+
const newSlots = response.timeSlots;
|
|
98
|
+
timeSlotsSignal.set(isLoadMore ? [...timeSlotsSignal.get(), ...newSlots] : newSlots);
|
|
68
99
|
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
69
100
|
cursorSignal.set(nextCursor);
|
|
70
101
|
hasMoreSignal.set(!!nextCursor);
|
|
@@ -72,7 +103,9 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
72
103
|
catch (error) {
|
|
73
104
|
const message = error instanceof Error
|
|
74
105
|
? error.message
|
|
75
|
-
:
|
|
106
|
+
: isLoadMore
|
|
107
|
+
? 'Failed to load more time slots'
|
|
108
|
+
: 'Failed to load time slots';
|
|
76
109
|
errorSignal.set(message);
|
|
77
110
|
}
|
|
78
111
|
finally {
|
|
@@ -90,27 +123,19 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
90
123
|
_id: r._id,
|
|
91
124
|
name: r.name,
|
|
92
125
|
}));
|
|
93
|
-
const
|
|
94
|
-
id: slot.location?._id,
|
|
95
|
-
name: slot.location?.name ?? undefined,
|
|
96
|
-
};
|
|
126
|
+
const resources = staffMembersFromSlot.length === 1 ? staffMembersFromSlot : [];
|
|
97
127
|
const timeSlot = {
|
|
98
128
|
startDate: slot.localStartDate,
|
|
99
129
|
endDate: slot.localEndDate,
|
|
100
130
|
totalCapacity: slot.totalCapacity ?? undefined,
|
|
101
131
|
remainingCapacity: slot.remainingCapacity ?? undefined,
|
|
102
132
|
scheduleId: slot.scheduleId ?? undefined,
|
|
103
|
-
eventInfo: slot.eventInfo
|
|
104
|
-
|
|
105
|
-
|
|
133
|
+
eventInfo: slot.eventInfo,
|
|
134
|
+
location: slot.location,
|
|
135
|
+
resources,
|
|
106
136
|
};
|
|
107
|
-
// Keep setting top-level location (for pre-slot selection use cases)
|
|
108
|
-
bookingService.actions.setLocation(slotLocation);
|
|
109
|
-
// Auto-select resources only when single staff
|
|
110
|
-
const resources = staffMembersFromSlot.length === 1 ? staffMembersFromSlot : [];
|
|
111
137
|
bookingService.actions.updateItem({
|
|
112
138
|
timeSlot,
|
|
113
|
-
resources,
|
|
114
139
|
});
|
|
115
140
|
};
|
|
116
141
|
/**
|
|
@@ -118,82 +143,46 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
118
143
|
* Updates the booking service with the selected resource
|
|
119
144
|
*/
|
|
120
145
|
const selectStaffMember = (staffMemberId) => {
|
|
121
|
-
const
|
|
122
|
-
if (!
|
|
146
|
+
const selectedTimeSlot = bookingService.serviceSelections.get()[0]?.timeSlot;
|
|
147
|
+
if (!selectedTimeSlot)
|
|
123
148
|
return;
|
|
124
|
-
const
|
|
149
|
+
const getStaffName = (id) => {
|
|
150
|
+
if (id === ANY_STAFF_MEMBER_ID)
|
|
151
|
+
return 'Any staff member';
|
|
125
152
|
const slots = timeSlotsSignal.get();
|
|
126
153
|
return (slots
|
|
127
154
|
.flatMap((slot) => slot.availableResources[0].resources)
|
|
128
155
|
.find((r) => r._id === id)?.name ?? undefined);
|
|
129
156
|
};
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
},
|
|
137
|
-
],
|
|
138
|
-
});
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
157
|
+
const resources = [
|
|
158
|
+
{
|
|
159
|
+
_id: staffMemberId,
|
|
160
|
+
name: getStaffName(staffMemberId),
|
|
161
|
+
},
|
|
162
|
+
];
|
|
141
163
|
bookingService.actions.updateItem({
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
},
|
|
147
|
-
],
|
|
164
|
+
timeSlot: {
|
|
165
|
+
...selectedTimeSlot,
|
|
166
|
+
resources,
|
|
167
|
+
},
|
|
148
168
|
});
|
|
149
169
|
};
|
|
150
170
|
/**
|
|
151
171
|
* Action: Clear staff selection while keeping the selected time slot
|
|
152
172
|
*/
|
|
153
173
|
const clearStaffSelection = () => {
|
|
154
|
-
const
|
|
155
|
-
if (!
|
|
174
|
+
const selectedTimeSlot = bookingService.serviceSelections.get()[0]?.timeSlot;
|
|
175
|
+
if (!selectedTimeSlot)
|
|
156
176
|
return;
|
|
157
177
|
bookingService.actions.updateItem({
|
|
158
|
-
resources: [],
|
|
178
|
+
timeSlot: { ...selectedTimeSlot, resources: [] },
|
|
159
179
|
});
|
|
160
180
|
};
|
|
161
181
|
/**
|
|
162
182
|
* Action: Load more slots (pagination)
|
|
163
183
|
*/
|
|
164
184
|
const loadMore = async () => {
|
|
165
|
-
|
|
166
|
-
if (!cursor || isLoadingSignal.get())
|
|
167
|
-
return;
|
|
168
|
-
const selectedServices = getServices();
|
|
169
|
-
isLoadingSignal.set(true);
|
|
170
|
-
try {
|
|
171
|
-
const response = await fetchAvailability({
|
|
172
|
-
services: selectedServices,
|
|
173
|
-
fromLocalDate: startDateSignal.get(),
|
|
174
|
-
toLocalDate: endDateSignal.get(),
|
|
175
|
-
timeZone: bookingService.timezone.get(),
|
|
176
|
-
cursor,
|
|
177
|
-
viewMode: viewModeSignal.get(),
|
|
178
|
-
...config.filters,
|
|
179
|
-
});
|
|
180
|
-
timeSlotsSignal.set([
|
|
181
|
-
...timeSlotsSignal.get(),
|
|
182
|
-
...response.timeSlots,
|
|
183
|
-
]);
|
|
184
|
-
const nextCursor = response.cursorPagingMetadata?.cursors?.next ?? undefined;
|
|
185
|
-
cursorSignal.set(nextCursor);
|
|
186
|
-
hasMoreSignal.set(!!nextCursor);
|
|
187
|
-
}
|
|
188
|
-
catch (error) {
|
|
189
|
-
const message = error instanceof Error
|
|
190
|
-
? error.message
|
|
191
|
-
: 'Failed to load more time slots';
|
|
192
|
-
errorSignal.set(message);
|
|
193
|
-
}
|
|
194
|
-
finally {
|
|
195
|
-
isLoadingSignal.set(false);
|
|
196
|
-
}
|
|
185
|
+
return fetchTimeSlots({ isLoadMore: true });
|
|
197
186
|
};
|
|
198
187
|
/**
|
|
199
188
|
* Action: Update date range and refetch slots
|
|
@@ -207,22 +196,19 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
207
196
|
}
|
|
208
197
|
startDateSignal.set(newStartDate);
|
|
209
198
|
endDateSignal.set(newEndDate);
|
|
210
|
-
fetchTimeSlots();
|
|
211
199
|
};
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// Subscribe to timezone changes from BookingService to auto-refetch
|
|
217
|
-
// NOTE: This should depend ONLY on bookingService.timezone.
|
|
218
|
-
let firstTimezoneEffectRun = true;
|
|
200
|
+
const resourcesSignal = signalsService.computed(() => {
|
|
201
|
+
return bookingService.serviceSelections.get()[0]?.resources;
|
|
202
|
+
});
|
|
203
|
+
let isFirstRun = true;
|
|
219
204
|
signalsService.effect(() => {
|
|
220
205
|
bookingService.timezone.get();
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
206
|
+
bookingService.location.get();
|
|
207
|
+
resourcesSignal.get();
|
|
208
|
+
if (!isFirstRun || !config.timeSlots) {
|
|
209
|
+
fetchTimeSlots();
|
|
224
210
|
}
|
|
225
|
-
|
|
211
|
+
isFirstRun = false;
|
|
226
212
|
});
|
|
227
213
|
// Return service API
|
|
228
214
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-bookings",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.35",
|
|
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": "ad087858a42d4f0c4cf86b870ec3b6b93fac74806bacb2f95cf753a2"
|
|
76
76
|
}
|