@wix/headless-bookings 0.0.11 → 0.0.12
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 +5 -5
- package/cjs/dist/react/core/booking/Booking.js +5 -5
- package/cjs/dist/services/booking/booking.d.ts +19 -11
- package/cjs/dist/services/booking/booking.js +48 -16
- package/cjs/dist/services/index.d.ts +1 -1
- package/cjs/dist/services/service/service.js +13 -14
- package/dist/react/core/booking/Booking.d.ts +5 -5
- package/dist/react/core/booking/Booking.js +5 -5
- package/dist/services/booking/booking.d.ts +19 -11
- package/dist/services/booking/booking.js +48 -16
- package/dist/services/index.d.ts +1 -1
- package/dist/services/service/service.js +13 -14
- package/package.json +2 -2
|
@@ -3,7 +3,7 @@
|
|
|
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
|
|
6
|
+
import { type BookingServiceConfig, type BookingSelection } from '../../../services/booking/booking.js';
|
|
7
7
|
import type { availabilityTimeSlots } from '@wix/bookings';
|
|
8
8
|
/**
|
|
9
9
|
* Props for Booking.Root component
|
|
@@ -33,7 +33,7 @@ export declare function Root(props: RootProps): React.ReactNode;
|
|
|
33
33
|
*/
|
|
34
34
|
export interface DataProps {
|
|
35
35
|
children: (data: {
|
|
36
|
-
|
|
36
|
+
bookingSelections: BookingSelection[];
|
|
37
37
|
selectedTimeSlot: availabilityTimeSlots.TimeSlot | null;
|
|
38
38
|
}) => React.ReactNode;
|
|
39
39
|
}
|
|
@@ -45,9 +45,9 @@ export interface DataProps {
|
|
|
45
45
|
* @example
|
|
46
46
|
* ```tsx
|
|
47
47
|
* <Booking.Data>
|
|
48
|
-
* {({
|
|
49
|
-
*
|
|
50
|
-
* <div>{
|
|
48
|
+
* {({ bookingSelections }) => (
|
|
49
|
+
* bookingSelections.length > 0 ? (
|
|
50
|
+
* <div>{bookingSelections.map(s => s.service.name).join(', ')}</div>
|
|
51
51
|
* ) : (
|
|
52
52
|
* <div>No services selected</div>
|
|
53
53
|
* )
|
|
@@ -28,9 +28,9 @@ export function Root(props) {
|
|
|
28
28
|
* @example
|
|
29
29
|
* ```tsx
|
|
30
30
|
* <Booking.Data>
|
|
31
|
-
* {({
|
|
32
|
-
*
|
|
33
|
-
* <div>{
|
|
31
|
+
* {({ bookingSelections }) => (
|
|
32
|
+
* bookingSelections.length > 0 ? (
|
|
33
|
+
* <div>{bookingSelections.map(s => s.service.name).join(', ')}</div>
|
|
34
34
|
* ) : (
|
|
35
35
|
* <div>No services selected</div>
|
|
36
36
|
* )
|
|
@@ -41,10 +41,10 @@ export function Root(props) {
|
|
|
41
41
|
export function Data(props) {
|
|
42
42
|
const { children } = props;
|
|
43
43
|
const bookingService = useService(BookingServiceDefinition);
|
|
44
|
-
const
|
|
44
|
+
const bookingSelections = bookingService.bookingSelections.get();
|
|
45
45
|
const selectedTimeSlot = bookingService.timeSlot.get();
|
|
46
46
|
return children({
|
|
47
|
-
|
|
47
|
+
bookingSelections,
|
|
48
48
|
selectedTimeSlot,
|
|
49
49
|
});
|
|
50
50
|
}
|
|
@@ -7,12 +7,16 @@ import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
|
7
7
|
import type { services, availabilityTimeSlots } from '@wix/bookings';
|
|
8
8
|
import type { FormValues } from '@wix/form-public';
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* BookingSelection contains the service AND its related selections
|
|
11
11
|
*/
|
|
12
|
-
export interface
|
|
12
|
+
export interface BookingSelection {
|
|
13
|
+
/** Unique identifier for this booking instance (allows same service multiple times) */
|
|
14
|
+
instanceId: string;
|
|
13
15
|
service: services.Service;
|
|
14
16
|
/** Selected resources (staff members, rooms, etc.) */
|
|
15
|
-
|
|
17
|
+
staff?: availabilityTimeSlots.Resource;
|
|
18
|
+
/** Per-service slot data from nested slots (for multi-service bookings) */
|
|
19
|
+
nestedSlotData?: availabilityTimeSlots.NestedTimeSlot;
|
|
16
20
|
}
|
|
17
21
|
export interface Location {
|
|
18
22
|
id: string;
|
|
@@ -22,10 +26,14 @@ export interface Location {
|
|
|
22
26
|
* BookingService actions - setters only, no API calls
|
|
23
27
|
*/
|
|
24
28
|
export interface BookingServiceActions {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
/** Clears all booking selections and sets a single service (instanceId is auto-generated) */
|
|
30
|
+
setService(service: Omit<BookingSelection, 'instanceId'>): string;
|
|
31
|
+
/** Adds a service and returns the generated instanceId */
|
|
32
|
+
addService(service: Omit<BookingSelection, 'instanceId'>): string;
|
|
33
|
+
/** Updates a booking selection by instanceId */
|
|
34
|
+
updateServiceByInstanceId(instanceId: string, updates: Partial<Omit<BookingSelection, 'instanceId'>>): void;
|
|
35
|
+
/** Removes a service selection by instanceId. If no instanceId provided, removes the first service. */
|
|
36
|
+
removeServiceByInstanceId(instanceId?: string): void;
|
|
29
37
|
clearServices(): void;
|
|
30
38
|
setTimeSlot(slot: availabilityTimeSlots.TimeSlot | null): void;
|
|
31
39
|
clearTimeSlot(): void;
|
|
@@ -40,8 +48,8 @@ export interface BookingServiceActions {
|
|
|
40
48
|
* Pure state management - no API calls
|
|
41
49
|
*/
|
|
42
50
|
export interface BookingService {
|
|
43
|
-
/** Signal containing the
|
|
44
|
-
|
|
51
|
+
/** Signal containing the booking selections (services with their related data) */
|
|
52
|
+
bookingSelections: Signal<BookingSelection[]>;
|
|
45
53
|
/** Signal containing the currently selected time slot */
|
|
46
54
|
timeSlot: Signal<availabilityTimeSlots.TimeSlot | null>;
|
|
47
55
|
/** Signal containing the selected location (derived from timeSlot) */
|
|
@@ -64,8 +72,8 @@ export declare const BookingServiceDefinition: string & {
|
|
|
64
72
|
* Note: location is NOT in config - it's derived internally from timeSlot
|
|
65
73
|
*/
|
|
66
74
|
export interface BookingServiceConfig {
|
|
67
|
-
/** Optional initial
|
|
68
|
-
|
|
75
|
+
/** Optional initial booking selections to pre-populate */
|
|
76
|
+
initialBookingSelections?: BookingSelection[];
|
|
69
77
|
/** Optional initial time slot to pre-populate */
|
|
70
78
|
initialTimeSlot?: availabilityTimeSlots.TimeSlot | null;
|
|
71
79
|
/** Optional initial form values */
|
|
@@ -5,6 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { defineService, implementService } from '@wix/services-definitions';
|
|
7
7
|
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Types
|
|
10
|
+
// ============================================================================
|
|
11
|
+
/**
|
|
12
|
+
* Generates a unique instance ID for service selections
|
|
13
|
+
*/
|
|
14
|
+
function generateInstanceId() {
|
|
15
|
+
return crypto.randomUUID();
|
|
16
|
+
}
|
|
8
17
|
/**
|
|
9
18
|
* Service definition for Booking
|
|
10
19
|
*/
|
|
@@ -39,7 +48,12 @@ export const BookingServiceDefinition = defineService('booking');
|
|
|
39
48
|
export const BookingService = implementService.withConfig()(BookingServiceDefinition, ({ getService, config }) => {
|
|
40
49
|
const signalsService = getService(SignalsServiceDefinition);
|
|
41
50
|
// Initialize signals with config values
|
|
42
|
-
|
|
51
|
+
// Ensure all initial booking selections have instanceIds
|
|
52
|
+
const initialSelectionsWithIds = (config.initialBookingSelections || []).map((s) => ({
|
|
53
|
+
...s,
|
|
54
|
+
instanceId: s.instanceId || generateInstanceId(),
|
|
55
|
+
}));
|
|
56
|
+
const bookingSelectionsSignal = signalsService.signal(initialSelectionsWithIds);
|
|
43
57
|
const timeSlotSignal = signalsService.signal(config.initialTimeSlot || null);
|
|
44
58
|
// Location is derived internally - initialize as null
|
|
45
59
|
const locationSignal = signalsService.signal(null);
|
|
@@ -49,25 +63,43 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
49
63
|
// ========================================================================
|
|
50
64
|
const actions = {
|
|
51
65
|
// Services actions
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
setService(service) {
|
|
67
|
+
const instanceId = generateInstanceId();
|
|
68
|
+
const newSelection = {
|
|
69
|
+
...service,
|
|
70
|
+
instanceId,
|
|
71
|
+
};
|
|
72
|
+
bookingSelectionsSignal.set([newSelection]);
|
|
73
|
+
return instanceId;
|
|
54
74
|
},
|
|
55
75
|
addService(service) {
|
|
56
|
-
const
|
|
57
|
-
|
|
76
|
+
const instanceId = generateInstanceId();
|
|
77
|
+
const current = bookingSelectionsSignal.get();
|
|
78
|
+
const newSelection = {
|
|
79
|
+
...service,
|
|
80
|
+
instanceId,
|
|
81
|
+
};
|
|
82
|
+
bookingSelectionsSignal.set([...current, newSelection]);
|
|
83
|
+
return instanceId;
|
|
58
84
|
},
|
|
59
|
-
|
|
60
|
-
const current =
|
|
61
|
-
const updated = current.map((s) => s.
|
|
62
|
-
|
|
85
|
+
updateServiceByInstanceId(instanceId, updates) {
|
|
86
|
+
const current = bookingSelectionsSignal.get();
|
|
87
|
+
const updated = current.map((s) => s.instanceId === instanceId ? { ...s, ...updates } : s);
|
|
88
|
+
bookingSelectionsSignal.set(updated);
|
|
63
89
|
},
|
|
64
|
-
|
|
65
|
-
const current =
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
removeServiceByInstanceId(instanceId) {
|
|
91
|
+
const current = bookingSelectionsSignal.get();
|
|
92
|
+
if (!instanceId) {
|
|
93
|
+
// Remove first selection if no instanceId provided
|
|
94
|
+
bookingSelectionsSignal.set(current.slice(1));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const filtered = current.filter((s) => s.instanceId !== instanceId);
|
|
98
|
+
bookingSelectionsSignal.set(filtered);
|
|
99
|
+
}
|
|
68
100
|
},
|
|
69
101
|
clearServices() {
|
|
70
|
-
|
|
102
|
+
bookingSelectionsSignal.set([]);
|
|
71
103
|
},
|
|
72
104
|
// Time Slot actions
|
|
73
105
|
setTimeSlot(slot) {
|
|
@@ -92,14 +124,14 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
92
124
|
},
|
|
93
125
|
// Clear all
|
|
94
126
|
clearAll() {
|
|
95
|
-
|
|
127
|
+
bookingSelectionsSignal.set([]);
|
|
96
128
|
timeSlotSignal.set(null);
|
|
97
129
|
locationSignal.set(null);
|
|
98
130
|
formValuesSignal.set(null);
|
|
99
131
|
},
|
|
100
132
|
};
|
|
101
133
|
return {
|
|
102
|
-
|
|
134
|
+
bookingSelections: bookingSelectionsSignal,
|
|
103
135
|
timeSlot: timeSlotSignal,
|
|
104
136
|
location: locationSignal,
|
|
105
137
|
formValues: formValuesSignal,
|
|
@@ -3,5 +3,5 @@
|
|
|
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 BookingSelection, type Location, type BookingServiceActions, } from './booking/booking.js';
|
|
7
7
|
export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
|
|
@@ -6,7 +6,7 @@ import { defineService, implementService } from '@wix/services-definitions';
|
|
|
6
6
|
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
7
7
|
import { services } from '@wix/bookings';
|
|
8
8
|
import { getServiceById, getServiceBySlug, } from '../../api/query-services/index.js';
|
|
9
|
-
import { BookingServiceDefinition
|
|
9
|
+
import { BookingServiceDefinition } from '../booking/booking.js';
|
|
10
10
|
import { convertToMoneyData } from '../../utils/money.js';
|
|
11
11
|
/**
|
|
12
12
|
* Booking app ID constant
|
|
@@ -81,8 +81,8 @@ export const ServiceService = implementService.withConfig()(ServiceServiceDefini
|
|
|
81
81
|
const currentService = serviceSignal.get();
|
|
82
82
|
if (!currentService._id)
|
|
83
83
|
return false;
|
|
84
|
-
const
|
|
85
|
-
return
|
|
84
|
+
const selections = bookingService.bookingSelections.get();
|
|
85
|
+
return selections.some((selection) => selection.service._id === currentService._id);
|
|
86
86
|
});
|
|
87
87
|
// requiresAvailability signal - true for APPOINTMENT and CLASS types
|
|
88
88
|
const requiresAvailabilitySignal = signalsService.computed(() => {
|
|
@@ -185,27 +185,26 @@ export const ServiceService = implementService.withConfig()(ServiceServiceDefini
|
|
|
185
185
|
if (!bookableSignal.get() || !bookingService)
|
|
186
186
|
return;
|
|
187
187
|
const currentService = serviceSignal.get();
|
|
188
|
-
|
|
189
|
-
service: currentService,
|
|
190
|
-
};
|
|
191
|
-
bookingService.actions.setServices([selection]);
|
|
188
|
+
bookingService.actions.setService({ service: currentService });
|
|
192
189
|
},
|
|
193
190
|
add: () => {
|
|
194
191
|
if (!bookableSignal.get() || !bookingService)
|
|
195
192
|
return;
|
|
196
193
|
const currentService = serviceSignal.get();
|
|
197
|
-
|
|
198
|
-
service: currentService,
|
|
199
|
-
};
|
|
200
|
-
bookingService.actions.addService(selection);
|
|
194
|
+
bookingService.actions.addService({ service: currentService });
|
|
201
195
|
},
|
|
202
196
|
remove: () => {
|
|
203
197
|
if (!bookableSignal.get() || !bookingService)
|
|
204
198
|
return;
|
|
205
199
|
const currentService = serviceSignal.get();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
// Remove all instances of this service (by serviceId)
|
|
201
|
+
const selections = bookingService.bookingSelections.get();
|
|
202
|
+
const instancesToRemove = selections
|
|
203
|
+
.filter((s) => s.service._id === currentService._id)
|
|
204
|
+
.map((s) => s.instanceId);
|
|
205
|
+
instancesToRemove.forEach((instanceId) => {
|
|
206
|
+
bookingService.actions.removeServiceByInstanceId(instanceId);
|
|
207
|
+
});
|
|
209
208
|
},
|
|
210
209
|
};
|
|
211
210
|
return {
|
|
@@ -3,7 +3,7 @@
|
|
|
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
|
|
6
|
+
import { type BookingServiceConfig, type BookingSelection } from '../../../services/booking/booking.js';
|
|
7
7
|
import type { availabilityTimeSlots } from '@wix/bookings';
|
|
8
8
|
/**
|
|
9
9
|
* Props for Booking.Root component
|
|
@@ -33,7 +33,7 @@ export declare function Root(props: RootProps): React.ReactNode;
|
|
|
33
33
|
*/
|
|
34
34
|
export interface DataProps {
|
|
35
35
|
children: (data: {
|
|
36
|
-
|
|
36
|
+
bookingSelections: BookingSelection[];
|
|
37
37
|
selectedTimeSlot: availabilityTimeSlots.TimeSlot | null;
|
|
38
38
|
}) => React.ReactNode;
|
|
39
39
|
}
|
|
@@ -45,9 +45,9 @@ export interface DataProps {
|
|
|
45
45
|
* @example
|
|
46
46
|
* ```tsx
|
|
47
47
|
* <Booking.Data>
|
|
48
|
-
* {({
|
|
49
|
-
*
|
|
50
|
-
* <div>{
|
|
48
|
+
* {({ bookingSelections }) => (
|
|
49
|
+
* bookingSelections.length > 0 ? (
|
|
50
|
+
* <div>{bookingSelections.map(s => s.service.name).join(', ')}</div>
|
|
51
51
|
* ) : (
|
|
52
52
|
* <div>No services selected</div>
|
|
53
53
|
* )
|
|
@@ -28,9 +28,9 @@ export function Root(props) {
|
|
|
28
28
|
* @example
|
|
29
29
|
* ```tsx
|
|
30
30
|
* <Booking.Data>
|
|
31
|
-
* {({
|
|
32
|
-
*
|
|
33
|
-
* <div>{
|
|
31
|
+
* {({ bookingSelections }) => (
|
|
32
|
+
* bookingSelections.length > 0 ? (
|
|
33
|
+
* <div>{bookingSelections.map(s => s.service.name).join(', ')}</div>
|
|
34
34
|
* ) : (
|
|
35
35
|
* <div>No services selected</div>
|
|
36
36
|
* )
|
|
@@ -41,10 +41,10 @@ export function Root(props) {
|
|
|
41
41
|
export function Data(props) {
|
|
42
42
|
const { children } = props;
|
|
43
43
|
const bookingService = useService(BookingServiceDefinition);
|
|
44
|
-
const
|
|
44
|
+
const bookingSelections = bookingService.bookingSelections.get();
|
|
45
45
|
const selectedTimeSlot = bookingService.timeSlot.get();
|
|
46
46
|
return children({
|
|
47
|
-
|
|
47
|
+
bookingSelections,
|
|
48
48
|
selectedTimeSlot,
|
|
49
49
|
});
|
|
50
50
|
}
|
|
@@ -7,12 +7,16 @@ import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
|
7
7
|
import type { services, availabilityTimeSlots } from '@wix/bookings';
|
|
8
8
|
import type { FormValues } from '@wix/form-public';
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* BookingSelection contains the service AND its related selections
|
|
11
11
|
*/
|
|
12
|
-
export interface
|
|
12
|
+
export interface BookingSelection {
|
|
13
|
+
/** Unique identifier for this booking instance (allows same service multiple times) */
|
|
14
|
+
instanceId: string;
|
|
13
15
|
service: services.Service;
|
|
14
16
|
/** Selected resources (staff members, rooms, etc.) */
|
|
15
|
-
|
|
17
|
+
staff?: availabilityTimeSlots.Resource;
|
|
18
|
+
/** Per-service slot data from nested slots (for multi-service bookings) */
|
|
19
|
+
nestedSlotData?: availabilityTimeSlots.NestedTimeSlot;
|
|
16
20
|
}
|
|
17
21
|
export interface Location {
|
|
18
22
|
id: string;
|
|
@@ -22,10 +26,14 @@ export interface Location {
|
|
|
22
26
|
* BookingService actions - setters only, no API calls
|
|
23
27
|
*/
|
|
24
28
|
export interface BookingServiceActions {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
/** Clears all booking selections and sets a single service (instanceId is auto-generated) */
|
|
30
|
+
setService(service: Omit<BookingSelection, 'instanceId'>): string;
|
|
31
|
+
/** Adds a service and returns the generated instanceId */
|
|
32
|
+
addService(service: Omit<BookingSelection, 'instanceId'>): string;
|
|
33
|
+
/** Updates a booking selection by instanceId */
|
|
34
|
+
updateServiceByInstanceId(instanceId: string, updates: Partial<Omit<BookingSelection, 'instanceId'>>): void;
|
|
35
|
+
/** Removes a service selection by instanceId. If no instanceId provided, removes the first service. */
|
|
36
|
+
removeServiceByInstanceId(instanceId?: string): void;
|
|
29
37
|
clearServices(): void;
|
|
30
38
|
setTimeSlot(slot: availabilityTimeSlots.TimeSlot | null): void;
|
|
31
39
|
clearTimeSlot(): void;
|
|
@@ -40,8 +48,8 @@ export interface BookingServiceActions {
|
|
|
40
48
|
* Pure state management - no API calls
|
|
41
49
|
*/
|
|
42
50
|
export interface BookingService {
|
|
43
|
-
/** Signal containing the
|
|
44
|
-
|
|
51
|
+
/** Signal containing the booking selections (services with their related data) */
|
|
52
|
+
bookingSelections: Signal<BookingSelection[]>;
|
|
45
53
|
/** Signal containing the currently selected time slot */
|
|
46
54
|
timeSlot: Signal<availabilityTimeSlots.TimeSlot | null>;
|
|
47
55
|
/** Signal containing the selected location (derived from timeSlot) */
|
|
@@ -64,8 +72,8 @@ export declare const BookingServiceDefinition: string & {
|
|
|
64
72
|
* Note: location is NOT in config - it's derived internally from timeSlot
|
|
65
73
|
*/
|
|
66
74
|
export interface BookingServiceConfig {
|
|
67
|
-
/** Optional initial
|
|
68
|
-
|
|
75
|
+
/** Optional initial booking selections to pre-populate */
|
|
76
|
+
initialBookingSelections?: BookingSelection[];
|
|
69
77
|
/** Optional initial time slot to pre-populate */
|
|
70
78
|
initialTimeSlot?: availabilityTimeSlots.TimeSlot | null;
|
|
71
79
|
/** Optional initial form values */
|
|
@@ -5,6 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { defineService, implementService } from '@wix/services-definitions';
|
|
7
7
|
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Types
|
|
10
|
+
// ============================================================================
|
|
11
|
+
/**
|
|
12
|
+
* Generates a unique instance ID for service selections
|
|
13
|
+
*/
|
|
14
|
+
function generateInstanceId() {
|
|
15
|
+
return crypto.randomUUID();
|
|
16
|
+
}
|
|
8
17
|
/**
|
|
9
18
|
* Service definition for Booking
|
|
10
19
|
*/
|
|
@@ -39,7 +48,12 @@ export const BookingServiceDefinition = defineService('booking');
|
|
|
39
48
|
export const BookingService = implementService.withConfig()(BookingServiceDefinition, ({ getService, config }) => {
|
|
40
49
|
const signalsService = getService(SignalsServiceDefinition);
|
|
41
50
|
// Initialize signals with config values
|
|
42
|
-
|
|
51
|
+
// Ensure all initial booking selections have instanceIds
|
|
52
|
+
const initialSelectionsWithIds = (config.initialBookingSelections || []).map((s) => ({
|
|
53
|
+
...s,
|
|
54
|
+
instanceId: s.instanceId || generateInstanceId(),
|
|
55
|
+
}));
|
|
56
|
+
const bookingSelectionsSignal = signalsService.signal(initialSelectionsWithIds);
|
|
43
57
|
const timeSlotSignal = signalsService.signal(config.initialTimeSlot || null);
|
|
44
58
|
// Location is derived internally - initialize as null
|
|
45
59
|
const locationSignal = signalsService.signal(null);
|
|
@@ -49,25 +63,43 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
49
63
|
// ========================================================================
|
|
50
64
|
const actions = {
|
|
51
65
|
// Services actions
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
setService(service) {
|
|
67
|
+
const instanceId = generateInstanceId();
|
|
68
|
+
const newSelection = {
|
|
69
|
+
...service,
|
|
70
|
+
instanceId,
|
|
71
|
+
};
|
|
72
|
+
bookingSelectionsSignal.set([newSelection]);
|
|
73
|
+
return instanceId;
|
|
54
74
|
},
|
|
55
75
|
addService(service) {
|
|
56
|
-
const
|
|
57
|
-
|
|
76
|
+
const instanceId = generateInstanceId();
|
|
77
|
+
const current = bookingSelectionsSignal.get();
|
|
78
|
+
const newSelection = {
|
|
79
|
+
...service,
|
|
80
|
+
instanceId,
|
|
81
|
+
};
|
|
82
|
+
bookingSelectionsSignal.set([...current, newSelection]);
|
|
83
|
+
return instanceId;
|
|
58
84
|
},
|
|
59
|
-
|
|
60
|
-
const current =
|
|
61
|
-
const updated = current.map((s) => s.
|
|
62
|
-
|
|
85
|
+
updateServiceByInstanceId(instanceId, updates) {
|
|
86
|
+
const current = bookingSelectionsSignal.get();
|
|
87
|
+
const updated = current.map((s) => s.instanceId === instanceId ? { ...s, ...updates } : s);
|
|
88
|
+
bookingSelectionsSignal.set(updated);
|
|
63
89
|
},
|
|
64
|
-
|
|
65
|
-
const current =
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
removeServiceByInstanceId(instanceId) {
|
|
91
|
+
const current = bookingSelectionsSignal.get();
|
|
92
|
+
if (!instanceId) {
|
|
93
|
+
// Remove first selection if no instanceId provided
|
|
94
|
+
bookingSelectionsSignal.set(current.slice(1));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const filtered = current.filter((s) => s.instanceId !== instanceId);
|
|
98
|
+
bookingSelectionsSignal.set(filtered);
|
|
99
|
+
}
|
|
68
100
|
},
|
|
69
101
|
clearServices() {
|
|
70
|
-
|
|
102
|
+
bookingSelectionsSignal.set([]);
|
|
71
103
|
},
|
|
72
104
|
// Time Slot actions
|
|
73
105
|
setTimeSlot(slot) {
|
|
@@ -92,14 +124,14 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
92
124
|
},
|
|
93
125
|
// Clear all
|
|
94
126
|
clearAll() {
|
|
95
|
-
|
|
127
|
+
bookingSelectionsSignal.set([]);
|
|
96
128
|
timeSlotSignal.set(null);
|
|
97
129
|
locationSignal.set(null);
|
|
98
130
|
formValuesSignal.set(null);
|
|
99
131
|
},
|
|
100
132
|
};
|
|
101
133
|
return {
|
|
102
|
-
|
|
134
|
+
bookingSelections: bookingSelectionsSignal,
|
|
103
135
|
timeSlot: timeSlotSignal,
|
|
104
136
|
location: locationSignal,
|
|
105
137
|
formValues: formValuesSignal,
|
package/dist/services/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
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 BookingSelection, type Location, type BookingServiceActions, } from './booking/booking.js';
|
|
7
7
|
export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
|
|
@@ -6,7 +6,7 @@ import { defineService, implementService } from '@wix/services-definitions';
|
|
|
6
6
|
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
7
7
|
import { services } from '@wix/bookings';
|
|
8
8
|
import { getServiceById, getServiceBySlug, } from '../../api/query-services/index.js';
|
|
9
|
-
import { BookingServiceDefinition
|
|
9
|
+
import { BookingServiceDefinition } from '../booking/booking.js';
|
|
10
10
|
import { convertToMoneyData } from '../../utils/money.js';
|
|
11
11
|
/**
|
|
12
12
|
* Booking app ID constant
|
|
@@ -81,8 +81,8 @@ export const ServiceService = implementService.withConfig()(ServiceServiceDefini
|
|
|
81
81
|
const currentService = serviceSignal.get();
|
|
82
82
|
if (!currentService._id)
|
|
83
83
|
return false;
|
|
84
|
-
const
|
|
85
|
-
return
|
|
84
|
+
const selections = bookingService.bookingSelections.get();
|
|
85
|
+
return selections.some((selection) => selection.service._id === currentService._id);
|
|
86
86
|
});
|
|
87
87
|
// requiresAvailability signal - true for APPOINTMENT and CLASS types
|
|
88
88
|
const requiresAvailabilitySignal = signalsService.computed(() => {
|
|
@@ -185,27 +185,26 @@ export const ServiceService = implementService.withConfig()(ServiceServiceDefini
|
|
|
185
185
|
if (!bookableSignal.get() || !bookingService)
|
|
186
186
|
return;
|
|
187
187
|
const currentService = serviceSignal.get();
|
|
188
|
-
|
|
189
|
-
service: currentService,
|
|
190
|
-
};
|
|
191
|
-
bookingService.actions.setServices([selection]);
|
|
188
|
+
bookingService.actions.setService({ service: currentService });
|
|
192
189
|
},
|
|
193
190
|
add: () => {
|
|
194
191
|
if (!bookableSignal.get() || !bookingService)
|
|
195
192
|
return;
|
|
196
193
|
const currentService = serviceSignal.get();
|
|
197
|
-
|
|
198
|
-
service: currentService,
|
|
199
|
-
};
|
|
200
|
-
bookingService.actions.addService(selection);
|
|
194
|
+
bookingService.actions.addService({ service: currentService });
|
|
201
195
|
},
|
|
202
196
|
remove: () => {
|
|
203
197
|
if (!bookableSignal.get() || !bookingService)
|
|
204
198
|
return;
|
|
205
199
|
const currentService = serviceSignal.get();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
// Remove all instances of this service (by serviceId)
|
|
201
|
+
const selections = bookingService.bookingSelections.get();
|
|
202
|
+
const instancesToRemove = selections
|
|
203
|
+
.filter((s) => s.service._id === currentService._id)
|
|
204
|
+
.map((s) => s.instanceId);
|
|
205
|
+
instancesToRemove.forEach((instanceId) => {
|
|
206
|
+
bookingService.actions.removeServiceByInstanceId(instanceId);
|
|
207
|
+
});
|
|
209
208
|
},
|
|
210
209
|
};
|
|
211
210
|
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.12",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -68,5 +68,5 @@
|
|
|
68
68
|
"groupId": "com.wixpress.headless-components"
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
|
-
"falconPackageHash": "
|
|
71
|
+
"falconPackageHash": "1ac24364dad5e7cd40939f468503e6a086b769e8398d3070b79d7ae2"
|
|
72
72
|
}
|