@wix/headless-bookings 0.0.31 → 0.0.33
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 +16 -16
- package/cjs/dist/__mocks__/booking.js +38 -34
- package/cjs/dist/react/core/payment/Payment.js +1 -1
- package/cjs/dist/react/time-slot-list/TimeSlot.d.ts +23 -11
- package/cjs/dist/react/time-slot-list/TimeSlot.js +34 -16
- package/cjs/dist/react/time-slot-list/TimeSlotList.d.ts +6 -0
- package/cjs/dist/react/time-slot-list/TimeSlotList.js +23 -5
- package/cjs/dist/services/booking/book-action/bookAction.js +3 -2
- package/cjs/dist/services/booking/book-action/buildBookingRequest.js +6 -4
- package/cjs/dist/services/booking/booking.d.ts +4 -0
- package/cjs/dist/services/payment/payment.d.ts +1 -1
- package/cjs/dist/services/time-slot-list/time-slot-list.js +13 -8
- package/dist/__mocks__/booking.d.ts +16 -16
- package/dist/__mocks__/booking.js +38 -34
- package/dist/react/core/payment/Payment.js +1 -1
- package/dist/react/time-slot-list/TimeSlot.d.ts +23 -11
- package/dist/react/time-slot-list/TimeSlot.js +34 -16
- package/dist/react/time-slot-list/TimeSlotList.d.ts +6 -0
- package/dist/react/time-slot-list/TimeSlotList.js +23 -5
- package/dist/services/booking/book-action/bookAction.js +3 -2
- package/dist/services/booking/book-action/buildBookingRequest.js +6 -4
- package/dist/services/booking/booking.d.ts +4 -0
- package/dist/services/payment/payment.d.ts +1 -1
- package/dist/services/time-slot-list/time-slot-list.js +13 -8
- package/package.json +2 -2
|
@@ -4,14 +4,6 @@
|
|
|
4
4
|
import type { Resource } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
5
5
|
import type { FormValues } from '@wix/form-public';
|
|
6
6
|
import type { ServiceSelection, ServiceSelectionTimeSlot, Location, BookingServiceConfig } from '../services/booking/booking.js';
|
|
7
|
-
/**
|
|
8
|
-
* Mock time slot data
|
|
9
|
-
*/
|
|
10
|
-
export declare const mockTimeSlot: ServiceSelectionTimeSlot;
|
|
11
|
-
/**
|
|
12
|
-
* Mock time slot for afternoon
|
|
13
|
-
*/
|
|
14
|
-
export declare const mockTimeSlotAfternoon: ServiceSelectionTimeSlot;
|
|
15
7
|
/**
|
|
16
8
|
* Mock resource (staff member)
|
|
17
9
|
*/
|
|
@@ -24,6 +16,22 @@ export declare const mockResourceTwo: Resource;
|
|
|
24
16
|
* Mock resource (room)
|
|
25
17
|
*/
|
|
26
18
|
export declare const mockResourceRoom: Resource;
|
|
19
|
+
/**
|
|
20
|
+
* Mock location
|
|
21
|
+
*/
|
|
22
|
+
export declare const mockLocation: Location;
|
|
23
|
+
/**
|
|
24
|
+
* Mock location (second)
|
|
25
|
+
*/
|
|
26
|
+
export declare const mockLocationTwo: Location;
|
|
27
|
+
/**
|
|
28
|
+
* Mock time slot data
|
|
29
|
+
*/
|
|
30
|
+
export declare const mockTimeSlot: ServiceSelectionTimeSlot;
|
|
31
|
+
/**
|
|
32
|
+
* Mock time slot for afternoon
|
|
33
|
+
*/
|
|
34
|
+
export declare const mockTimeSlotAfternoon: ServiceSelectionTimeSlot;
|
|
27
35
|
/**
|
|
28
36
|
* Mock service selection with all data
|
|
29
37
|
*/
|
|
@@ -36,14 +44,6 @@ export declare const mockServiceSelectionWithoutTimeSlot: ServiceSelection;
|
|
|
36
44
|
* Mock service selection for class
|
|
37
45
|
*/
|
|
38
46
|
export declare const mockServiceSelectionClass: ServiceSelection;
|
|
39
|
-
/**
|
|
40
|
-
* Mock location
|
|
41
|
-
*/
|
|
42
|
-
export declare const mockLocation: Location;
|
|
43
|
-
/**
|
|
44
|
-
* Mock location (second)
|
|
45
|
-
*/
|
|
46
|
-
export declare const mockLocationTwo: Location;
|
|
47
47
|
/**
|
|
48
48
|
* Mock form submission (contact info)
|
|
49
49
|
*/
|
|
@@ -2,26 +2,6 @@
|
|
|
2
2
|
* Shared mock data for booking service tests
|
|
3
3
|
*/
|
|
4
4
|
import { mockService, mockServiceClass } from './services.js';
|
|
5
|
-
/**
|
|
6
|
-
* Mock time slot data
|
|
7
|
-
*/
|
|
8
|
-
export const mockTimeSlot = {
|
|
9
|
-
startDate: '2024-03-15T10:00:00Z',
|
|
10
|
-
endDate: '2024-03-15T11:00:00Z',
|
|
11
|
-
totalCapacity: 10,
|
|
12
|
-
remainingCapacity: 5,
|
|
13
|
-
scheduleId: 'schedule-123',
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Mock time slot for afternoon
|
|
17
|
-
*/
|
|
18
|
-
export const mockTimeSlotAfternoon = {
|
|
19
|
-
startDate: '2024-03-15T14:00:00Z',
|
|
20
|
-
endDate: '2024-03-15T15:00:00Z',
|
|
21
|
-
totalCapacity: 10,
|
|
22
|
-
remainingCapacity: 8,
|
|
23
|
-
scheduleId: 'schedule-456',
|
|
24
|
-
};
|
|
25
5
|
/**
|
|
26
6
|
* Mock resource (staff member)
|
|
27
7
|
*/
|
|
@@ -48,6 +28,44 @@ export const mockResourceRoom = {
|
|
|
48
28
|
name: 'Conference Room A',
|
|
49
29
|
tag: 'RESOURCE',
|
|
50
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Mock location
|
|
33
|
+
*/
|
|
34
|
+
export const mockLocation = {
|
|
35
|
+
id: 'location-1',
|
|
36
|
+
name: 'Main Office',
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Mock location (second)
|
|
40
|
+
*/
|
|
41
|
+
export const mockLocationTwo = {
|
|
42
|
+
id: 'location-2',
|
|
43
|
+
name: 'Downtown Branch',
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Mock time slot data
|
|
47
|
+
*/
|
|
48
|
+
export const mockTimeSlot = {
|
|
49
|
+
startDate: '2024-03-15T10:00:00Z',
|
|
50
|
+
endDate: '2024-03-15T11:00:00Z',
|
|
51
|
+
totalCapacity: 10,
|
|
52
|
+
remainingCapacity: 5,
|
|
53
|
+
scheduleId: 'schedule-123',
|
|
54
|
+
resources: [mockResource],
|
|
55
|
+
location: mockLocation,
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Mock time slot for afternoon
|
|
59
|
+
*/
|
|
60
|
+
export const mockTimeSlotAfternoon = {
|
|
61
|
+
startDate: '2024-03-15T14:00:00Z',
|
|
62
|
+
endDate: '2024-03-15T15:00:00Z',
|
|
63
|
+
totalCapacity: 10,
|
|
64
|
+
remainingCapacity: 8,
|
|
65
|
+
scheduleId: 'schedule-456',
|
|
66
|
+
resources: [mockResourceTwo],
|
|
67
|
+
location: mockLocationTwo,
|
|
68
|
+
};
|
|
51
69
|
/**
|
|
52
70
|
* Mock service selection with all data
|
|
53
71
|
*/
|
|
@@ -77,20 +95,6 @@ export const mockServiceSelectionClass = {
|
|
|
77
95
|
timeSlot: mockTimeSlotAfternoon,
|
|
78
96
|
totalParticipants: 3,
|
|
79
97
|
};
|
|
80
|
-
/**
|
|
81
|
-
* Mock location
|
|
82
|
-
*/
|
|
83
|
-
export const mockLocation = {
|
|
84
|
-
id: 'location-1',
|
|
85
|
-
name: 'Main Office',
|
|
86
|
-
};
|
|
87
|
-
/**
|
|
88
|
-
* Mock location (second)
|
|
89
|
-
*/
|
|
90
|
-
export const mockLocationTwo = {
|
|
91
|
-
id: 'location-2',
|
|
92
|
-
name: 'Downtown Branch',
|
|
93
|
-
};
|
|
94
98
|
/**
|
|
95
99
|
* Mock form submission (contact info)
|
|
96
100
|
*/
|
|
@@ -43,7 +43,7 @@ function RootContent({ children, }) {
|
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
45
|
export function Root(props) {
|
|
46
|
-
const { children, paymentServiceConfig = {}, slotServices, numberOfParticipants } = props;
|
|
46
|
+
const { children, paymentServiceConfig = {}, slotServices, numberOfParticipants, } = props;
|
|
47
47
|
// Merge public config with lazy loading props into internal config
|
|
48
48
|
const internalConfig = {
|
|
49
49
|
...paymentServiceConfig,
|
|
@@ -21,13 +21,13 @@ export interface StartDateProps {
|
|
|
21
21
|
className?: string;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* Displays the time slot start
|
|
25
|
-
*
|
|
24
|
+
* Displays the time slot start time.
|
|
25
|
+
* Default formatting shows time as "10:00 AM" (hour:minute with AM/PM).
|
|
26
26
|
*
|
|
27
27
|
* @component
|
|
28
28
|
* @example
|
|
29
29
|
* ```tsx
|
|
30
|
-
* // Default usage - displays
|
|
30
|
+
* // Default usage - displays "10:00 AM"
|
|
31
31
|
* <TimeSlot.StartDate className="text-foreground" />
|
|
32
32
|
*
|
|
33
33
|
* // With custom formatting
|
|
@@ -50,13 +50,13 @@ export interface EndDateProps {
|
|
|
50
50
|
className?: string;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
* Displays the time slot end
|
|
54
|
-
*
|
|
53
|
+
* Displays the time slot end time.
|
|
54
|
+
* Default formatting shows time as "11:00 AM" (hour:minute with AM/PM).
|
|
55
55
|
*
|
|
56
56
|
* @component
|
|
57
57
|
* @example
|
|
58
58
|
* ```tsx
|
|
59
|
-
* // Default usage - displays
|
|
59
|
+
* // Default usage - displays "11:00 AM"
|
|
60
60
|
* <TimeSlot.EndDate className="text-foreground" />
|
|
61
61
|
*
|
|
62
62
|
* // With custom formatting
|
|
@@ -112,12 +112,16 @@ export interface SelectProps {
|
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
114
|
* Button to select this time slot.
|
|
115
|
+
* Default label is "Select".
|
|
115
116
|
*
|
|
116
117
|
* @component
|
|
117
118
|
* @example
|
|
118
119
|
* ```tsx
|
|
119
|
-
* // Default button
|
|
120
|
-
* <TimeSlot.Actions.Select
|
|
120
|
+
* // Default button with "Select" label
|
|
121
|
+
* <TimeSlot.Actions.Select />
|
|
122
|
+
*
|
|
123
|
+
* // With custom label
|
|
124
|
+
* <TimeSlot.Actions.Select label="Book" />
|
|
121
125
|
*
|
|
122
126
|
* // With asChild
|
|
123
127
|
* <TimeSlot.Actions.Select asChild>
|
|
@@ -149,11 +153,15 @@ export interface ClearStaffSelectionProps {
|
|
|
149
153
|
/**
|
|
150
154
|
* Button to clear staff selection while keeping the slot selected.
|
|
151
155
|
* Only renders when a staff member is selected and there are multiple staff options.
|
|
156
|
+
* Default label is "Change".
|
|
152
157
|
*
|
|
153
158
|
* @component
|
|
154
159
|
* @example
|
|
155
160
|
* ```tsx
|
|
156
|
-
* // Default button
|
|
161
|
+
* // Default button with "Change" label
|
|
162
|
+
* <TimeSlot.Actions.ClearStaffSelection />
|
|
163
|
+
*
|
|
164
|
+
* // With custom label
|
|
157
165
|
* <TimeSlot.Actions.ClearStaffSelection label="Change staff" />
|
|
158
166
|
*
|
|
159
167
|
* // With asChild
|
|
@@ -297,12 +305,16 @@ export interface SelectStaffMemberProps {
|
|
|
297
305
|
}
|
|
298
306
|
/**
|
|
299
307
|
* Button to select this staff member.
|
|
308
|
+
* Default label is "Select".
|
|
300
309
|
*
|
|
301
310
|
* @component
|
|
302
311
|
* @example
|
|
303
312
|
* ```tsx
|
|
304
|
-
* // Default button
|
|
305
|
-
* <TimeSlot.StaffMember.Actions.Select
|
|
313
|
+
* // Default button with "Select" label
|
|
314
|
+
* <TimeSlot.StaffMember.Actions.Select />
|
|
315
|
+
*
|
|
316
|
+
* // With custom label
|
|
317
|
+
* <TimeSlot.StaffMember.Actions.Select label="Choose" />
|
|
306
318
|
*
|
|
307
319
|
* // With asChild
|
|
308
320
|
* <TimeSlot.StaffMember.Actions.Select asChild>
|
|
@@ -31,13 +31,13 @@ var TestIds;
|
|
|
31
31
|
TestIds["timeSlotStaffMemberActionSelect"] = "time-slot-staff-member-action-select";
|
|
32
32
|
})(TestIds || (TestIds = {}));
|
|
33
33
|
/**
|
|
34
|
-
* Displays the time slot start
|
|
35
|
-
*
|
|
34
|
+
* Displays the time slot start time.
|
|
35
|
+
* Default formatting shows time as "10:00 AM" (hour:minute with AM/PM).
|
|
36
36
|
*
|
|
37
37
|
* @component
|
|
38
38
|
* @example
|
|
39
39
|
* ```tsx
|
|
40
|
-
* // Default usage - displays
|
|
40
|
+
* // Default usage - displays "10:00 AM"
|
|
41
41
|
* <TimeSlot.StartDate className="text-foreground" />
|
|
42
42
|
*
|
|
43
43
|
* // With custom formatting
|
|
@@ -50,17 +50,20 @@ var TestIds;
|
|
|
50
50
|
*/
|
|
51
51
|
export const StartDate = React.forwardRef((props, ref) => {
|
|
52
52
|
const { asChild, children, className } = props;
|
|
53
|
-
return (_jsx(CoreTimeSlot.Info, { children: ({ startDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotStartDate, customElement: children, customElementProps: { startDate }, children: _jsx("span", { children: startDate.
|
|
53
|
+
return (_jsx(CoreTimeSlot.Info, { children: ({ startDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotStartDate, customElement: children, customElementProps: { startDate }, children: _jsx("span", { children: startDate.toLocaleTimeString([], {
|
|
54
|
+
hour: '2-digit',
|
|
55
|
+
minute: '2-digit',
|
|
56
|
+
}) }) })) }));
|
|
54
57
|
});
|
|
55
58
|
StartDate.displayName = 'TimeSlot.StartDate';
|
|
56
59
|
/**
|
|
57
|
-
* Displays the time slot end
|
|
58
|
-
*
|
|
60
|
+
* Displays the time slot end time.
|
|
61
|
+
* Default formatting shows time as "11:00 AM" (hour:minute with AM/PM).
|
|
59
62
|
*
|
|
60
63
|
* @component
|
|
61
64
|
* @example
|
|
62
65
|
* ```tsx
|
|
63
|
-
* // Default usage - displays
|
|
66
|
+
* // Default usage - displays "11:00 AM"
|
|
64
67
|
* <TimeSlot.EndDate className="text-foreground" />
|
|
65
68
|
*
|
|
66
69
|
* // With custom formatting
|
|
@@ -73,7 +76,10 @@ StartDate.displayName = 'TimeSlot.StartDate';
|
|
|
73
76
|
*/
|
|
74
77
|
export const EndDate = React.forwardRef((props, ref) => {
|
|
75
78
|
const { asChild, children, className } = props;
|
|
76
|
-
return (_jsx(CoreTimeSlot.Info, { children: ({ endDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotEndDate, customElement: children, customElementProps: { endDate }, children: _jsx("span", { children: endDate.
|
|
79
|
+
return (_jsx(CoreTimeSlot.Info, { children: ({ endDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotEndDate, customElement: children, customElementProps: { endDate }, children: _jsx("span", { children: endDate.toLocaleTimeString([], {
|
|
80
|
+
hour: '2-digit',
|
|
81
|
+
minute: '2-digit',
|
|
82
|
+
}) }) })) }));
|
|
77
83
|
});
|
|
78
84
|
EndDate.displayName = 'TimeSlot.EndDate';
|
|
79
85
|
/**
|
|
@@ -101,12 +107,16 @@ export const Duration = React.forwardRef((props, ref) => {
|
|
|
101
107
|
Duration.displayName = 'TimeSlot.Duration';
|
|
102
108
|
/**
|
|
103
109
|
* Button to select this time slot.
|
|
110
|
+
* Default label is "Select".
|
|
104
111
|
*
|
|
105
112
|
* @component
|
|
106
113
|
* @example
|
|
107
114
|
* ```tsx
|
|
108
|
-
* // Default button
|
|
109
|
-
* <TimeSlot.Actions.Select
|
|
115
|
+
* // Default button with "Select" label
|
|
116
|
+
* <TimeSlot.Actions.Select />
|
|
117
|
+
*
|
|
118
|
+
* // With custom label
|
|
119
|
+
* <TimeSlot.Actions.Select label="Book" />
|
|
110
120
|
*
|
|
111
121
|
* // With asChild
|
|
112
122
|
* <TimeSlot.Actions.Select asChild>
|
|
@@ -124,7 +134,7 @@ Duration.displayName = 'TimeSlot.Duration';
|
|
|
124
134
|
* ```
|
|
125
135
|
*/
|
|
126
136
|
export const Select = React.forwardRef((props, ref) => {
|
|
127
|
-
const { asChild, children, className, label } = props;
|
|
137
|
+
const { asChild, children, className, label = 'Select' } = props;
|
|
128
138
|
return (_jsx(CoreTimeSlot.Actions, { children: ({ selectTimeSlot, isSelected, bookable }) => {
|
|
129
139
|
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotActionSelect, "data-selected": isSelected, "data-bookable": bookable, customElement: children, customElementProps: {
|
|
130
140
|
onClick: selectTimeSlot,
|
|
@@ -138,11 +148,15 @@ Select.displayName = 'TimeSlot.Actions.Select';
|
|
|
138
148
|
/**
|
|
139
149
|
* Button to clear staff selection while keeping the slot selected.
|
|
140
150
|
* Only renders when a staff member is selected and there are multiple staff options.
|
|
151
|
+
* Default label is "Change".
|
|
141
152
|
*
|
|
142
153
|
* @component
|
|
143
154
|
* @example
|
|
144
155
|
* ```tsx
|
|
145
|
-
* // Default button
|
|
156
|
+
* // Default button with "Change" label
|
|
157
|
+
* <TimeSlot.Actions.ClearStaffSelection />
|
|
158
|
+
*
|
|
159
|
+
* // With custom label
|
|
146
160
|
* <TimeSlot.Actions.ClearStaffSelection label="Change staff" />
|
|
147
161
|
*
|
|
148
162
|
* // With asChild
|
|
@@ -157,7 +171,7 @@ Select.displayName = 'TimeSlot.Actions.Select';
|
|
|
157
171
|
* ```
|
|
158
172
|
*/
|
|
159
173
|
export const ClearStaffSelection = React.forwardRef((props, ref) => {
|
|
160
|
-
const { asChild, children, className, label } = props;
|
|
174
|
+
const { asChild, children, className, label = 'Change' } = props;
|
|
161
175
|
return (_jsx(CoreTimeSlot.Actions, { children: ({ clearStaffSelection }) => {
|
|
162
176
|
if (!clearStaffSelection) {
|
|
163
177
|
return null;
|
|
@@ -278,12 +292,16 @@ export const StaffMemberName = React.forwardRef((props, ref) => {
|
|
|
278
292
|
StaffMemberName.displayName = 'TimeSlot.StaffMember.Name';
|
|
279
293
|
/**
|
|
280
294
|
* Button to select this staff member.
|
|
295
|
+
* Default label is "Select".
|
|
281
296
|
*
|
|
282
297
|
* @component
|
|
283
298
|
* @example
|
|
284
299
|
* ```tsx
|
|
285
|
-
* // Default button
|
|
286
|
-
* <TimeSlot.StaffMember.Actions.Select
|
|
300
|
+
* // Default button with "Select" label
|
|
301
|
+
* <TimeSlot.StaffMember.Actions.Select />
|
|
302
|
+
*
|
|
303
|
+
* // With custom label
|
|
304
|
+
* <TimeSlot.StaffMember.Actions.Select label="Choose" />
|
|
287
305
|
*
|
|
288
306
|
* // With asChild
|
|
289
307
|
* <TimeSlot.StaffMember.Actions.Select asChild>
|
|
@@ -301,7 +319,7 @@ StaffMemberName.displayName = 'TimeSlot.StaffMember.Name';
|
|
|
301
319
|
* ```
|
|
302
320
|
*/
|
|
303
321
|
export const SelectStaffMember = React.forwardRef((props, ref) => {
|
|
304
|
-
const { asChild, children, className, label } = props;
|
|
322
|
+
const { asChild, children, className, label = 'Select' } = props;
|
|
305
323
|
return (_jsx(CoreTimeSlot.StaffMemberActions, { children: ({ selectStaffMember, isSelected }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotStaffMemberActionSelect, "data-selected": isSelected, customElement: children, customElementProps: {
|
|
306
324
|
onClick: selectStaffMember,
|
|
307
325
|
isSelected,
|
|
@@ -190,9 +190,15 @@ export interface DateRangeInputProps {
|
|
|
190
190
|
* Uses the core DateRange component to access service data.
|
|
191
191
|
* Both dates are set together and trigger a single fetch.
|
|
192
192
|
*
|
|
193
|
+
* Default rendering provides two native date inputs.
|
|
194
|
+
*
|
|
193
195
|
* @component
|
|
194
196
|
* @example
|
|
195
197
|
* ```tsx
|
|
198
|
+
* // Default usage - renders two date inputs
|
|
199
|
+
* <TimeSlotList.DateRange.Input className="flex gap-2" />
|
|
200
|
+
*
|
|
201
|
+
* // With asChild for custom rendering
|
|
196
202
|
* <TimeSlotList.DateRange.Input asChild>
|
|
197
203
|
* {({ startDate, endDate, onChange }) => (
|
|
198
204
|
* <MyDateRangePicker
|
|
@@ -9,6 +9,15 @@ 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
11
|
import { StartDate as TimeSlotStartDate, EndDate as TimeSlotEndDate, Duration, Actions as TimeSlotActions, Location, StaffMembers, StaffMemberRepeater, StaffMember, } from './TimeSlot.js';
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Utilities
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Converts a Date to ISO date string format (YYYY-MM-DD) for input[type="date"]
|
|
17
|
+
*/
|
|
18
|
+
function toISODateString(date) {
|
|
19
|
+
return date.toISOString().split('T')[0] ?? '';
|
|
20
|
+
}
|
|
12
21
|
/**
|
|
13
22
|
* TimeSlot with required id field for GenericList
|
|
14
23
|
*/
|
|
@@ -178,9 +187,15 @@ export const Actions = {
|
|
|
178
187
|
* Uses the core DateRange component to access service data.
|
|
179
188
|
* Both dates are set together and trigger a single fetch.
|
|
180
189
|
*
|
|
190
|
+
* Default rendering provides two native date inputs.
|
|
191
|
+
*
|
|
181
192
|
* @component
|
|
182
193
|
* @example
|
|
183
194
|
* ```tsx
|
|
195
|
+
* // Default usage - renders two date inputs
|
|
196
|
+
* <TimeSlotList.DateRange.Input className="flex gap-2" />
|
|
197
|
+
*
|
|
198
|
+
* // With asChild for custom rendering
|
|
184
199
|
* <TimeSlotList.DateRange.Input asChild>
|
|
185
200
|
* {({ startDate, endDate, onChange }) => (
|
|
186
201
|
* <MyDateRangePicker
|
|
@@ -194,11 +209,14 @@ export const Actions = {
|
|
|
194
209
|
*/
|
|
195
210
|
export const DateRangeInput = React.forwardRef((props, ref) => {
|
|
196
211
|
const { asChild, children, className, ...otherProps } = props;
|
|
197
|
-
return (_jsx(CoreTimeSlotList.DateRange, { children: ({ startDate, endDate, setDateRange }) =>
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
212
|
+
return (_jsx(CoreTimeSlotList.DateRange, { children: ({ startDate, endDate, setDateRange }) => {
|
|
213
|
+
const defaultContent = (_jsxs("div", { ref: ref, className: className, "data-testid": TestIds.timeSlotListDateRangeInput, children: [_jsx("input", { type: "date", value: toISODateString(startDate), onChange: (e) => setDateRange(new Date(e.target.value), endDate) }), _jsx("span", { children: "-" }), _jsx("input", { type: "date", value: toISODateString(endDate), onChange: (e) => setDateRange(startDate, new Date(e.target.value)) })] }));
|
|
214
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotListDateRangeInput, customElement: children, customElementProps: {
|
|
215
|
+
startDate,
|
|
216
|
+
endDate,
|
|
217
|
+
onChange: setDateRange,
|
|
218
|
+
}, ...otherProps, children: defaultContent }));
|
|
219
|
+
} }));
|
|
202
220
|
});
|
|
203
221
|
DateRangeInput.displayName = 'TimeSlotList.DateRange.Input';
|
|
204
222
|
/**
|
|
@@ -43,8 +43,9 @@ export async function executeBookAction(params) {
|
|
|
43
43
|
throw new Error('Failed to create booking');
|
|
44
44
|
}
|
|
45
45
|
// Step 2: Create checkout
|
|
46
|
-
// businessLocationId
|
|
47
|
-
const
|
|
46
|
+
// businessLocationId: prefer timeSlot.location, fallback to global location
|
|
47
|
+
const firstSelection = params.serviceSelections[0];
|
|
48
|
+
const businessLocationId = firstSelection?.timeSlot?.location?.id ?? undefined;
|
|
48
49
|
const checkoutRequest = buildCheckoutRequest({
|
|
49
50
|
bookingId,
|
|
50
51
|
contactDetails,
|
|
@@ -46,8 +46,10 @@ export function buildBookingRequest(params) {
|
|
|
46
46
|
if (!item.timeSlot) {
|
|
47
47
|
throw new Error('No time slot selected for service selection');
|
|
48
48
|
}
|
|
49
|
-
// Get resource
|
|
50
|
-
const resource = item.resources?.[0];
|
|
49
|
+
// Get resource: prefer timeSlot.resources, fallback to item.resources
|
|
50
|
+
const resource = item.timeSlot.resources?.[0] ?? item.resources?.[0];
|
|
51
|
+
// Get location: prefer timeSlot.location, fallback to params.location
|
|
52
|
+
const slotLocation = item.timeSlot.location ?? location;
|
|
51
53
|
return {
|
|
52
54
|
booking: {
|
|
53
55
|
// Payment option derived from service payment settings
|
|
@@ -66,8 +68,8 @@ export function buildBookingRequest(params) {
|
|
|
66
68
|
resource: resource
|
|
67
69
|
? { _id: resource._id, name: resource.name }
|
|
68
70
|
: undefined,
|
|
69
|
-
location:
|
|
70
|
-
? { _id:
|
|
71
|
+
location: slotLocation
|
|
72
|
+
? { _id: slotLocation.id, name: slotLocation.name }
|
|
71
73
|
: undefined,
|
|
72
74
|
},
|
|
73
75
|
},
|
|
@@ -19,6 +19,10 @@ export interface ServiceSelectionTimeSlot {
|
|
|
19
19
|
remainingCapacity?: number;
|
|
20
20
|
scheduleId?: string;
|
|
21
21
|
eventInfo?: EventInfo;
|
|
22
|
+
/** Resources available in this time slot (populated when slot is selected) */
|
|
23
|
+
resources?: Resource[];
|
|
24
|
+
/** Location of this time slot (populated when slot is selected) */
|
|
25
|
+
location?: Location;
|
|
22
26
|
}
|
|
23
27
|
/**
|
|
24
28
|
* ServiceSelection contains the service AND its related selections
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { type PaymentServiceConfig, type PaymentServiceInternalConfig, type SlotService } from './payment.def.js';
|
|
7
7
|
export { PaymentServiceDefinition, type PaymentServiceConfig, type PaymentDetails, type LineItemAdditionalInfo, type SlotService, } from './payment.def.js';
|
|
8
|
-
export type { PaymentServiceAPI, PaymentServiceInternalConfig } from './payment.def.js';
|
|
8
|
+
export type { PaymentServiceAPI, PaymentServiceInternalConfig, } from './payment.def.js';
|
|
9
9
|
export declare const PaymentService: import("@wix/services-definitions").ServiceFactory<string & {
|
|
10
10
|
__api: import("./payment.def.js").PaymentServiceAPI;
|
|
11
11
|
__config: {};
|
|
@@ -86,6 +86,14 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
86
86
|
* - Auto-selects staff only when there is exactly one staff option in this slot
|
|
87
87
|
*/
|
|
88
88
|
const selectSlot = (slot) => {
|
|
89
|
+
const staffMembersFromSlot = slot.availableResources[0].resources.map((r) => ({
|
|
90
|
+
_id: r._id,
|
|
91
|
+
name: r.name,
|
|
92
|
+
}));
|
|
93
|
+
const slotLocation = {
|
|
94
|
+
id: slot.location?._id,
|
|
95
|
+
name: slot.location?.name ?? undefined,
|
|
96
|
+
};
|
|
89
97
|
const timeSlot = {
|
|
90
98
|
startDate: slot.localStartDate,
|
|
91
99
|
endDate: slot.localEndDate,
|
|
@@ -93,15 +101,12 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
93
101
|
remainingCapacity: slot.remainingCapacity ?? undefined,
|
|
94
102
|
scheduleId: slot.scheduleId ?? undefined,
|
|
95
103
|
eventInfo: slot.eventInfo ?? undefined,
|
|
104
|
+
resources: staffMembersFromSlot,
|
|
105
|
+
location: slotLocation,
|
|
96
106
|
};
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
});
|
|
101
|
-
const staffMembersFromSlot = slot.availableResources[0].resources.map((r) => ({
|
|
102
|
-
id: r._id,
|
|
103
|
-
name: r.name,
|
|
104
|
-
}));
|
|
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
|
|
105
110
|
const resources = staffMembersFromSlot.length === 1 ? staffMembersFromSlot : [];
|
|
106
111
|
bookingService.actions.updateItem({
|
|
107
112
|
timeSlot,
|
|
@@ -4,14 +4,6 @@
|
|
|
4
4
|
import type { Resource } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
5
5
|
import type { FormValues } from '@wix/form-public';
|
|
6
6
|
import type { ServiceSelection, ServiceSelectionTimeSlot, Location, BookingServiceConfig } from '../services/booking/booking.js';
|
|
7
|
-
/**
|
|
8
|
-
* Mock time slot data
|
|
9
|
-
*/
|
|
10
|
-
export declare const mockTimeSlot: ServiceSelectionTimeSlot;
|
|
11
|
-
/**
|
|
12
|
-
* Mock time slot for afternoon
|
|
13
|
-
*/
|
|
14
|
-
export declare const mockTimeSlotAfternoon: ServiceSelectionTimeSlot;
|
|
15
7
|
/**
|
|
16
8
|
* Mock resource (staff member)
|
|
17
9
|
*/
|
|
@@ -24,6 +16,22 @@ export declare const mockResourceTwo: Resource;
|
|
|
24
16
|
* Mock resource (room)
|
|
25
17
|
*/
|
|
26
18
|
export declare const mockResourceRoom: Resource;
|
|
19
|
+
/**
|
|
20
|
+
* Mock location
|
|
21
|
+
*/
|
|
22
|
+
export declare const mockLocation: Location;
|
|
23
|
+
/**
|
|
24
|
+
* Mock location (second)
|
|
25
|
+
*/
|
|
26
|
+
export declare const mockLocationTwo: Location;
|
|
27
|
+
/**
|
|
28
|
+
* Mock time slot data
|
|
29
|
+
*/
|
|
30
|
+
export declare const mockTimeSlot: ServiceSelectionTimeSlot;
|
|
31
|
+
/**
|
|
32
|
+
* Mock time slot for afternoon
|
|
33
|
+
*/
|
|
34
|
+
export declare const mockTimeSlotAfternoon: ServiceSelectionTimeSlot;
|
|
27
35
|
/**
|
|
28
36
|
* Mock service selection with all data
|
|
29
37
|
*/
|
|
@@ -36,14 +44,6 @@ export declare const mockServiceSelectionWithoutTimeSlot: ServiceSelection;
|
|
|
36
44
|
* Mock service selection for class
|
|
37
45
|
*/
|
|
38
46
|
export declare const mockServiceSelectionClass: ServiceSelection;
|
|
39
|
-
/**
|
|
40
|
-
* Mock location
|
|
41
|
-
*/
|
|
42
|
-
export declare const mockLocation: Location;
|
|
43
|
-
/**
|
|
44
|
-
* Mock location (second)
|
|
45
|
-
*/
|
|
46
|
-
export declare const mockLocationTwo: Location;
|
|
47
47
|
/**
|
|
48
48
|
* Mock form submission (contact info)
|
|
49
49
|
*/
|
|
@@ -2,26 +2,6 @@
|
|
|
2
2
|
* Shared mock data for booking service tests
|
|
3
3
|
*/
|
|
4
4
|
import { mockService, mockServiceClass } from './services.js';
|
|
5
|
-
/**
|
|
6
|
-
* Mock time slot data
|
|
7
|
-
*/
|
|
8
|
-
export const mockTimeSlot = {
|
|
9
|
-
startDate: '2024-03-15T10:00:00Z',
|
|
10
|
-
endDate: '2024-03-15T11:00:00Z',
|
|
11
|
-
totalCapacity: 10,
|
|
12
|
-
remainingCapacity: 5,
|
|
13
|
-
scheduleId: 'schedule-123',
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Mock time slot for afternoon
|
|
17
|
-
*/
|
|
18
|
-
export const mockTimeSlotAfternoon = {
|
|
19
|
-
startDate: '2024-03-15T14:00:00Z',
|
|
20
|
-
endDate: '2024-03-15T15:00:00Z',
|
|
21
|
-
totalCapacity: 10,
|
|
22
|
-
remainingCapacity: 8,
|
|
23
|
-
scheduleId: 'schedule-456',
|
|
24
|
-
};
|
|
25
5
|
/**
|
|
26
6
|
* Mock resource (staff member)
|
|
27
7
|
*/
|
|
@@ -48,6 +28,44 @@ export const mockResourceRoom = {
|
|
|
48
28
|
name: 'Conference Room A',
|
|
49
29
|
tag: 'RESOURCE',
|
|
50
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Mock location
|
|
33
|
+
*/
|
|
34
|
+
export const mockLocation = {
|
|
35
|
+
id: 'location-1',
|
|
36
|
+
name: 'Main Office',
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Mock location (second)
|
|
40
|
+
*/
|
|
41
|
+
export const mockLocationTwo = {
|
|
42
|
+
id: 'location-2',
|
|
43
|
+
name: 'Downtown Branch',
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Mock time slot data
|
|
47
|
+
*/
|
|
48
|
+
export const mockTimeSlot = {
|
|
49
|
+
startDate: '2024-03-15T10:00:00Z',
|
|
50
|
+
endDate: '2024-03-15T11:00:00Z',
|
|
51
|
+
totalCapacity: 10,
|
|
52
|
+
remainingCapacity: 5,
|
|
53
|
+
scheduleId: 'schedule-123',
|
|
54
|
+
resources: [mockResource],
|
|
55
|
+
location: mockLocation,
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Mock time slot for afternoon
|
|
59
|
+
*/
|
|
60
|
+
export const mockTimeSlotAfternoon = {
|
|
61
|
+
startDate: '2024-03-15T14:00:00Z',
|
|
62
|
+
endDate: '2024-03-15T15:00:00Z',
|
|
63
|
+
totalCapacity: 10,
|
|
64
|
+
remainingCapacity: 8,
|
|
65
|
+
scheduleId: 'schedule-456',
|
|
66
|
+
resources: [mockResourceTwo],
|
|
67
|
+
location: mockLocationTwo,
|
|
68
|
+
};
|
|
51
69
|
/**
|
|
52
70
|
* Mock service selection with all data
|
|
53
71
|
*/
|
|
@@ -77,20 +95,6 @@ export const mockServiceSelectionClass = {
|
|
|
77
95
|
timeSlot: mockTimeSlotAfternoon,
|
|
78
96
|
totalParticipants: 3,
|
|
79
97
|
};
|
|
80
|
-
/**
|
|
81
|
-
* Mock location
|
|
82
|
-
*/
|
|
83
|
-
export const mockLocation = {
|
|
84
|
-
id: 'location-1',
|
|
85
|
-
name: 'Main Office',
|
|
86
|
-
};
|
|
87
|
-
/**
|
|
88
|
-
* Mock location (second)
|
|
89
|
-
*/
|
|
90
|
-
export const mockLocationTwo = {
|
|
91
|
-
id: 'location-2',
|
|
92
|
-
name: 'Downtown Branch',
|
|
93
|
-
};
|
|
94
98
|
/**
|
|
95
99
|
* Mock form submission (contact info)
|
|
96
100
|
*/
|
|
@@ -43,7 +43,7 @@ function RootContent({ children, }) {
|
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
45
|
export function Root(props) {
|
|
46
|
-
const { children, paymentServiceConfig = {}, slotServices, numberOfParticipants } = props;
|
|
46
|
+
const { children, paymentServiceConfig = {}, slotServices, numberOfParticipants, } = props;
|
|
47
47
|
// Merge public config with lazy loading props into internal config
|
|
48
48
|
const internalConfig = {
|
|
49
49
|
...paymentServiceConfig,
|
|
@@ -21,13 +21,13 @@ export interface StartDateProps {
|
|
|
21
21
|
className?: string;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* Displays the time slot start
|
|
25
|
-
*
|
|
24
|
+
* Displays the time slot start time.
|
|
25
|
+
* Default formatting shows time as "10:00 AM" (hour:minute with AM/PM).
|
|
26
26
|
*
|
|
27
27
|
* @component
|
|
28
28
|
* @example
|
|
29
29
|
* ```tsx
|
|
30
|
-
* // Default usage - displays
|
|
30
|
+
* // Default usage - displays "10:00 AM"
|
|
31
31
|
* <TimeSlot.StartDate className="text-foreground" />
|
|
32
32
|
*
|
|
33
33
|
* // With custom formatting
|
|
@@ -50,13 +50,13 @@ export interface EndDateProps {
|
|
|
50
50
|
className?: string;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
* Displays the time slot end
|
|
54
|
-
*
|
|
53
|
+
* Displays the time slot end time.
|
|
54
|
+
* Default formatting shows time as "11:00 AM" (hour:minute with AM/PM).
|
|
55
55
|
*
|
|
56
56
|
* @component
|
|
57
57
|
* @example
|
|
58
58
|
* ```tsx
|
|
59
|
-
* // Default usage - displays
|
|
59
|
+
* // Default usage - displays "11:00 AM"
|
|
60
60
|
* <TimeSlot.EndDate className="text-foreground" />
|
|
61
61
|
*
|
|
62
62
|
* // With custom formatting
|
|
@@ -112,12 +112,16 @@ export interface SelectProps {
|
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
114
|
* Button to select this time slot.
|
|
115
|
+
* Default label is "Select".
|
|
115
116
|
*
|
|
116
117
|
* @component
|
|
117
118
|
* @example
|
|
118
119
|
* ```tsx
|
|
119
|
-
* // Default button
|
|
120
|
-
* <TimeSlot.Actions.Select
|
|
120
|
+
* // Default button with "Select" label
|
|
121
|
+
* <TimeSlot.Actions.Select />
|
|
122
|
+
*
|
|
123
|
+
* // With custom label
|
|
124
|
+
* <TimeSlot.Actions.Select label="Book" />
|
|
121
125
|
*
|
|
122
126
|
* // With asChild
|
|
123
127
|
* <TimeSlot.Actions.Select asChild>
|
|
@@ -149,11 +153,15 @@ export interface ClearStaffSelectionProps {
|
|
|
149
153
|
/**
|
|
150
154
|
* Button to clear staff selection while keeping the slot selected.
|
|
151
155
|
* Only renders when a staff member is selected and there are multiple staff options.
|
|
156
|
+
* Default label is "Change".
|
|
152
157
|
*
|
|
153
158
|
* @component
|
|
154
159
|
* @example
|
|
155
160
|
* ```tsx
|
|
156
|
-
* // Default button
|
|
161
|
+
* // Default button with "Change" label
|
|
162
|
+
* <TimeSlot.Actions.ClearStaffSelection />
|
|
163
|
+
*
|
|
164
|
+
* // With custom label
|
|
157
165
|
* <TimeSlot.Actions.ClearStaffSelection label="Change staff" />
|
|
158
166
|
*
|
|
159
167
|
* // With asChild
|
|
@@ -297,12 +305,16 @@ export interface SelectStaffMemberProps {
|
|
|
297
305
|
}
|
|
298
306
|
/**
|
|
299
307
|
* Button to select this staff member.
|
|
308
|
+
* Default label is "Select".
|
|
300
309
|
*
|
|
301
310
|
* @component
|
|
302
311
|
* @example
|
|
303
312
|
* ```tsx
|
|
304
|
-
* // Default button
|
|
305
|
-
* <TimeSlot.StaffMember.Actions.Select
|
|
313
|
+
* // Default button with "Select" label
|
|
314
|
+
* <TimeSlot.StaffMember.Actions.Select />
|
|
315
|
+
*
|
|
316
|
+
* // With custom label
|
|
317
|
+
* <TimeSlot.StaffMember.Actions.Select label="Choose" />
|
|
306
318
|
*
|
|
307
319
|
* // With asChild
|
|
308
320
|
* <TimeSlot.StaffMember.Actions.Select asChild>
|
|
@@ -31,13 +31,13 @@ var TestIds;
|
|
|
31
31
|
TestIds["timeSlotStaffMemberActionSelect"] = "time-slot-staff-member-action-select";
|
|
32
32
|
})(TestIds || (TestIds = {}));
|
|
33
33
|
/**
|
|
34
|
-
* Displays the time slot start
|
|
35
|
-
*
|
|
34
|
+
* Displays the time slot start time.
|
|
35
|
+
* Default formatting shows time as "10:00 AM" (hour:minute with AM/PM).
|
|
36
36
|
*
|
|
37
37
|
* @component
|
|
38
38
|
* @example
|
|
39
39
|
* ```tsx
|
|
40
|
-
* // Default usage - displays
|
|
40
|
+
* // Default usage - displays "10:00 AM"
|
|
41
41
|
* <TimeSlot.StartDate className="text-foreground" />
|
|
42
42
|
*
|
|
43
43
|
* // With custom formatting
|
|
@@ -50,17 +50,20 @@ var TestIds;
|
|
|
50
50
|
*/
|
|
51
51
|
export const StartDate = React.forwardRef((props, ref) => {
|
|
52
52
|
const { asChild, children, className } = props;
|
|
53
|
-
return (_jsx(CoreTimeSlot.Info, { children: ({ startDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotStartDate, customElement: children, customElementProps: { startDate }, children: _jsx("span", { children: startDate.
|
|
53
|
+
return (_jsx(CoreTimeSlot.Info, { children: ({ startDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotStartDate, customElement: children, customElementProps: { startDate }, children: _jsx("span", { children: startDate.toLocaleTimeString([], {
|
|
54
|
+
hour: '2-digit',
|
|
55
|
+
minute: '2-digit',
|
|
56
|
+
}) }) })) }));
|
|
54
57
|
});
|
|
55
58
|
StartDate.displayName = 'TimeSlot.StartDate';
|
|
56
59
|
/**
|
|
57
|
-
* Displays the time slot end
|
|
58
|
-
*
|
|
60
|
+
* Displays the time slot end time.
|
|
61
|
+
* Default formatting shows time as "11:00 AM" (hour:minute with AM/PM).
|
|
59
62
|
*
|
|
60
63
|
* @component
|
|
61
64
|
* @example
|
|
62
65
|
* ```tsx
|
|
63
|
-
* // Default usage - displays
|
|
66
|
+
* // Default usage - displays "11:00 AM"
|
|
64
67
|
* <TimeSlot.EndDate className="text-foreground" />
|
|
65
68
|
*
|
|
66
69
|
* // With custom formatting
|
|
@@ -73,7 +76,10 @@ StartDate.displayName = 'TimeSlot.StartDate';
|
|
|
73
76
|
*/
|
|
74
77
|
export const EndDate = React.forwardRef((props, ref) => {
|
|
75
78
|
const { asChild, children, className } = props;
|
|
76
|
-
return (_jsx(CoreTimeSlot.Info, { children: ({ endDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotEndDate, customElement: children, customElementProps: { endDate }, children: _jsx("span", { children: endDate.
|
|
79
|
+
return (_jsx(CoreTimeSlot.Info, { children: ({ endDate }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotEndDate, customElement: children, customElementProps: { endDate }, children: _jsx("span", { children: endDate.toLocaleTimeString([], {
|
|
80
|
+
hour: '2-digit',
|
|
81
|
+
minute: '2-digit',
|
|
82
|
+
}) }) })) }));
|
|
77
83
|
});
|
|
78
84
|
EndDate.displayName = 'TimeSlot.EndDate';
|
|
79
85
|
/**
|
|
@@ -101,12 +107,16 @@ export const Duration = React.forwardRef((props, ref) => {
|
|
|
101
107
|
Duration.displayName = 'TimeSlot.Duration';
|
|
102
108
|
/**
|
|
103
109
|
* Button to select this time slot.
|
|
110
|
+
* Default label is "Select".
|
|
104
111
|
*
|
|
105
112
|
* @component
|
|
106
113
|
* @example
|
|
107
114
|
* ```tsx
|
|
108
|
-
* // Default button
|
|
109
|
-
* <TimeSlot.Actions.Select
|
|
115
|
+
* // Default button with "Select" label
|
|
116
|
+
* <TimeSlot.Actions.Select />
|
|
117
|
+
*
|
|
118
|
+
* // With custom label
|
|
119
|
+
* <TimeSlot.Actions.Select label="Book" />
|
|
110
120
|
*
|
|
111
121
|
* // With asChild
|
|
112
122
|
* <TimeSlot.Actions.Select asChild>
|
|
@@ -124,7 +134,7 @@ Duration.displayName = 'TimeSlot.Duration';
|
|
|
124
134
|
* ```
|
|
125
135
|
*/
|
|
126
136
|
export const Select = React.forwardRef((props, ref) => {
|
|
127
|
-
const { asChild, children, className, label } = props;
|
|
137
|
+
const { asChild, children, className, label = 'Select' } = props;
|
|
128
138
|
return (_jsx(CoreTimeSlot.Actions, { children: ({ selectTimeSlot, isSelected, bookable }) => {
|
|
129
139
|
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotActionSelect, "data-selected": isSelected, "data-bookable": bookable, customElement: children, customElementProps: {
|
|
130
140
|
onClick: selectTimeSlot,
|
|
@@ -138,11 +148,15 @@ Select.displayName = 'TimeSlot.Actions.Select';
|
|
|
138
148
|
/**
|
|
139
149
|
* Button to clear staff selection while keeping the slot selected.
|
|
140
150
|
* Only renders when a staff member is selected and there are multiple staff options.
|
|
151
|
+
* Default label is "Change".
|
|
141
152
|
*
|
|
142
153
|
* @component
|
|
143
154
|
* @example
|
|
144
155
|
* ```tsx
|
|
145
|
-
* // Default button
|
|
156
|
+
* // Default button with "Change" label
|
|
157
|
+
* <TimeSlot.Actions.ClearStaffSelection />
|
|
158
|
+
*
|
|
159
|
+
* // With custom label
|
|
146
160
|
* <TimeSlot.Actions.ClearStaffSelection label="Change staff" />
|
|
147
161
|
*
|
|
148
162
|
* // With asChild
|
|
@@ -157,7 +171,7 @@ Select.displayName = 'TimeSlot.Actions.Select';
|
|
|
157
171
|
* ```
|
|
158
172
|
*/
|
|
159
173
|
export const ClearStaffSelection = React.forwardRef((props, ref) => {
|
|
160
|
-
const { asChild, children, className, label } = props;
|
|
174
|
+
const { asChild, children, className, label = 'Change' } = props;
|
|
161
175
|
return (_jsx(CoreTimeSlot.Actions, { children: ({ clearStaffSelection }) => {
|
|
162
176
|
if (!clearStaffSelection) {
|
|
163
177
|
return null;
|
|
@@ -278,12 +292,16 @@ export const StaffMemberName = React.forwardRef((props, ref) => {
|
|
|
278
292
|
StaffMemberName.displayName = 'TimeSlot.StaffMember.Name';
|
|
279
293
|
/**
|
|
280
294
|
* Button to select this staff member.
|
|
295
|
+
* Default label is "Select".
|
|
281
296
|
*
|
|
282
297
|
* @component
|
|
283
298
|
* @example
|
|
284
299
|
* ```tsx
|
|
285
|
-
* // Default button
|
|
286
|
-
* <TimeSlot.StaffMember.Actions.Select
|
|
300
|
+
* // Default button with "Select" label
|
|
301
|
+
* <TimeSlot.StaffMember.Actions.Select />
|
|
302
|
+
*
|
|
303
|
+
* // With custom label
|
|
304
|
+
* <TimeSlot.StaffMember.Actions.Select label="Choose" />
|
|
287
305
|
*
|
|
288
306
|
* // With asChild
|
|
289
307
|
* <TimeSlot.StaffMember.Actions.Select asChild>
|
|
@@ -301,7 +319,7 @@ StaffMemberName.displayName = 'TimeSlot.StaffMember.Name';
|
|
|
301
319
|
* ```
|
|
302
320
|
*/
|
|
303
321
|
export const SelectStaffMember = React.forwardRef((props, ref) => {
|
|
304
|
-
const { asChild, children, className, label } = props;
|
|
322
|
+
const { asChild, children, className, label = 'Select' } = props;
|
|
305
323
|
return (_jsx(CoreTimeSlot.StaffMemberActions, { children: ({ selectStaffMember, isSelected }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotStaffMemberActionSelect, "data-selected": isSelected, customElement: children, customElementProps: {
|
|
306
324
|
onClick: selectStaffMember,
|
|
307
325
|
isSelected,
|
|
@@ -190,9 +190,15 @@ export interface DateRangeInputProps {
|
|
|
190
190
|
* Uses the core DateRange component to access service data.
|
|
191
191
|
* Both dates are set together and trigger a single fetch.
|
|
192
192
|
*
|
|
193
|
+
* Default rendering provides two native date inputs.
|
|
194
|
+
*
|
|
193
195
|
* @component
|
|
194
196
|
* @example
|
|
195
197
|
* ```tsx
|
|
198
|
+
* // Default usage - renders two date inputs
|
|
199
|
+
* <TimeSlotList.DateRange.Input className="flex gap-2" />
|
|
200
|
+
*
|
|
201
|
+
* // With asChild for custom rendering
|
|
196
202
|
* <TimeSlotList.DateRange.Input asChild>
|
|
197
203
|
* {({ startDate, endDate, onChange }) => (
|
|
198
204
|
* <MyDateRangePicker
|
|
@@ -9,6 +9,15 @@ 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
11
|
import { StartDate as TimeSlotStartDate, EndDate as TimeSlotEndDate, Duration, Actions as TimeSlotActions, Location, StaffMembers, StaffMemberRepeater, StaffMember, } from './TimeSlot.js';
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Utilities
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Converts a Date to ISO date string format (YYYY-MM-DD) for input[type="date"]
|
|
17
|
+
*/
|
|
18
|
+
function toISODateString(date) {
|
|
19
|
+
return date.toISOString().split('T')[0] ?? '';
|
|
20
|
+
}
|
|
12
21
|
/**
|
|
13
22
|
* TimeSlot with required id field for GenericList
|
|
14
23
|
*/
|
|
@@ -178,9 +187,15 @@ export const Actions = {
|
|
|
178
187
|
* Uses the core DateRange component to access service data.
|
|
179
188
|
* Both dates are set together and trigger a single fetch.
|
|
180
189
|
*
|
|
190
|
+
* Default rendering provides two native date inputs.
|
|
191
|
+
*
|
|
181
192
|
* @component
|
|
182
193
|
* @example
|
|
183
194
|
* ```tsx
|
|
195
|
+
* // Default usage - renders two date inputs
|
|
196
|
+
* <TimeSlotList.DateRange.Input className="flex gap-2" />
|
|
197
|
+
*
|
|
198
|
+
* // With asChild for custom rendering
|
|
184
199
|
* <TimeSlotList.DateRange.Input asChild>
|
|
185
200
|
* {({ startDate, endDate, onChange }) => (
|
|
186
201
|
* <MyDateRangePicker
|
|
@@ -194,11 +209,14 @@ export const Actions = {
|
|
|
194
209
|
*/
|
|
195
210
|
export const DateRangeInput = React.forwardRef((props, ref) => {
|
|
196
211
|
const { asChild, children, className, ...otherProps } = props;
|
|
197
|
-
return (_jsx(CoreTimeSlotList.DateRange, { children: ({ startDate, endDate, setDateRange }) =>
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
212
|
+
return (_jsx(CoreTimeSlotList.DateRange, { children: ({ startDate, endDate, setDateRange }) => {
|
|
213
|
+
const defaultContent = (_jsxs("div", { ref: ref, className: className, "data-testid": TestIds.timeSlotListDateRangeInput, children: [_jsx("input", { type: "date", value: toISODateString(startDate), onChange: (e) => setDateRange(new Date(e.target.value), endDate) }), _jsx("span", { children: "-" }), _jsx("input", { type: "date", value: toISODateString(endDate), onChange: (e) => setDateRange(startDate, new Date(e.target.value)) })] }));
|
|
214
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.timeSlotListDateRangeInput, customElement: children, customElementProps: {
|
|
215
|
+
startDate,
|
|
216
|
+
endDate,
|
|
217
|
+
onChange: setDateRange,
|
|
218
|
+
}, ...otherProps, children: defaultContent }));
|
|
219
|
+
} }));
|
|
202
220
|
});
|
|
203
221
|
DateRangeInput.displayName = 'TimeSlotList.DateRange.Input';
|
|
204
222
|
/**
|
|
@@ -43,8 +43,9 @@ export async function executeBookAction(params) {
|
|
|
43
43
|
throw new Error('Failed to create booking');
|
|
44
44
|
}
|
|
45
45
|
// Step 2: Create checkout
|
|
46
|
-
// businessLocationId
|
|
47
|
-
const
|
|
46
|
+
// businessLocationId: prefer timeSlot.location, fallback to global location
|
|
47
|
+
const firstSelection = params.serviceSelections[0];
|
|
48
|
+
const businessLocationId = firstSelection?.timeSlot?.location?.id ?? undefined;
|
|
48
49
|
const checkoutRequest = buildCheckoutRequest({
|
|
49
50
|
bookingId,
|
|
50
51
|
contactDetails,
|
|
@@ -46,8 +46,10 @@ export function buildBookingRequest(params) {
|
|
|
46
46
|
if (!item.timeSlot) {
|
|
47
47
|
throw new Error('No time slot selected for service selection');
|
|
48
48
|
}
|
|
49
|
-
// Get resource
|
|
50
|
-
const resource = item.resources?.[0];
|
|
49
|
+
// Get resource: prefer timeSlot.resources, fallback to item.resources
|
|
50
|
+
const resource = item.timeSlot.resources?.[0] ?? item.resources?.[0];
|
|
51
|
+
// Get location: prefer timeSlot.location, fallback to params.location
|
|
52
|
+
const slotLocation = item.timeSlot.location ?? location;
|
|
51
53
|
return {
|
|
52
54
|
booking: {
|
|
53
55
|
// Payment option derived from service payment settings
|
|
@@ -66,8 +68,8 @@ export function buildBookingRequest(params) {
|
|
|
66
68
|
resource: resource
|
|
67
69
|
? { _id: resource._id, name: resource.name }
|
|
68
70
|
: undefined,
|
|
69
|
-
location:
|
|
70
|
-
? { _id:
|
|
71
|
+
location: slotLocation
|
|
72
|
+
? { _id: slotLocation.id, name: slotLocation.name }
|
|
71
73
|
: undefined,
|
|
72
74
|
},
|
|
73
75
|
},
|
|
@@ -19,6 +19,10 @@ export interface ServiceSelectionTimeSlot {
|
|
|
19
19
|
remainingCapacity?: number;
|
|
20
20
|
scheduleId?: string;
|
|
21
21
|
eventInfo?: EventInfo;
|
|
22
|
+
/** Resources available in this time slot (populated when slot is selected) */
|
|
23
|
+
resources?: Resource[];
|
|
24
|
+
/** Location of this time slot (populated when slot is selected) */
|
|
25
|
+
location?: Location;
|
|
22
26
|
}
|
|
23
27
|
/**
|
|
24
28
|
* ServiceSelection contains the service AND its related selections
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { type PaymentServiceConfig, type PaymentServiceInternalConfig, type SlotService } from './payment.def.js';
|
|
7
7
|
export { PaymentServiceDefinition, type PaymentServiceConfig, type PaymentDetails, type LineItemAdditionalInfo, type SlotService, } from './payment.def.js';
|
|
8
|
-
export type { PaymentServiceAPI, PaymentServiceInternalConfig } from './payment.def.js';
|
|
8
|
+
export type { PaymentServiceAPI, PaymentServiceInternalConfig, } from './payment.def.js';
|
|
9
9
|
export declare const PaymentService: import("@wix/services-definitions").ServiceFactory<string & {
|
|
10
10
|
__api: import("./payment.def.js").PaymentServiceAPI;
|
|
11
11
|
__config: {};
|
|
@@ -86,6 +86,14 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
86
86
|
* - Auto-selects staff only when there is exactly one staff option in this slot
|
|
87
87
|
*/
|
|
88
88
|
const selectSlot = (slot) => {
|
|
89
|
+
const staffMembersFromSlot = slot.availableResources[0].resources.map((r) => ({
|
|
90
|
+
_id: r._id,
|
|
91
|
+
name: r.name,
|
|
92
|
+
}));
|
|
93
|
+
const slotLocation = {
|
|
94
|
+
id: slot.location?._id,
|
|
95
|
+
name: slot.location?.name ?? undefined,
|
|
96
|
+
};
|
|
89
97
|
const timeSlot = {
|
|
90
98
|
startDate: slot.localStartDate,
|
|
91
99
|
endDate: slot.localEndDate,
|
|
@@ -93,15 +101,12 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
93
101
|
remainingCapacity: slot.remainingCapacity ?? undefined,
|
|
94
102
|
scheduleId: slot.scheduleId ?? undefined,
|
|
95
103
|
eventInfo: slot.eventInfo ?? undefined,
|
|
104
|
+
resources: staffMembersFromSlot,
|
|
105
|
+
location: slotLocation,
|
|
96
106
|
};
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
});
|
|
101
|
-
const staffMembersFromSlot = slot.availableResources[0].resources.map((r) => ({
|
|
102
|
-
id: r._id,
|
|
103
|
-
name: r.name,
|
|
104
|
-
}));
|
|
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
|
|
105
110
|
const resources = staffMembersFromSlot.length === 1 ? staffMembersFromSlot : [];
|
|
106
111
|
bookingService.actions.updateItem({
|
|
107
112
|
timeSlot,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-bookings",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
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": "b5f66fe6afde6dde21cff958f2df9ecaa8bb6890eed0ee093057887e"
|
|
76
76
|
}
|