@wix/headless-bookings 0.0.15 → 0.0.16
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/booking/Booking.d.ts +17 -9
- package/cjs/dist/react/core/booking/Booking.js +13 -7
- package/cjs/dist/services/booking/booking.d.ts +48 -30
- package/cjs/dist/services/booking/booking.js +74 -44
- package/cjs/dist/services/index.d.ts +1 -1
- package/cjs/dist/services/service/service.js +10 -7
- package/cjs/dist/services/time-slot-list/time-slot-list.js +109 -56
- package/dist/react/core/booking/Booking.d.ts +17 -9
- package/dist/react/core/booking/Booking.js +13 -7
- package/dist/services/booking/booking.d.ts +48 -30
- package/dist/services/booking/booking.js +74 -44
- package/dist/services/index.d.ts +1 -1
- package/dist/services/service/service.js +10 -7
- package/dist/services/time-slot-list/time-slot-list.js +109 -56
- package/package.json +2 -2
|
@@ -6,7 +6,7 @@ import { implementService } from '@wix/services-definitions';
|
|
|
6
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/time-slot-list/index.js';
|
|
9
|
-
import { BookingServiceDefinition } from '../booking/booking.js';
|
|
9
|
+
import { BookingServiceDefinition, } from '../booking/booking.js';
|
|
10
10
|
// ============================================================================
|
|
11
11
|
// Helpers
|
|
12
12
|
// ============================================================================
|
|
@@ -79,7 +79,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
79
79
|
const getServices = () => {
|
|
80
80
|
if (config.services?.length)
|
|
81
81
|
return config.services;
|
|
82
|
-
return bookingService.
|
|
82
|
+
return bookingService.bookingItems.get().map((s) => s.service);
|
|
83
83
|
};
|
|
84
84
|
/**
|
|
85
85
|
* Fetch time slots from API
|
|
@@ -115,82 +115,139 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
115
115
|
};
|
|
116
116
|
/**
|
|
117
117
|
* Action: Select a time slot
|
|
118
|
-
*
|
|
119
|
-
*
|
|
118
|
+
* Auto-selects location/staff based on availability:
|
|
119
|
+
* - One location + one staff → auto-select both
|
|
120
|
+
* - One location + many staff → auto-select location only
|
|
121
|
+
* - Many locations + one staff → auto-select staff only
|
|
122
|
+
* - Many locations + many staff → don't auto-select anything
|
|
120
123
|
*/
|
|
121
124
|
const selectSlot = (slot) => {
|
|
122
125
|
const currentSelection = selectionSignal.get();
|
|
123
126
|
if (currentSelection?.slot?.id === slot.id)
|
|
124
127
|
return;
|
|
128
|
+
const timeSlot = {
|
|
129
|
+
startDate: slot.localStartDate,
|
|
130
|
+
endDate: slot.localEndDate,
|
|
131
|
+
totalCapacity: slot.totalCapacity ?? undefined,
|
|
132
|
+
remainingCapacity: slot.remainingCapacity ?? undefined,
|
|
133
|
+
scheduleId: slot.scheduleId ?? undefined,
|
|
134
|
+
eventInfo: slot.eventInfo ?? undefined,
|
|
135
|
+
};
|
|
125
136
|
const hasOneLocation = slot.staffByLocation.length === 1;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
bookingService.actions.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
137
|
+
// Collect all unique staff members across all locations
|
|
138
|
+
const allStaffMembers = slot.staffByLocation.flatMap((loc) => loc.staffMembers);
|
|
139
|
+
const uniqueStaffIds = new Set(allStaffMembers.map((s) => s.id));
|
|
140
|
+
const hasOneStaff = uniqueStaffIds.size === 1;
|
|
141
|
+
// Determine what to auto-select
|
|
142
|
+
let locationId = null;
|
|
143
|
+
let staffMemberId = null;
|
|
144
|
+
if (hasOneLocation) {
|
|
145
|
+
const { location } = slot.staffByLocation[0];
|
|
146
|
+
locationId = location.id;
|
|
147
|
+
bookingService.actions.setLocation(location);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
bookingService.actions.clearLocation();
|
|
151
|
+
}
|
|
152
|
+
if (hasOneStaff) {
|
|
153
|
+
const staffMember = allStaffMembers[0];
|
|
154
|
+
staffMemberId = staffMember.id;
|
|
155
|
+
bookingService.actions.updateItem({
|
|
156
|
+
timeSlot,
|
|
157
|
+
resources: [
|
|
158
|
+
{
|
|
159
|
+
_id: staffMemberId,
|
|
160
|
+
name: staffMember.name,
|
|
161
|
+
},
|
|
162
|
+
],
|
|
146
163
|
});
|
|
147
164
|
}
|
|
148
165
|
else {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
staffMemberId: null,
|
|
166
|
+
bookingService.actions.updateItem({
|
|
167
|
+
timeSlot,
|
|
168
|
+
resources: [],
|
|
153
169
|
});
|
|
154
170
|
}
|
|
171
|
+
selectionSignal.set({
|
|
172
|
+
slot,
|
|
173
|
+
locationId,
|
|
174
|
+
staffMemberId,
|
|
175
|
+
});
|
|
155
176
|
};
|
|
156
177
|
/**
|
|
157
178
|
* Action: Select a location for the current slot
|
|
158
|
-
* Updates the booking service with the
|
|
179
|
+
* Updates the booking service with the location
|
|
180
|
+
* If the location has only one staff member, auto-selects that staff member too
|
|
159
181
|
*/
|
|
160
182
|
const selectLocation = (locationId) => {
|
|
161
183
|
const currentSelections = selectionSignal.get();
|
|
162
184
|
if (!currentSelections?.slot)
|
|
163
185
|
return;
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
if (
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
186
|
+
const slot = currentSelections.slot;
|
|
187
|
+
const locationEntry = slot.staffByLocation.find((loc) => loc.location.id === locationId);
|
|
188
|
+
if (!locationEntry)
|
|
189
|
+
return;
|
|
190
|
+
const { location, staffMembers } = locationEntry;
|
|
191
|
+
bookingService.actions.setLocation(location);
|
|
192
|
+
// If this location has only one staff member, auto-select them
|
|
193
|
+
const hasOneStaff = staffMembers.length === 1;
|
|
194
|
+
if (hasOneStaff) {
|
|
195
|
+
const staffMember = staffMembers[0];
|
|
196
|
+
selectionSignal.set({
|
|
197
|
+
...currentSelections,
|
|
198
|
+
locationId,
|
|
199
|
+
staffMemberId: staffMember.id,
|
|
170
200
|
});
|
|
201
|
+
bookingService.actions.updateItem({
|
|
202
|
+
resources: [
|
|
203
|
+
{
|
|
204
|
+
_id: staffMember.id,
|
|
205
|
+
name: staffMember.name,
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
selectionSignal.set({ ...currentSelections, locationId });
|
|
171
212
|
}
|
|
172
213
|
};
|
|
173
214
|
/**
|
|
174
215
|
* Action: Select a staff member for the current slot
|
|
175
|
-
* Updates the booking service with the selected resource
|
|
216
|
+
* Updates the booking service with the selected resource
|
|
217
|
+
* If the staff member is only available at one location, auto-selects that location too
|
|
176
218
|
*/
|
|
177
219
|
const selectStaffMember = (staffMemberId) => {
|
|
178
220
|
const currentSelections = selectionSignal.get();
|
|
179
221
|
if (!currentSelections?.slot)
|
|
180
222
|
return;
|
|
181
|
-
selectionSignal.set({ ...currentSelections, staffMemberId });
|
|
182
223
|
const slot = currentSelections.slot;
|
|
183
|
-
// Find
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
224
|
+
// Find all locations that have this staff member
|
|
225
|
+
const locationsWithThisStaff = slot.staffByLocation.filter((loc) => loc.staffMembers.some((s) => s.id === staffMemberId));
|
|
226
|
+
// Find the staff member info
|
|
227
|
+
const staffMember = locationsWithThisStaff[0]?.staffMembers.find((s) => s.id === staffMemberId);
|
|
228
|
+
if (!staffMember)
|
|
229
|
+
return;
|
|
230
|
+
bookingService.actions.updateItem({
|
|
231
|
+
resources: [
|
|
232
|
+
{
|
|
233
|
+
_id: staffMember.id,
|
|
234
|
+
name: staffMember.name,
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
});
|
|
238
|
+
// If this staff member is only at one location, auto-select that location
|
|
239
|
+
const hasOneLocation = locationsWithThisStaff.length === 1;
|
|
240
|
+
if (hasOneLocation) {
|
|
241
|
+
const location = locationsWithThisStaff[0].location;
|
|
242
|
+
selectionSignal.set({
|
|
243
|
+
...currentSelections,
|
|
244
|
+
staffMemberId,
|
|
245
|
+
locationId: location.id,
|
|
246
|
+
});
|
|
247
|
+
bookingService.actions.setLocation(location);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
selectionSignal.set({ ...currentSelections, staffMemberId });
|
|
194
251
|
}
|
|
195
252
|
};
|
|
196
253
|
/**
|
|
@@ -206,15 +263,11 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
206
263
|
locationId: null,
|
|
207
264
|
staffMemberId: null,
|
|
208
265
|
});
|
|
209
|
-
// Clear
|
|
266
|
+
// Clear selections in booking service
|
|
210
267
|
bookingService.actions.clearLocation();
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
bookingService.actions.updateBookingSelection({
|
|
215
|
-
staff: undefined,
|
|
216
|
-
});
|
|
217
|
-
}
|
|
268
|
+
bookingService.actions.updateItem({
|
|
269
|
+
resources: [],
|
|
270
|
+
});
|
|
218
271
|
};
|
|
219
272
|
/**
|
|
220
273
|
* Action: Load more slots (pagination)
|
|
@@ -3,8 +3,8 @@
|
|
|
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
|
|
7
|
-
import type {
|
|
6
|
+
import { type BookingServiceConfig, type BookingItem, type Location, type BookingServiceActions } from '../../../services/booking/booking.js';
|
|
7
|
+
import type { FormValues } from '@wix/form-public';
|
|
8
8
|
/**
|
|
9
9
|
* Props for Booking.Root component
|
|
10
10
|
*/
|
|
@@ -28,14 +28,22 @@ export interface RootProps {
|
|
|
28
28
|
* ```
|
|
29
29
|
*/
|
|
30
30
|
export declare function Root(props: RootProps): React.ReactNode;
|
|
31
|
+
/**
|
|
32
|
+
* Data exposed by Booking.Data component
|
|
33
|
+
* Mirrors BookingServiceConfig structure with actions
|
|
34
|
+
*/
|
|
35
|
+
export interface BookingData {
|
|
36
|
+
bookingItems: BookingItem[];
|
|
37
|
+
location: Location | null;
|
|
38
|
+
formValues: FormValues | null;
|
|
39
|
+
numberOfParticipants: number;
|
|
40
|
+
actions: BookingServiceActions;
|
|
41
|
+
}
|
|
31
42
|
/**
|
|
32
43
|
* Props for Data render prop component
|
|
33
44
|
*/
|
|
34
45
|
export interface DataProps {
|
|
35
|
-
children: (data:
|
|
36
|
-
bookingSelections: BookingSelection[];
|
|
37
|
-
selectedTimeSlot: TimeSlot | null;
|
|
38
|
-
}) => React.ReactNode;
|
|
46
|
+
children: (data: BookingData) => React.ReactNode;
|
|
39
47
|
}
|
|
40
48
|
/**
|
|
41
49
|
* Core component that provides access to booking data via render props.
|
|
@@ -45,9 +53,9 @@ export interface DataProps {
|
|
|
45
53
|
* @example
|
|
46
54
|
* ```tsx
|
|
47
55
|
* <Booking.Data>
|
|
48
|
-
* {({
|
|
49
|
-
*
|
|
50
|
-
* <div>{
|
|
56
|
+
* {({ bookingItems, location, formValues, numberOfParticipants, actions }) => (
|
|
57
|
+
* bookingItems.length > 0 ? (
|
|
58
|
+
* <div>{bookingItems.map(item => item.service.name).join(', ')}</div>
|
|
51
59
|
* ) : (
|
|
52
60
|
* <div>No services selected</div>
|
|
53
61
|
* )
|
|
@@ -28,9 +28,9 @@ export function Root(props) {
|
|
|
28
28
|
* @example
|
|
29
29
|
* ```tsx
|
|
30
30
|
* <Booking.Data>
|
|
31
|
-
* {({
|
|
32
|
-
*
|
|
33
|
-
* <div>{
|
|
31
|
+
* {({ bookingItems, location, formValues, numberOfParticipants, actions }) => (
|
|
32
|
+
* bookingItems.length > 0 ? (
|
|
33
|
+
* <div>{bookingItems.map(item => item.service.name).join(', ')}</div>
|
|
34
34
|
* ) : (
|
|
35
35
|
* <div>No services selected</div>
|
|
36
36
|
* )
|
|
@@ -41,10 +41,16 @@ export function Root(props) {
|
|
|
41
41
|
export function Data(props) {
|
|
42
42
|
const { children } = props;
|
|
43
43
|
const bookingService = useService(BookingServiceDefinition);
|
|
44
|
-
const
|
|
45
|
-
const
|
|
44
|
+
const bookingItems = bookingService.bookingItems.get();
|
|
45
|
+
const location = bookingService.location.get();
|
|
46
|
+
const formValues = bookingService.formValues.get();
|
|
47
|
+
const numberOfParticipants = bookingService.numberOfParticipants.get();
|
|
48
|
+
const { actions } = bookingService;
|
|
46
49
|
return children({
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
bookingItems,
|
|
51
|
+
location,
|
|
52
|
+
formValues,
|
|
53
|
+
numberOfParticipants,
|
|
54
|
+
actions,
|
|
49
55
|
});
|
|
50
56
|
}
|
|
@@ -5,19 +5,31 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
7
7
|
import type { Service } from '@wix/auto_sdk_bookings_services';
|
|
8
|
-
import type {
|
|
8
|
+
import type { Resource, EventInfo } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
9
9
|
import type { FormValues } from '@wix/form-public';
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Minimal time slot data for booking items
|
|
12
12
|
*/
|
|
13
|
-
export interface
|
|
13
|
+
export interface BookingItemTimeSlot {
|
|
14
|
+
startDate: string;
|
|
15
|
+
endDate: string;
|
|
16
|
+
totalCapacity?: number;
|
|
17
|
+
remainingCapacity?: number;
|
|
18
|
+
scheduleId?: string;
|
|
19
|
+
eventInfo?: EventInfo;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* BookingItem contains the service AND its related selections
|
|
23
|
+
*/
|
|
24
|
+
export interface BookingItem {
|
|
14
25
|
/** Unique identifier for this booking instance (allows same service multiple times) */
|
|
15
26
|
instanceId: string;
|
|
27
|
+
/** The selected service */
|
|
16
28
|
service: Service;
|
|
17
29
|
/** Selected resources (staff members, rooms, etc.) */
|
|
18
|
-
|
|
19
|
-
/** Per-
|
|
20
|
-
|
|
30
|
+
resources: Resource[];
|
|
31
|
+
/** Per-item time slot data */
|
|
32
|
+
timeSlot?: BookingItemTimeSlot;
|
|
21
33
|
}
|
|
22
34
|
export interface Location {
|
|
23
35
|
id: string;
|
|
@@ -27,21 +39,25 @@ export interface Location {
|
|
|
27
39
|
* BookingService actions - setters only, no API calls
|
|
28
40
|
*/
|
|
29
41
|
export interface BookingServiceActions {
|
|
30
|
-
/** Clears all booking
|
|
31
|
-
|
|
32
|
-
/** Adds
|
|
33
|
-
|
|
34
|
-
/** Updates a booking
|
|
35
|
-
|
|
36
|
-
/** Removes a booking
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
/** Clears all booking items and sets a single item (instanceId is auto-generated) */
|
|
43
|
+
setItem(item: Omit<BookingItem, 'instanceId'>): string;
|
|
44
|
+
/** Adds an item and returns the generated instanceId */
|
|
45
|
+
addItem(item: Omit<BookingItem, 'instanceId'>): string;
|
|
46
|
+
/** Updates a booking item. If no instanceId provided, updates the first item. */
|
|
47
|
+
updateItem(updates: Partial<Omit<BookingItem, 'instanceId'>>, instanceId?: string): void;
|
|
48
|
+
/** Removes a booking item. If no instanceId provided, removes the first item. */
|
|
49
|
+
removeItem(instanceId?: string): void;
|
|
50
|
+
/** Clears all booking items */
|
|
51
|
+
clearItems(): void;
|
|
52
|
+
/** Sets a resource by ID within a booking item. If no instanceId provided, uses the first item. */
|
|
53
|
+
setResourceById(resourceId: string, resource: Resource, instanceId?: string): void;
|
|
54
|
+
/** Gets a resource by ID from a booking item. If no instanceId provided, uses the first item. */
|
|
55
|
+
getResourceById(resourceId: string, instanceId?: string): Resource | undefined;
|
|
41
56
|
setLocation(location: Location | null): void;
|
|
42
57
|
clearLocation(): void;
|
|
43
58
|
setFormValues(values: FormValues | null): void;
|
|
44
59
|
clearFormValues(): void;
|
|
60
|
+
setNumberOfParticipants(count: number): void;
|
|
45
61
|
clearAll(): void;
|
|
46
62
|
}
|
|
47
63
|
/**
|
|
@@ -49,14 +65,14 @@ export interface BookingServiceActions {
|
|
|
49
65
|
* Pure state management - no API calls
|
|
50
66
|
*/
|
|
51
67
|
export interface BookingService {
|
|
52
|
-
/** Signal containing the booking
|
|
53
|
-
|
|
54
|
-
/** Signal containing the
|
|
55
|
-
timeSlot: Signal<TimeSlot | null>;
|
|
56
|
-
/** Signal containing the selected location (derived from timeSlot) */
|
|
68
|
+
/** Signal containing the booking items (services with their related data) */
|
|
69
|
+
bookingItems: Signal<BookingItem[]>;
|
|
70
|
+
/** Signal containing the selected location */
|
|
57
71
|
location: Signal<Location | null>;
|
|
58
72
|
/** Signal containing the form values */
|
|
59
73
|
formValues: Signal<FormValues | null>;
|
|
74
|
+
/** Signal containing the number of participants */
|
|
75
|
+
numberOfParticipants: Signal<number>;
|
|
60
76
|
/** Actions for modifying booking state */
|
|
61
77
|
actions: BookingServiceActions;
|
|
62
78
|
}
|
|
@@ -70,15 +86,17 @@ export declare const BookingServiceDefinition: string & {
|
|
|
70
86
|
} & BookingService;
|
|
71
87
|
/**
|
|
72
88
|
* Configuration interface for BookingService
|
|
73
|
-
*
|
|
89
|
+
* Mirrors BookingService structure (without actions and Signal wrappers)
|
|
74
90
|
*/
|
|
75
91
|
export interface BookingServiceConfig {
|
|
76
|
-
/** Optional initial booking
|
|
77
|
-
|
|
78
|
-
/** Optional initial
|
|
79
|
-
|
|
92
|
+
/** Optional initial booking items to pre-populate */
|
|
93
|
+
bookingItems?: BookingItem[];
|
|
94
|
+
/** Optional initial location */
|
|
95
|
+
location?: Location | null;
|
|
80
96
|
/** Optional initial form values */
|
|
81
|
-
|
|
97
|
+
formValues?: FormValues | null;
|
|
98
|
+
/** Optional initial number of participants (defaults to 1) */
|
|
99
|
+
numberOfParticipants?: number;
|
|
82
100
|
}
|
|
83
101
|
/**
|
|
84
102
|
* Implementation of BookingService
|
|
@@ -98,8 +116,8 @@ export interface BookingServiceConfig {
|
|
|
98
116
|
*
|
|
99
117
|
* // In any page - access booking data
|
|
100
118
|
* <Booking.Data>
|
|
101
|
-
* {({
|
|
102
|
-
* <div>{
|
|
119
|
+
* {({ bookingItems, location, formValues, actions }) => (
|
|
120
|
+
* <div>{bookingItems.map(item => item.service.name).join(', ')}</div>
|
|
103
121
|
* )}
|
|
104
122
|
* </Booking.Data>
|
|
105
123
|
* ```
|
|
@@ -9,7 +9,7 @@ import { SignalsServiceDefinition, } from '@wix/services-definitions/core-servic
|
|
|
9
9
|
// Types
|
|
10
10
|
// ============================================================================
|
|
11
11
|
/**
|
|
12
|
-
* Generates a unique instance ID for
|
|
12
|
+
* Generates a unique instance ID for booking items
|
|
13
13
|
*/
|
|
14
14
|
function generateInstanceId() {
|
|
15
15
|
return crypto.randomUUID();
|
|
@@ -39,8 +39,8 @@ export const BookingServiceDefinition = defineService('booking');
|
|
|
39
39
|
*
|
|
40
40
|
* // In any page - access booking data
|
|
41
41
|
* <Booking.Data>
|
|
42
|
-
* {({
|
|
43
|
-
* <div>{
|
|
42
|
+
* {({ bookingItems, location, formValues, actions }) => (
|
|
43
|
+
* <div>{bookingItems.map(item => item.service.name).join(', ')}</div>
|
|
44
44
|
* )}
|
|
45
45
|
* </Booking.Data>
|
|
46
46
|
* ```
|
|
@@ -48,67 +48,93 @@ export const BookingServiceDefinition = defineService('booking');
|
|
|
48
48
|
export const BookingService = implementService.withConfig()(BookingServiceDefinition, ({ getService, config }) => {
|
|
49
49
|
const signalsService = getService(SignalsServiceDefinition);
|
|
50
50
|
// Initialize signals with config values
|
|
51
|
-
// Ensure all initial booking
|
|
52
|
-
const
|
|
53
|
-
...
|
|
54
|
-
instanceId:
|
|
51
|
+
// Ensure all initial booking items have instanceIds
|
|
52
|
+
const initialItemsWithIds = (config.bookingItems || []).map((item) => ({
|
|
53
|
+
...item,
|
|
54
|
+
instanceId: item.instanceId || generateInstanceId(),
|
|
55
55
|
}));
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
const formValuesSignal = signalsService.signal(config.initialFormValues || null);
|
|
56
|
+
const bookingItemsSignal = signalsService.signal(initialItemsWithIds);
|
|
57
|
+
const locationSignal = signalsService.signal(config.location || null);
|
|
58
|
+
const formValuesSignal = signalsService.signal(config.formValues || null);
|
|
59
|
+
const numberOfParticipantsSignal = signalsService.signal(config.numberOfParticipants ?? 1);
|
|
61
60
|
// ========================================================================
|
|
62
61
|
// Actions Implementation
|
|
63
62
|
// ========================================================================
|
|
64
63
|
const actions = {
|
|
65
|
-
//
|
|
66
|
-
|
|
64
|
+
// Item management actions
|
|
65
|
+
setItem(item) {
|
|
67
66
|
const instanceId = generateInstanceId();
|
|
68
|
-
const
|
|
69
|
-
...
|
|
67
|
+
const newItem = {
|
|
68
|
+
...item,
|
|
70
69
|
instanceId,
|
|
71
70
|
};
|
|
72
|
-
|
|
71
|
+
bookingItemsSignal.set([newItem]);
|
|
73
72
|
return instanceId;
|
|
74
73
|
},
|
|
75
|
-
|
|
74
|
+
addItem(item) {
|
|
76
75
|
const instanceId = generateInstanceId();
|
|
77
|
-
const current =
|
|
78
|
-
const
|
|
79
|
-
...
|
|
76
|
+
const current = bookingItemsSignal.get();
|
|
77
|
+
const newItem = {
|
|
78
|
+
...item,
|
|
80
79
|
instanceId,
|
|
81
80
|
};
|
|
82
|
-
|
|
81
|
+
bookingItemsSignal.set([...current, newItem]);
|
|
83
82
|
return instanceId;
|
|
84
83
|
},
|
|
85
|
-
|
|
86
|
-
const current =
|
|
87
|
-
// If no instanceId provided, update the first
|
|
84
|
+
updateItem(updates, instanceId) {
|
|
85
|
+
const current = bookingItemsSignal.get();
|
|
86
|
+
// If no instanceId provided, update the first item (index 0)
|
|
88
87
|
const targetId = instanceId ?? current[0]?.instanceId;
|
|
89
|
-
const updated = current.map((
|
|
90
|
-
|
|
88
|
+
const updated = current.map((item) => item.instanceId === targetId ? { ...item, ...updates } : item);
|
|
89
|
+
bookingItemsSignal.set(updated);
|
|
91
90
|
},
|
|
92
|
-
|
|
93
|
-
const current =
|
|
91
|
+
removeItem(instanceId) {
|
|
92
|
+
const current = bookingItemsSignal.get();
|
|
94
93
|
if (!instanceId) {
|
|
95
|
-
// Remove first
|
|
96
|
-
|
|
94
|
+
// Remove first item if no instanceId provided
|
|
95
|
+
bookingItemsSignal.set(current.slice(1));
|
|
97
96
|
}
|
|
98
97
|
else {
|
|
99
|
-
const filtered = current.filter((
|
|
100
|
-
|
|
98
|
+
const filtered = current.filter((item) => item.instanceId !== instanceId);
|
|
99
|
+
bookingItemsSignal.set(filtered);
|
|
101
100
|
}
|
|
102
101
|
},
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
clearItems() {
|
|
103
|
+
bookingItemsSignal.set([]);
|
|
105
104
|
},
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
// Resource helpers
|
|
106
|
+
setResourceById(resourceId, resource, instanceId) {
|
|
107
|
+
const current = bookingItemsSignal.get();
|
|
108
|
+
// If no instanceId provided, use the first item
|
|
109
|
+
const targetId = instanceId ?? current[0]?.instanceId;
|
|
110
|
+
if (!targetId)
|
|
111
|
+
return;
|
|
112
|
+
const updated = current.map((item) => {
|
|
113
|
+
if (item.instanceId !== targetId)
|
|
114
|
+
return item;
|
|
115
|
+
// Find and replace resource by ID, or add if not found
|
|
116
|
+
const existingIndex = item.resources.findIndex((r) => r._id === resourceId);
|
|
117
|
+
const newResources = [...item.resources];
|
|
118
|
+
if (existingIndex >= 0) {
|
|
119
|
+
newResources[existingIndex] = resource;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
newResources.push(resource);
|
|
123
|
+
}
|
|
124
|
+
return { ...item, resources: newResources };
|
|
125
|
+
});
|
|
126
|
+
bookingItemsSignal.set(updated);
|
|
109
127
|
},
|
|
110
|
-
|
|
111
|
-
|
|
128
|
+
getResourceById(resourceId, instanceId) {
|
|
129
|
+
const current = bookingItemsSignal.get();
|
|
130
|
+
// If no instanceId provided, use the first item
|
|
131
|
+
const targetId = instanceId ?? current[0]?.instanceId;
|
|
132
|
+
if (!targetId)
|
|
133
|
+
return undefined;
|
|
134
|
+
const targetItem = current.find((item) => item.instanceId === targetId);
|
|
135
|
+
if (!targetItem)
|
|
136
|
+
return undefined;
|
|
137
|
+
return targetItem.resources.find((r) => r._id === resourceId);
|
|
112
138
|
},
|
|
113
139
|
// Location actions
|
|
114
140
|
setLocation(location) {
|
|
@@ -124,19 +150,23 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
124
150
|
clearFormValues() {
|
|
125
151
|
formValuesSignal.set(null);
|
|
126
152
|
},
|
|
153
|
+
// Number of Participants actions
|
|
154
|
+
setNumberOfParticipants(count) {
|
|
155
|
+
numberOfParticipantsSignal.set(count);
|
|
156
|
+
},
|
|
127
157
|
// Clear all
|
|
128
158
|
clearAll() {
|
|
129
|
-
|
|
130
|
-
timeSlotSignal.set(null);
|
|
159
|
+
bookingItemsSignal.set([]);
|
|
131
160
|
locationSignal.set(null);
|
|
132
161
|
formValuesSignal.set(null);
|
|
162
|
+
numberOfParticipantsSignal.set(1);
|
|
133
163
|
},
|
|
134
164
|
};
|
|
135
165
|
return {
|
|
136
|
-
|
|
137
|
-
timeSlot: timeSlotSignal,
|
|
166
|
+
bookingItems: bookingItemsSignal,
|
|
138
167
|
location: locationSignal,
|
|
139
168
|
formValues: formValuesSignal,
|
|
169
|
+
numberOfParticipants: numberOfParticipantsSignal,
|
|
140
170
|
actions,
|
|
141
171
|
};
|
|
142
172
|
});
|
package/dist/services/index.d.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
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 Location, type BookingServiceActions, } from './booking/booking.js';
|
|
7
7
|
export { TimeSlotListService, TimeSlotListServiceDefinition, ViewMode, loadTimeSlotListConfig, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type SlotSelection, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list/index.js';
|
|
8
8
|
export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
|