@wix/headless-bookings 0.0.15 → 0.0.17
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/api/time-slot-list/index.d.ts +21 -1
- package/cjs/dist/api/time-slot-list/index.js +46 -8
- package/cjs/dist/react/core/booking/Booking.d.ts +17 -9
- package/cjs/dist/react/core/booking/Booking.js +13 -7
- package/cjs/dist/react/core/time-slot-list/TimeSlot.d.ts +22 -18
- package/cjs/dist/react/core/time-slot-list/TimeSlot.js +41 -28
- package/cjs/dist/react/core/time-slot-list/TimeSlotList.d.ts +39 -7
- package/cjs/dist/react/core/time-slot-list/TimeSlotList.js +56 -11
- package/cjs/dist/react/time-slot-list/TimeSlot.js +3 -3
- package/cjs/dist/react/time-slot-list/TimeSlotList.d.ts +55 -12
- package/cjs/dist/react/time-slot-list/TimeSlotList.js +47 -17
- 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 +3 -3
- package/cjs/dist/services/service/service.js +10 -7
- package/cjs/dist/services/time-slot-list/index.d.ts +1 -1
- package/cjs/dist/services/time-slot-list/time-slot-list.d.ts +3 -3
- package/cjs/dist/services/time-slot-list/time-slot-list.def.d.ts +23 -4
- package/cjs/dist/services/time-slot-list/time-slot-list.js +146 -65
- package/dist/api/time-slot-list/index.d.ts +21 -1
- package/dist/api/time-slot-list/index.js +46 -8
- package/dist/react/core/booking/Booking.d.ts +17 -9
- package/dist/react/core/booking/Booking.js +13 -7
- package/dist/react/core/time-slot-list/TimeSlot.d.ts +22 -18
- package/dist/react/core/time-slot-list/TimeSlot.js +41 -28
- package/dist/react/core/time-slot-list/TimeSlotList.d.ts +39 -7
- package/dist/react/core/time-slot-list/TimeSlotList.js +56 -11
- package/dist/react/time-slot-list/TimeSlot.js +3 -3
- package/dist/react/time-slot-list/TimeSlotList.d.ts +55 -12
- package/dist/react/time-slot-list/TimeSlotList.js +47 -17
- package/dist/services/booking/booking.d.ts +48 -30
- package/dist/services/booking/booking.js +74 -44
- package/dist/services/index.d.ts +3 -3
- package/dist/services/service/service.js +10 -7
- package/dist/services/time-slot-list/index.d.ts +1 -1
- package/dist/services/time-slot-list/time-slot-list.d.ts +3 -3
- package/dist/services/time-slot-list/time-slot-list.def.d.ts +23 -4
- package/dist/services/time-slot-list/time-slot-list.js +146 -65
- package/package.json +3 -2
|
@@ -128,13 +128,13 @@ Duration.displayName = 'TimeSlot.Duration';
|
|
|
128
128
|
*/
|
|
129
129
|
export const Select = React.forwardRef((props, ref) => {
|
|
130
130
|
const { asChild, children, className, label } = props;
|
|
131
|
-
return (_jsx(CoreTimeSlot.Actions, { children: ({
|
|
131
|
+
return (_jsx(CoreTimeSlot.Actions, { children: ({ selectTimeSlot, isSelected, bookable }) => {
|
|
132
132
|
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotActionSelect, "data-selected": isSelected, "data-bookable": bookable, customElement: children, customElementProps: {
|
|
133
|
-
onClick:
|
|
133
|
+
onClick: selectTimeSlot,
|
|
134
134
|
disabled: !bookable,
|
|
135
135
|
isSelected,
|
|
136
136
|
bookable,
|
|
137
|
-
}, children: _jsx("button", { onClick:
|
|
137
|
+
}, children: _jsx("button", { onClick: selectTimeSlot, disabled: !bookable, children: label }) }));
|
|
138
138
|
} }));
|
|
139
139
|
});
|
|
140
140
|
Select.displayName = 'TimeSlot.Actions.Select';
|
|
@@ -7,14 +7,21 @@ import { ListVariant } from '@wix/headless-components/react';
|
|
|
7
7
|
import { AsChildChildren } from '@wix/headless-utils/react';
|
|
8
8
|
import type { TimeSlotListServiceConfig } from '../../services/time-slot-list/time-slot-list.def.js';
|
|
9
9
|
export type { StartDateProps as TimeSlotStartDateProps, EndDateProps as TimeSlotEndDateProps, DurationProps as TimeSlotDurationProps, SelectProps as TimeSlotSelectProps, LocationsProps as TimeSlotLocationsProps, LocationRepeaterProps as TimeSlotLocationRepeaterProps, LocationNameProps as TimeSlotLocationNameProps, SelectLocationProps as TimeSlotSelectLocationProps, StaffMembersProps as TimeSlotStaffMembersProps, StaffMemberRepeaterProps as TimeSlotStaffMemberRepeaterProps, StaffMemberNameProps as TimeSlotStaffMemberNameProps, SelectStaffMemberProps as TimeSlotSelectStaffMemberProps, } from './TimeSlot.js';
|
|
10
|
+
/**
|
|
11
|
+
* Render props for TimeSlotList.Timezone component
|
|
12
|
+
*/
|
|
13
|
+
export interface TimezoneRenderProps {
|
|
14
|
+
/** Raw IANA timezone string (e.g., "America/New_York") */
|
|
15
|
+
timezone: string | undefined;
|
|
16
|
+
/** User-friendly display name (e.g., "Eastern Standard Time") */
|
|
17
|
+
displayName: string;
|
|
18
|
+
}
|
|
10
19
|
/**
|
|
11
20
|
* Props for TimeSlotList.Timezone component
|
|
12
21
|
*/
|
|
13
22
|
export interface TimezoneProps {
|
|
14
23
|
asChild?: boolean;
|
|
15
|
-
children?: AsChildChildren<
|
|
16
|
-
timezone: string | undefined;
|
|
17
|
-
}>;
|
|
24
|
+
children?: AsChildChildren<TimezoneRenderProps>;
|
|
18
25
|
className?: string;
|
|
19
26
|
}
|
|
20
27
|
/**
|
|
@@ -23,19 +30,14 @@ export interface TimezoneProps {
|
|
|
23
30
|
* @component
|
|
24
31
|
* @example
|
|
25
32
|
* ```tsx
|
|
26
|
-
* // Default usage
|
|
33
|
+
* // Default usage - shows display name
|
|
27
34
|
* <TimeSlotList.Timezone className="text-sm text-gray-600" />
|
|
28
35
|
*
|
|
29
|
-
* // asChild with
|
|
30
|
-
* <TimeSlotList.Timezone asChild>
|
|
31
|
-
* <span className="font-medium" />
|
|
32
|
-
* </TimeSlotList.Timezone>
|
|
33
|
-
*
|
|
34
|
-
* // asChild with render function
|
|
36
|
+
* // asChild with render function for custom formatting
|
|
35
37
|
* <TimeSlotList.Timezone asChild>
|
|
36
|
-
* {({ timezone }) => (
|
|
38
|
+
* {({ timezone, displayName }) => (
|
|
37
39
|
* <span className="text-sm">
|
|
38
|
-
*
|
|
40
|
+
* {displayName} // "Eastern Standard Time"
|
|
39
41
|
* </span>
|
|
40
42
|
* )}
|
|
41
43
|
* </TimeSlotList.Timezone>
|
|
@@ -153,6 +155,47 @@ export declare const LoadMore: React.ForwardRefExoticComponent<LoadMoreProps & R
|
|
|
153
155
|
export declare const Actions: {
|
|
154
156
|
LoadMore: React.ForwardRefExoticComponent<LoadMoreProps & React.RefAttributes<HTMLButtonElement>>;
|
|
155
157
|
};
|
|
158
|
+
/**
|
|
159
|
+
* Props for DateRange.Input component
|
|
160
|
+
*/
|
|
161
|
+
export interface DateRangeInputProps {
|
|
162
|
+
/** Whether to render as a child component */
|
|
163
|
+
asChild?: boolean;
|
|
164
|
+
/** Children for custom rendering */
|
|
165
|
+
children?: AsChildChildren<{
|
|
166
|
+
startDate: Date;
|
|
167
|
+
endDate: Date;
|
|
168
|
+
onChange: (start: Date, end: Date) => void;
|
|
169
|
+
}>;
|
|
170
|
+
/** CSS classes to apply to the container */
|
|
171
|
+
className?: string;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Date range input for setting the start and end date filter.
|
|
175
|
+
* Uses the core DateRange component to access service data.
|
|
176
|
+
* Both dates are set together and trigger a single fetch.
|
|
177
|
+
*
|
|
178
|
+
* @component
|
|
179
|
+
* @example
|
|
180
|
+
* ```tsx
|
|
181
|
+
* <TimeSlotList.DateRange.Input asChild>
|
|
182
|
+
* {({ startDate, endDate, onChange }) => (
|
|
183
|
+
* <MyDateRangePicker
|
|
184
|
+
* start={startDate}
|
|
185
|
+
* end={endDate}
|
|
186
|
+
* onRangeChange={(start, end) => onChange(start, end)}
|
|
187
|
+
* />
|
|
188
|
+
* )}
|
|
189
|
+
* </TimeSlotList.DateRange.Input>
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
export declare const DateRangeInput: React.ForwardRefExoticComponent<DateRangeInputProps & React.RefAttributes<HTMLElement>>;
|
|
193
|
+
/**
|
|
194
|
+
* DateRange namespace for TimeSlotList date range filter
|
|
195
|
+
*/
|
|
196
|
+
export declare const DateRange: {
|
|
197
|
+
Input: React.ForwardRefExoticComponent<DateRangeInputProps & React.RefAttributes<HTMLElement>>;
|
|
198
|
+
};
|
|
156
199
|
/**
|
|
157
200
|
* TimeSlot namespace containing components for displaying individual time slot data.
|
|
158
201
|
* These components must be used within TimeSlotList.TimeSlotRepeater.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* TimeSlotList - High-level component for displaying time slot list information
|
|
4
4
|
* Provides components for displaying timezone and time slot data
|
|
@@ -8,7 +8,7 @@ import { GenericList } from '@wix/headless-components/react';
|
|
|
8
8
|
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
9
9
|
import * as CoreTimeSlotList from '../core/time-slot-list/TimeSlotList.js';
|
|
10
10
|
import { TimeSlotProvider, Info as CoreTimeSlotInfo, } from '../core/time-slot-list/TimeSlot.js';
|
|
11
|
-
import { StartDate, EndDate, Duration, Actions as TimeSlotActions, Locations, LocationRepeater, Location, StaffMembers, StaffMemberRepeater, StaffMember, } from './TimeSlot.js';
|
|
11
|
+
import { StartDate as TimeSlotStartDate, EndDate as TimeSlotEndDate, Duration, Actions as TimeSlotActions, Locations, LocationRepeater, Location, StaffMembers, StaffMemberRepeater, StaffMember, } from './TimeSlot.js';
|
|
12
12
|
/**
|
|
13
13
|
* TimeSlot with required id field for GenericList
|
|
14
14
|
*/
|
|
@@ -19,6 +19,7 @@ var TestIds;
|
|
|
19
19
|
TestIds["timeSlotListTimeSlot"] = "time-slot-list-time-slot";
|
|
20
20
|
TestIds["timeSlotListTimezone"] = "time-slot-list-timezone";
|
|
21
21
|
TestIds["timeSlotListLoadMore"] = "time-slot-list-load-more";
|
|
22
|
+
TestIds["timeSlotListDateRangeInput"] = "time-slot-list-date-range-input";
|
|
22
23
|
})(TestIds || (TestIds = {}));
|
|
23
24
|
/**
|
|
24
25
|
* Displays the current timezone with customizable rendering.
|
|
@@ -26,19 +27,14 @@ var TestIds;
|
|
|
26
27
|
* @component
|
|
27
28
|
* @example
|
|
28
29
|
* ```tsx
|
|
29
|
-
* // Default usage
|
|
30
|
+
* // Default usage - shows display name
|
|
30
31
|
* <TimeSlotList.Timezone className="text-sm text-gray-600" />
|
|
31
32
|
*
|
|
32
|
-
* // asChild with
|
|
33
|
+
* // asChild with render function for custom formatting
|
|
33
34
|
* <TimeSlotList.Timezone asChild>
|
|
34
|
-
*
|
|
35
|
-
* </TimeSlotList.Timezone>
|
|
36
|
-
*
|
|
37
|
-
* // asChild with render function
|
|
38
|
-
* <TimeSlotList.Timezone asChild>
|
|
39
|
-
* {({ timezone }) => (
|
|
35
|
+
* {({ timezone, displayName }) => (
|
|
40
36
|
* <span className="text-sm">
|
|
41
|
-
*
|
|
37
|
+
* {displayName} // "Eastern Standard Time"
|
|
42
38
|
* </span>
|
|
43
39
|
* )}
|
|
44
40
|
* </TimeSlotList.Timezone>
|
|
@@ -46,7 +42,7 @@ var TestIds;
|
|
|
46
42
|
*/
|
|
47
43
|
export const Timezone = React.forwardRef((props, ref) => {
|
|
48
44
|
const { asChild, children, className } = props;
|
|
49
|
-
return (_jsx(CoreTimeSlotList.Timezone, { children: ({ timezone }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotListTimezone, customElement: children, customElementProps: { timezone }, children: _jsx("span", { children:
|
|
45
|
+
return (_jsx(CoreTimeSlotList.Timezone, { children: ({ timezone, displayName }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotListTimezone, customElement: children, customElementProps: { timezone, displayName }, children: _jsx("span", { children: displayName }) })) }));
|
|
50
46
|
});
|
|
51
47
|
Timezone.displayName = 'TimeSlotList.Timezone';
|
|
52
48
|
/**
|
|
@@ -107,8 +103,8 @@ TimeSlots.displayName = 'TimeSlotList.TimeSlots';
|
|
|
107
103
|
* Internal component that wraps each time slot item with context provider.
|
|
108
104
|
* This is separate to allow using the service hook within the repeater.
|
|
109
105
|
*/
|
|
110
|
-
const TimeSlotItemWrapper = ({
|
|
111
|
-
return (_jsx(TimeSlotProvider, {
|
|
106
|
+
const TimeSlotItemWrapper = ({ timeSlot, children, }) => {
|
|
107
|
+
return (_jsx(TimeSlotProvider, { timeSlot: timeSlot, children: _jsx(CoreTimeSlotInfo, { children: ({ bookable, isSelected }) => (_jsx("div", { "data-testid": TestIds.timeSlotListTimeSlot, "data-slot-start": timeSlot.localStartDate, "data-slot-id": timeSlot.id, "data-bookable": bookable, "data-selected": isSelected, children: children })) }) }));
|
|
112
108
|
};
|
|
113
109
|
/**
|
|
114
110
|
* Repeater component that renders each time slot.
|
|
@@ -127,7 +123,7 @@ const TimeSlotItemWrapper = ({ slot, children, }) => {
|
|
|
127
123
|
*/
|
|
128
124
|
export const TimeSlotRepeater = React.forwardRef((props, ref) => {
|
|
129
125
|
const { children } = props;
|
|
130
|
-
return (_jsx(GenericList.Repeater, { ref: ref, itemWrapper: ({ item:
|
|
126
|
+
return (_jsx(GenericList.Repeater, { ref: ref, itemWrapper: ({ item: timeSlot, children: itemChildren }) => (_jsx(TimeSlotItemWrapper, { timeSlot: timeSlot, children: itemChildren }, timeSlot.id)), children: children }));
|
|
131
127
|
});
|
|
132
128
|
TimeSlotRepeater.displayName = 'TimeSlotList.TimeSlotRepeater';
|
|
133
129
|
/**
|
|
@@ -153,6 +149,40 @@ LoadMore.displayName = 'TimeSlotList.Actions.LoadMore';
|
|
|
153
149
|
export const Actions = {
|
|
154
150
|
LoadMore,
|
|
155
151
|
};
|
|
152
|
+
/**
|
|
153
|
+
* Date range input for setting the start and end date filter.
|
|
154
|
+
* Uses the core DateRange component to access service data.
|
|
155
|
+
* Both dates are set together and trigger a single fetch.
|
|
156
|
+
*
|
|
157
|
+
* @component
|
|
158
|
+
* @example
|
|
159
|
+
* ```tsx
|
|
160
|
+
* <TimeSlotList.DateRange.Input asChild>
|
|
161
|
+
* {({ startDate, endDate, onChange }) => (
|
|
162
|
+
* <MyDateRangePicker
|
|
163
|
+
* start={startDate}
|
|
164
|
+
* end={endDate}
|
|
165
|
+
* onRangeChange={(start, end) => onChange(start, end)}
|
|
166
|
+
* />
|
|
167
|
+
* )}
|
|
168
|
+
* </TimeSlotList.DateRange.Input>
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export const DateRangeInput = React.forwardRef((props, ref) => {
|
|
172
|
+
const { asChild, children, className, ...otherProps } = props;
|
|
173
|
+
return (_jsx(CoreTimeSlotList.DateRange, { children: ({ startDate, endDate, setDateRange }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotListDateRangeInput, customElement: children, customElementProps: {
|
|
174
|
+
startDate,
|
|
175
|
+
endDate,
|
|
176
|
+
onChange: setDateRange,
|
|
177
|
+
}, ...otherProps, children: _jsxs("div", { children: [startDate.toLocaleDateString(), " - ", endDate.toLocaleDateString()] }) })) }));
|
|
178
|
+
});
|
|
179
|
+
DateRangeInput.displayName = 'TimeSlotList.DateRange.Input';
|
|
180
|
+
/**
|
|
181
|
+
* DateRange namespace for TimeSlotList date range filter
|
|
182
|
+
*/
|
|
183
|
+
export const DateRange = {
|
|
184
|
+
Input: DateRangeInput,
|
|
185
|
+
};
|
|
156
186
|
// ============================================================================
|
|
157
187
|
// TimeSlot Entity Components (re-exported)
|
|
158
188
|
// ============================================================================
|
|
@@ -186,8 +216,8 @@ export const Actions = {
|
|
|
186
216
|
* ```
|
|
187
217
|
*/
|
|
188
218
|
export const TimeSlot = {
|
|
189
|
-
StartDate,
|
|
190
|
-
EndDate,
|
|
219
|
+
StartDate: TimeSlotStartDate,
|
|
220
|
+
EndDate: TimeSlotEndDate,
|
|
191
221
|
Duration,
|
|
192
222
|
Actions: TimeSlotActions,
|
|
193
223
|
Locations,
|
|
@@ -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
|
});
|
|
@@ -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
|
|
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
|
-
export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
|
|
6
|
+
export { BookingService, BookingServiceDefinition, type BookingServiceConfig, type Location, type BookingServiceActions, } from './booking/booking.js';
|
|
7
|
+
export { TimeSlotListService, TimeSlotListServiceDefinition, ViewMode, loadTimeSlotListConfig, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type SlotSelection, type TimeSlot, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list/index.js';
|
|
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';
|
|
@@ -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 bookingItems = bookingService.bookingItems.get();
|
|
85
|
+
return bookingItems.some(({ service }) => service._id === currentService._id);
|
|
86
86
|
});
|
|
87
87
|
// requiresAvailability signal - true for APPOINTMENT and CLASS types
|
|
88
88
|
const requiresAvailabilitySignal = signalsService.computed(() => {
|
|
@@ -185,25 +185,28 @@ export const ServiceService = implementService.withConfig()(ServiceServiceDefini
|
|
|
185
185
|
if (!bookableSignal.get() || !bookingService)
|
|
186
186
|
return;
|
|
187
187
|
const currentService = serviceSignal.get();
|
|
188
|
-
bookingService.actions.
|
|
188
|
+
bookingService.actions.updateItem({ service: currentService });
|
|
189
189
|
},
|
|
190
190
|
add: () => {
|
|
191
191
|
if (!bookableSignal.get() || !bookingService)
|
|
192
192
|
return;
|
|
193
193
|
const currentService = serviceSignal.get();
|
|
194
|
-
bookingService.actions.
|
|
194
|
+
bookingService.actions.addItem({
|
|
195
|
+
service: currentService,
|
|
196
|
+
resources: [],
|
|
197
|
+
});
|
|
195
198
|
},
|
|
196
199
|
remove: () => {
|
|
197
200
|
if (!bookableSignal.get() || !bookingService)
|
|
198
201
|
return;
|
|
199
202
|
const currentService = serviceSignal.get();
|
|
200
203
|
// Remove all instances of this service (by serviceId)
|
|
201
|
-
const
|
|
202
|
-
const instancesToRemove =
|
|
204
|
+
const bookingItems = bookingService.bookingItems.get();
|
|
205
|
+
const instancesToRemove = bookingItems
|
|
203
206
|
.filter((s) => s.service._id === currentService._id)
|
|
204
207
|
.map((s) => s.instanceId);
|
|
205
208
|
instancesToRemove.forEach((instanceId) => {
|
|
206
|
-
bookingService.actions.
|
|
209
|
+
bookingService.actions.removeItem(instanceId);
|
|
207
210
|
});
|
|
208
211
|
},
|
|
209
212
|
};
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* TimeSlotList Service
|
|
3
3
|
* Main export file for time slot list service
|
|
4
4
|
*/
|
|
5
|
-
export { TimeSlotListServiceDefinition, ViewMode, type TimeSlot, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type SlotSelection, } from './time-slot-list.def.js';
|
|
5
|
+
export { TimeSlotListServiceDefinition, ViewMode, type TimeSlot, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type TimeSlotsFilters, type SlotSelection, } from './time-slot-list.def.js';
|
|
6
6
|
export { TimeSlotListService, loadTimeSlotListConfig, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list.js';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Manages time slot fetching, selection, and state
|
|
4
4
|
*/
|
|
5
5
|
import type { Service } from '@wix/auto_sdk_bookings_services';
|
|
6
|
-
import { type TimeSlotListServiceConfig,
|
|
6
|
+
import { type TimeSlotListServiceConfig, type TimeSlotsFilters } from './time-slot-list.def.js';
|
|
7
7
|
export declare const TimeSlotListService: import("@wix/services-definitions").ServiceFactory<string & {
|
|
8
8
|
__api: import("./time-slot-list.def.js").TimeSlotListServiceAPI;
|
|
9
9
|
__config: {};
|
|
@@ -21,8 +21,8 @@ export interface LoadTimeSlotListConfigOptions {
|
|
|
21
21
|
endDate?: Date;
|
|
22
22
|
/** Timezone for formatting (defaults to UTC) */
|
|
23
23
|
timezone?: string;
|
|
24
|
-
/**
|
|
25
|
-
|
|
24
|
+
/** Filters for narrowing down time slots */
|
|
25
|
+
filters?: TimeSlotsFilters;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* Result type for SSR loader - either success with config or error
|