@wix/headless-bookings 0.0.33 → 0.0.34
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/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/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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { Resource } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
5
5
|
import type { FormValues } from '@wix/form-public';
|
|
6
|
-
import type { ServiceSelection, ServiceSelectionTimeSlot,
|
|
6
|
+
import type { ServiceSelection, ServiceSelectionTimeSlot, BookingServiceConfig } from '../services/booking/booking.js';
|
|
7
7
|
/**
|
|
8
8
|
* Mock resource (staff member)
|
|
9
9
|
*/
|
|
@@ -19,11 +19,17 @@ export declare const mockResourceRoom: Resource;
|
|
|
19
19
|
/**
|
|
20
20
|
* Mock location
|
|
21
21
|
*/
|
|
22
|
-
export declare const mockLocation:
|
|
22
|
+
export declare const mockLocation: {
|
|
23
|
+
_id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
};
|
|
23
26
|
/**
|
|
24
27
|
* Mock location (second)
|
|
25
28
|
*/
|
|
26
|
-
export declare const mockLocationTwo:
|
|
29
|
+
export declare const mockLocationTwo: {
|
|
30
|
+
_id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
};
|
|
27
33
|
/**
|
|
28
34
|
* Mock time slot data
|
|
29
35
|
*/
|
|
@@ -32,14 +32,14 @@ export const mockResourceRoom = {
|
|
|
32
32
|
* Mock location
|
|
33
33
|
*/
|
|
34
34
|
export const mockLocation = {
|
|
35
|
-
|
|
35
|
+
_id: 'location-1',
|
|
36
36
|
name: 'Main Office',
|
|
37
37
|
};
|
|
38
38
|
/**
|
|
39
39
|
* Mock location (second)
|
|
40
40
|
*/
|
|
41
41
|
export const mockLocationTwo = {
|
|
42
|
-
|
|
42
|
+
_id: 'location-2',
|
|
43
43
|
name: 'Downtown Branch',
|
|
44
44
|
};
|
|
45
45
|
/**
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* Provides low-level access to BookingService via render props and service setup
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
|
-
import { type BookingServiceConfig, type ServiceSelection, type
|
|
6
|
+
import { type BookingServiceConfig, type ServiceSelection, type BookingServiceActions } from '../../../services/booking/booking.js';
|
|
7
7
|
import type { FormValues } from '@wix/form-public';
|
|
8
|
+
import type { Location as TimeSlotLocationType } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
8
9
|
/**
|
|
9
10
|
* Props for Booking.Root component
|
|
10
11
|
*/
|
|
@@ -34,7 +35,7 @@ export declare function Root(props: RootProps): React.ReactNode;
|
|
|
34
35
|
*/
|
|
35
36
|
export interface BookingData {
|
|
36
37
|
serviceSelections: ServiceSelection[];
|
|
37
|
-
location:
|
|
38
|
+
location: TimeSlotLocationType | null;
|
|
38
39
|
timezone: string | undefined;
|
|
39
40
|
formSubmission: FormValues | null;
|
|
40
41
|
actions: BookingServiceActions;
|
|
@@ -31,9 +31,8 @@ export function TimeSlotProvider(props) {
|
|
|
31
31
|
const service = useService(TimeSlotListServiceDefinition);
|
|
32
32
|
const bookingService = useService(BookingServiceDefinition);
|
|
33
33
|
const selectedTimeSlot = bookingService.serviceSelections.get()[0]?.timeSlot;
|
|
34
|
-
const selectedLocationId = bookingService.location.get()?.id;
|
|
35
34
|
const isSelected = selectedTimeSlot?.startDate === timeSlot.localStartDate &&
|
|
36
|
-
|
|
35
|
+
selectedTimeSlot?.location?._id === timeSlot.location?._id;
|
|
37
36
|
const selectTimeSlot = () => {
|
|
38
37
|
service.actions.selectSlot(timeSlot);
|
|
39
38
|
};
|
|
@@ -132,7 +131,7 @@ export function Actions(props) {
|
|
|
132
131
|
const service = useService(TimeSlotListServiceDefinition);
|
|
133
132
|
const bookingService = useService(BookingServiceDefinition);
|
|
134
133
|
const bookable = timeSlot.bookable ?? false;
|
|
135
|
-
const selectedStaffMemberId = bookingService.serviceSelections.get()[0]?.resources?.[0]?._id;
|
|
134
|
+
const selectedStaffMemberId = bookingService.serviceSelections.get()[0]?.timeSlot?.resources?.[0]?._id;
|
|
136
135
|
const staffFromSlot = timeSlot.availableResources[0].resources.map((r) => r._id);
|
|
137
136
|
const shouldShowClearStaffSelection = !!selectedStaffMemberId && staffFromSlot.length > 1;
|
|
138
137
|
const clearStaffSelection = shouldShowClearStaffSelection
|
|
@@ -169,7 +168,7 @@ export function StaffMembers(props) {
|
|
|
169
168
|
const { timeSlot } = useTimeSlotContext();
|
|
170
169
|
const service = useService(TimeSlotListServiceDefinition);
|
|
171
170
|
const bookingService = useService(BookingServiceDefinition);
|
|
172
|
-
const selectedStaffMemberId = bookingService.serviceSelections.get()[0]?.resources?.[0]?._id;
|
|
171
|
+
const selectedStaffMemberId = bookingService.serviceSelections.get()[0]?.timeSlot?.resources?.[0]?._id;
|
|
173
172
|
const staffMembers = timeSlot.availableResources[0].resources;
|
|
174
173
|
// Return null if only 1 staff member - nothing to choose from
|
|
175
174
|
if (staffMembers.length <= 1) {
|
|
@@ -45,7 +45,7 @@ export async function executeBookAction(params) {
|
|
|
45
45
|
// Step 2: Create checkout
|
|
46
46
|
// businessLocationId: prefer timeSlot.location, fallback to global location
|
|
47
47
|
const firstSelection = params.serviceSelections[0];
|
|
48
|
-
const businessLocationId = firstSelection?.timeSlot?.location?.
|
|
48
|
+
const businessLocationId = firstSelection?.timeSlot?.location?._id ?? undefined;
|
|
49
49
|
const checkoutRequest = buildCheckoutRequest({
|
|
50
50
|
bookingId,
|
|
51
51
|
contactDetails,
|
|
@@ -69,7 +69,7 @@ export function buildBookingRequest(params) {
|
|
|
69
69
|
? { _id: resource._id, name: resource.name }
|
|
70
70
|
: undefined,
|
|
71
71
|
location: slotLocation
|
|
72
|
-
? { _id: slotLocation.
|
|
72
|
+
? { _id: slotLocation._id, name: slotLocation.name }
|
|
73
73
|
: undefined,
|
|
74
74
|
},
|
|
75
75
|
},
|
|
@@ -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 */
|
|
@@ -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 {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { Resource } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
5
5
|
import type { FormValues } from '@wix/form-public';
|
|
6
|
-
import type { ServiceSelection, ServiceSelectionTimeSlot,
|
|
6
|
+
import type { ServiceSelection, ServiceSelectionTimeSlot, BookingServiceConfig } from '../services/booking/booking.js';
|
|
7
7
|
/**
|
|
8
8
|
* Mock resource (staff member)
|
|
9
9
|
*/
|
|
@@ -19,11 +19,17 @@ export declare const mockResourceRoom: Resource;
|
|
|
19
19
|
/**
|
|
20
20
|
* Mock location
|
|
21
21
|
*/
|
|
22
|
-
export declare const mockLocation:
|
|
22
|
+
export declare const mockLocation: {
|
|
23
|
+
_id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
};
|
|
23
26
|
/**
|
|
24
27
|
* Mock location (second)
|
|
25
28
|
*/
|
|
26
|
-
export declare const mockLocationTwo:
|
|
29
|
+
export declare const mockLocationTwo: {
|
|
30
|
+
_id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
};
|
|
27
33
|
/**
|
|
28
34
|
* Mock time slot data
|
|
29
35
|
*/
|
|
@@ -32,14 +32,14 @@ export const mockResourceRoom = {
|
|
|
32
32
|
* Mock location
|
|
33
33
|
*/
|
|
34
34
|
export const mockLocation = {
|
|
35
|
-
|
|
35
|
+
_id: 'location-1',
|
|
36
36
|
name: 'Main Office',
|
|
37
37
|
};
|
|
38
38
|
/**
|
|
39
39
|
* Mock location (second)
|
|
40
40
|
*/
|
|
41
41
|
export const mockLocationTwo = {
|
|
42
|
-
|
|
42
|
+
_id: 'location-2',
|
|
43
43
|
name: 'Downtown Branch',
|
|
44
44
|
};
|
|
45
45
|
/**
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* Provides low-level access to BookingService via render props and service setup
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
|
-
import { type BookingServiceConfig, type ServiceSelection, type
|
|
6
|
+
import { type BookingServiceConfig, type ServiceSelection, type BookingServiceActions } from '../../../services/booking/booking.js';
|
|
7
7
|
import type { FormValues } from '@wix/form-public';
|
|
8
|
+
import type { Location as TimeSlotLocationType } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
8
9
|
/**
|
|
9
10
|
* Props for Booking.Root component
|
|
10
11
|
*/
|
|
@@ -34,7 +35,7 @@ export declare function Root(props: RootProps): React.ReactNode;
|
|
|
34
35
|
*/
|
|
35
36
|
export interface BookingData {
|
|
36
37
|
serviceSelections: ServiceSelection[];
|
|
37
|
-
location:
|
|
38
|
+
location: TimeSlotLocationType | null;
|
|
38
39
|
timezone: string | undefined;
|
|
39
40
|
formSubmission: FormValues | null;
|
|
40
41
|
actions: BookingServiceActions;
|
|
@@ -31,9 +31,8 @@ export function TimeSlotProvider(props) {
|
|
|
31
31
|
const service = useService(TimeSlotListServiceDefinition);
|
|
32
32
|
const bookingService = useService(BookingServiceDefinition);
|
|
33
33
|
const selectedTimeSlot = bookingService.serviceSelections.get()[0]?.timeSlot;
|
|
34
|
-
const selectedLocationId = bookingService.location.get()?.id;
|
|
35
34
|
const isSelected = selectedTimeSlot?.startDate === timeSlot.localStartDate &&
|
|
36
|
-
|
|
35
|
+
selectedTimeSlot?.location?._id === timeSlot.location?._id;
|
|
37
36
|
const selectTimeSlot = () => {
|
|
38
37
|
service.actions.selectSlot(timeSlot);
|
|
39
38
|
};
|
|
@@ -132,7 +131,7 @@ export function Actions(props) {
|
|
|
132
131
|
const service = useService(TimeSlotListServiceDefinition);
|
|
133
132
|
const bookingService = useService(BookingServiceDefinition);
|
|
134
133
|
const bookable = timeSlot.bookable ?? false;
|
|
135
|
-
const selectedStaffMemberId = bookingService.serviceSelections.get()[0]?.resources?.[0]?._id;
|
|
134
|
+
const selectedStaffMemberId = bookingService.serviceSelections.get()[0]?.timeSlot?.resources?.[0]?._id;
|
|
136
135
|
const staffFromSlot = timeSlot.availableResources[0].resources.map((r) => r._id);
|
|
137
136
|
const shouldShowClearStaffSelection = !!selectedStaffMemberId && staffFromSlot.length > 1;
|
|
138
137
|
const clearStaffSelection = shouldShowClearStaffSelection
|
|
@@ -169,7 +168,7 @@ export function StaffMembers(props) {
|
|
|
169
168
|
const { timeSlot } = useTimeSlotContext();
|
|
170
169
|
const service = useService(TimeSlotListServiceDefinition);
|
|
171
170
|
const bookingService = useService(BookingServiceDefinition);
|
|
172
|
-
const selectedStaffMemberId = bookingService.serviceSelections.get()[0]?.resources?.[0]?._id;
|
|
171
|
+
const selectedStaffMemberId = bookingService.serviceSelections.get()[0]?.timeSlot?.resources?.[0]?._id;
|
|
173
172
|
const staffMembers = timeSlot.availableResources[0].resources;
|
|
174
173
|
// Return null if only 1 staff member - nothing to choose from
|
|
175
174
|
if (staffMembers.length <= 1) {
|
|
@@ -45,7 +45,7 @@ export async function executeBookAction(params) {
|
|
|
45
45
|
// Step 2: Create checkout
|
|
46
46
|
// businessLocationId: prefer timeSlot.location, fallback to global location
|
|
47
47
|
const firstSelection = params.serviceSelections[0];
|
|
48
|
-
const businessLocationId = firstSelection?.timeSlot?.location?.
|
|
48
|
+
const businessLocationId = firstSelection?.timeSlot?.location?._id ?? undefined;
|
|
49
49
|
const checkoutRequest = buildCheckoutRequest({
|
|
50
50
|
bookingId,
|
|
51
51
|
contactDetails,
|
|
@@ -69,7 +69,7 @@ export function buildBookingRequest(params) {
|
|
|
69
69
|
? { _id: resource._id, name: resource.name }
|
|
70
70
|
: undefined,
|
|
71
71
|
location: slotLocation
|
|
72
|
-
? { _id: slotLocation.
|
|
72
|
+
? { _id: slotLocation._id, name: slotLocation.name }
|
|
73
73
|
: undefined,
|
|
74
74
|
},
|
|
75
75
|
},
|
|
@@ -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.34",
|
|
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": "b195003cbc18c0477da02f42dd5e5409c483f2de627832cbdb859424"
|
|
76
76
|
}
|