@wix/headless-bookings 0.0.21 → 0.0.22
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/fetch-availability/index.d.ts +4 -3
- package/cjs/dist/api/fetch-availability/index.js +31 -14
- package/cjs/dist/react/booking/Booking.d.ts +2 -1
- package/cjs/dist/react/booking/Booking.js +1 -1
- package/cjs/dist/react/core/booking/Booking.d.ts +3 -2
- package/cjs/dist/react/core/booking/Booking.js +5 -2
- package/cjs/dist/react/core/time-slot-list/TimeSlot.d.ts +4 -4
- package/cjs/dist/react/core/time-slot-list/TimeSlot.js +7 -5
- package/cjs/dist/react/core/time-slot-list/TimeSlotList.js +3 -2
- package/cjs/dist/react/time-slot-list/TimeSlot.d.ts +6 -6
- package/cjs/dist/react/time-slot-list/TimeSlot.js +6 -6
- package/cjs/dist/react/time-slot-list/TimeSlotList.d.ts +4 -4
- package/cjs/dist/services/booking/booking.d.ts +5 -0
- package/cjs/dist/services/booking/booking.js +17 -1
- package/cjs/dist/services/time-slot-list/time-slot-list.def.d.ts +12 -4
- package/cjs/dist/services/time-slot-list/time-slot-list.js +21 -30
- package/dist/api/fetch-availability/index.d.ts +4 -3
- package/dist/api/fetch-availability/index.js +31 -14
- package/dist/react/booking/Booking.d.ts +2 -1
- package/dist/react/booking/Booking.js +1 -1
- package/dist/react/core/booking/Booking.d.ts +3 -2
- package/dist/react/core/booking/Booking.js +5 -2
- package/dist/react/core/time-slot-list/TimeSlot.d.ts +4 -4
- package/dist/react/core/time-slot-list/TimeSlot.js +7 -5
- package/dist/react/core/time-slot-list/TimeSlotList.js +3 -2
- package/dist/react/time-slot-list/TimeSlot.d.ts +6 -6
- package/dist/react/time-slot-list/TimeSlot.js +6 -6
- package/dist/react/time-slot-list/TimeSlotList.d.ts +4 -4
- package/dist/services/booking/booking.d.ts +5 -0
- package/dist/services/booking/booking.js +17 -1
- package/dist/services/time-slot-list/time-slot-list.def.d.ts +12 -4
- package/dist/services/time-slot-list/time-slot-list.js +21 -30
- package/package.json +2 -2
|
@@ -52,10 +52,10 @@ export interface FetchAvailabilityResponse {
|
|
|
52
52
|
* // response.timeSlots are grouped by time
|
|
53
53
|
* ```
|
|
54
54
|
*/
|
|
55
|
-
export declare function fetchAvailability({ services: selectedServices, viewMode, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, }: {
|
|
55
|
+
export declare function fetchAvailability({ services: selectedServices, viewMode, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, timeSlotsPerDay, }: {
|
|
56
56
|
services: Service[];
|
|
57
|
-
fromLocalDate:
|
|
58
|
-
toLocalDate:
|
|
57
|
+
fromLocalDate: Date;
|
|
58
|
+
toLocalDate: Date;
|
|
59
59
|
timeZone?: string;
|
|
60
60
|
locations?: Location[];
|
|
61
61
|
staffMemberIds?: string[];
|
|
@@ -68,4 +68,5 @@ export declare function fetchAvailability({ services: selectedServices, viewMode
|
|
|
68
68
|
bookOnlineDisabled?: boolean;
|
|
69
69
|
};
|
|
70
70
|
slotsPerPage?: number;
|
|
71
|
+
timeSlotsPerDay?: number;
|
|
71
72
|
}): Promise<FetchAvailabilityResponse>;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { listAvailabilityTimeSlots, } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
6
6
|
import { listEventTimeSlots } from '@wix/auto_sdk_bookings_event-time-slots';
|
|
7
7
|
import { ServiceType } from '@wix/auto_sdk_bookings_services';
|
|
8
|
+
import { format } from 'date-fns';
|
|
8
9
|
import { ANY_STAFF_MEMBER_ID } from '../../services/booking/booking.js';
|
|
9
10
|
import { ViewMode, } from '../../services/time-slot-list/time-slot-list.def.js';
|
|
10
11
|
// ============================================================================
|
|
@@ -18,6 +19,16 @@ var FlowType;
|
|
|
18
19
|
})(FlowType || (FlowType = {}));
|
|
19
20
|
const STAFF_RESOURCE_TYPE_ID = '1cd44cf8-756f-41c3-bd90-3e2ffcaf1155';
|
|
20
21
|
// ============================================================================
|
|
22
|
+
// Helpers
|
|
23
|
+
// ============================================================================
|
|
24
|
+
/**
|
|
25
|
+
* Formats a Date to local ISO string (YYYY-MM-DDThh:mm:ss)
|
|
26
|
+
*/
|
|
27
|
+
function toLocalDateString(date) {
|
|
28
|
+
const pattern = "yyyy-MM-dd'T'HH:mm:ss";
|
|
29
|
+
return format(date, pattern);
|
|
30
|
+
}
|
|
31
|
+
// ============================================================================
|
|
21
32
|
// Grouping Functions
|
|
22
33
|
// ============================================================================
|
|
23
34
|
/**
|
|
@@ -104,9 +115,19 @@ function detectFlow(bookingServices, viewMode) {
|
|
|
104
115
|
* // response.timeSlots are grouped by time
|
|
105
116
|
* ```
|
|
106
117
|
*/
|
|
107
|
-
export async function fetchAvailability({ services: selectedServices, viewMode = ViewMode.CLASSIC, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, }) {
|
|
118
|
+
export async function fetchAvailability({ services: selectedServices, viewMode = ViewMode.CLASSIC, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, timeSlotsPerDay, }) {
|
|
108
119
|
const flow = detectFlow(selectedServices, viewMode);
|
|
109
120
|
const serviceIds = selectedServices.map((s) => s._id);
|
|
121
|
+
// Convert Date objects to local ISO strings
|
|
122
|
+
const fromLocalDateStr = toLocalDateString(fromLocalDate);
|
|
123
|
+
const toLocalDateStr = toLocalDateString(toLocalDate);
|
|
124
|
+
const isFetchAvailabileDays = timeSlotsPerDay === 1;
|
|
125
|
+
const cursorPaging = isFetchAvailabileDays
|
|
126
|
+
? undefined
|
|
127
|
+
: {
|
|
128
|
+
cursor,
|
|
129
|
+
limit: slotsPerPage,
|
|
130
|
+
};
|
|
110
131
|
if (flow === FlowType.SINGLE_SERVICE) {
|
|
111
132
|
const isAnyStaffMemberSelected = staffMemberIds?.length === 1 && staffMemberIds[0] === ANY_STAFF_MEMBER_ID;
|
|
112
133
|
const resourceTypes = staffMemberIds?.length && !isAnyStaffMemberSelected
|
|
@@ -119,20 +140,18 @@ export async function fetchAvailability({ services: selectedServices, viewMode =
|
|
|
119
140
|
: undefined;
|
|
120
141
|
const response = await listAvailabilityTimeSlots({
|
|
121
142
|
serviceId: serviceIds[0],
|
|
122
|
-
fromLocalDate,
|
|
123
|
-
toLocalDate,
|
|
143
|
+
fromLocalDate: fromLocalDateStr,
|
|
144
|
+
toLocalDate: toLocalDateStr,
|
|
124
145
|
timeZone,
|
|
125
146
|
locations,
|
|
126
|
-
cursorPaging
|
|
127
|
-
cursor,
|
|
128
|
-
limit: slotsPerPage,
|
|
129
|
-
},
|
|
147
|
+
cursorPaging,
|
|
130
148
|
resourceTypes,
|
|
131
149
|
bookable,
|
|
132
150
|
bookingPolicyViolations,
|
|
133
|
-
includeResourceTypeIds: isAnyStaffMemberSelected
|
|
151
|
+
includeResourceTypeIds: isAnyStaffMemberSelected || isFetchAvailabileDays
|
|
134
152
|
? undefined
|
|
135
153
|
: [STAFF_RESOURCE_TYPE_ID],
|
|
154
|
+
timeSlotsPerDay,
|
|
136
155
|
});
|
|
137
156
|
return {
|
|
138
157
|
timeSlots: groupTimeSlotsByTime(response.timeSlots || []),
|
|
@@ -154,17 +173,15 @@ export async function fetchAvailability({ services: selectedServices, viewMode =
|
|
|
154
173
|
eventFilter['resources.id'] = { $hasSome: staffMemberIds };
|
|
155
174
|
}
|
|
156
175
|
const response = await listEventTimeSlots({
|
|
157
|
-
fromLocalDate,
|
|
158
|
-
toLocalDate,
|
|
176
|
+
fromLocalDate: fromLocalDateStr,
|
|
177
|
+
toLocalDate: toLocalDateStr,
|
|
159
178
|
timeZone,
|
|
160
179
|
serviceIds: serviceIds.length ? serviceIds : undefined,
|
|
161
|
-
cursorPaging
|
|
162
|
-
cursor,
|
|
163
|
-
limit: slotsPerPage,
|
|
164
|
-
},
|
|
180
|
+
cursorPaging,
|
|
165
181
|
eventFilter: Object.keys(eventFilter).length ? eventFilter : undefined,
|
|
166
182
|
includeNonBookable: bookable ? !bookable : undefined,
|
|
167
183
|
bookingPolicyViolations,
|
|
184
|
+
maxSlotsPerDay: timeSlotsPerDay,
|
|
168
185
|
});
|
|
169
186
|
return {
|
|
170
187
|
timeSlots: groupTimeSlotsByTime(response.timeSlots || []),
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import type { BookingServiceConfig } from '../../services/booking/booking.js';
|
|
7
|
+
export type { BookingData, DataProps } from '../core/booking/Booking.js';
|
|
7
8
|
/**
|
|
8
9
|
* Props for Booking.Root component
|
|
9
10
|
*/
|
|
10
11
|
export interface BookingRootProps {
|
|
11
12
|
children: React.ReactNode;
|
|
12
|
-
bookingServiceConfig
|
|
13
|
+
bookingServiceConfig?: BookingServiceConfig;
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Root component that provides booking context to the entire app.
|
|
@@ -19,7 +19,7 @@ import * as CoreBooking from '../core/booking/Booking.js';
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
export const Root = (props) => {
|
|
22
|
-
const { children, bookingServiceConfig } = props;
|
|
22
|
+
const { children, bookingServiceConfig = {} } = props;
|
|
23
23
|
return (_jsx(CoreBooking.Root, { bookingServiceConfig: bookingServiceConfig, children: children }));
|
|
24
24
|
};
|
|
25
25
|
Root.displayName = 'Booking.Root';
|
|
@@ -10,7 +10,7 @@ import type { FormValues } from '@wix/form-public';
|
|
|
10
10
|
*/
|
|
11
11
|
export interface RootProps {
|
|
12
12
|
children: React.ReactNode;
|
|
13
|
-
bookingServiceConfig
|
|
13
|
+
bookingServiceConfig?: BookingServiceConfig;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Root component that provides booking context to the entire app.
|
|
@@ -35,6 +35,7 @@ export declare function Root(props: RootProps): React.ReactNode;
|
|
|
35
35
|
export interface BookingData {
|
|
36
36
|
serviceSelections: ServiceSelection[];
|
|
37
37
|
location: Location | null;
|
|
38
|
+
timezone: string | undefined;
|
|
38
39
|
formSubmission: FormValues | null;
|
|
39
40
|
actions: BookingServiceActions;
|
|
40
41
|
}
|
|
@@ -52,7 +53,7 @@ export interface DataProps {
|
|
|
52
53
|
* @example
|
|
53
54
|
* ```tsx
|
|
54
55
|
* <Booking.Data>
|
|
55
|
-
* {({ serviceSelections, location, formSubmission, actions }) => (
|
|
56
|
+
* {({ serviceSelections, location, timezone, formSubmission, actions }) => (
|
|
56
57
|
* serviceSelections.length > 0 ? (
|
|
57
58
|
* <div>{serviceSelections.map(item => item.service.name).join(', ')}</div>
|
|
58
59
|
* ) : (
|
|
@@ -18,7 +18,8 @@ import { BookingServiceDefinition, BookingService, } from '../../../services/boo
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
export function Root(props) {
|
|
21
|
-
|
|
21
|
+
const { bookingServiceConfig = {}, children } = props;
|
|
22
|
+
return (_jsx(WixServices, { servicesMap: createServicesMap().addService(BookingServiceDefinition, BookingService, bookingServiceConfig), children: children }));
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Core component that provides access to booking data via render props.
|
|
@@ -28,7 +29,7 @@ export function Root(props) {
|
|
|
28
29
|
* @example
|
|
29
30
|
* ```tsx
|
|
30
31
|
* <Booking.Data>
|
|
31
|
-
* {({ serviceSelections, location, formSubmission, actions }) => (
|
|
32
|
+
* {({ serviceSelections, location, timezone, formSubmission, actions }) => (
|
|
32
33
|
* serviceSelections.length > 0 ? (
|
|
33
34
|
* <div>{serviceSelections.map(item => item.service.name).join(', ')}</div>
|
|
34
35
|
* ) : (
|
|
@@ -43,11 +44,13 @@ export function Data(props) {
|
|
|
43
44
|
const bookingService = useService(BookingServiceDefinition);
|
|
44
45
|
const serviceSelections = bookingService.serviceSelections.get();
|
|
45
46
|
const location = bookingService.location.get();
|
|
47
|
+
const timezone = bookingService.timezone.get();
|
|
46
48
|
const formSubmission = bookingService.formSubmission.get();
|
|
47
49
|
const { actions } = bookingService;
|
|
48
50
|
return children({
|
|
49
51
|
serviceSelections,
|
|
50
52
|
location,
|
|
53
|
+
timezone,
|
|
51
54
|
formSubmission,
|
|
52
55
|
actions,
|
|
53
56
|
});
|
|
@@ -88,10 +88,10 @@ export declare function StaffMemberProvider(props: StaffMemberProviderProps): Re
|
|
|
88
88
|
*/
|
|
89
89
|
export interface InfoProps {
|
|
90
90
|
children: (data: {
|
|
91
|
-
/**
|
|
92
|
-
startDate:
|
|
93
|
-
/**
|
|
94
|
-
endDate:
|
|
91
|
+
/** Parsed localStartDate as Date (constructed from the raw localStartDate string) */
|
|
92
|
+
startDate: Date | undefined;
|
|
93
|
+
/** Parsed localEndDate as Date (constructed from the raw localEndDate string) */
|
|
94
|
+
endDate: Date | undefined;
|
|
95
95
|
/** Calculated duration in minutes (null if dates unavailable) */
|
|
96
96
|
durationInMinutes: number | null;
|
|
97
97
|
/** Whether the timeSlot is bookable */
|
|
@@ -116,15 +116,17 @@ export function StaffMemberProvider(props) {
|
|
|
116
116
|
*/
|
|
117
117
|
export function Info(props) {
|
|
118
118
|
const { timeSlot, isSelected } = useTimeSlotContext();
|
|
119
|
-
const startDate = timeSlot.localStartDate
|
|
120
|
-
|
|
119
|
+
const startDate = timeSlot.localStartDate
|
|
120
|
+
? new Date(timeSlot.localStartDate)
|
|
121
|
+
: undefined;
|
|
122
|
+
const endDate = timeSlot.localEndDate
|
|
123
|
+
? new Date(timeSlot.localEndDate)
|
|
124
|
+
: undefined;
|
|
121
125
|
const bookable = timeSlot.bookable ?? false;
|
|
122
126
|
// Calculate duration from start and end dates
|
|
123
127
|
let durationInMinutes = null;
|
|
124
128
|
if (startDate && endDate) {
|
|
125
|
-
|
|
126
|
-
const endTime = new Date(endDate).getTime();
|
|
127
|
-
durationInMinutes = Math.round((endTime - startTime) / (1000 * 60));
|
|
129
|
+
durationInMinutes = Math.round((endDate.getTime() - startDate.getTime()) / (1000 * 60));
|
|
128
130
|
}
|
|
129
131
|
return props.children({
|
|
130
132
|
startDate,
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useService, WixServices } from '@wix/services-manager-react';
|
|
3
3
|
import { createServicesMap } from '@wix/services-manager';
|
|
4
4
|
import { TimeSlotListServiceDefinition, TimeSlotListService, } from '../../../services/time-slot-list/index.js';
|
|
5
|
+
import { BookingServiceDefinition } from '../../../services/booking/booking.js';
|
|
5
6
|
/**
|
|
6
7
|
* Root component that provides TimeSlotListService context to its children.
|
|
7
8
|
*
|
|
@@ -54,8 +55,8 @@ function getTimezoneDisplayName(timezone) {
|
|
|
54
55
|
* ```
|
|
55
56
|
*/
|
|
56
57
|
export function Timezone(props) {
|
|
57
|
-
const
|
|
58
|
-
const timezone =
|
|
58
|
+
const bookingService = useService(BookingServiceDefinition);
|
|
59
|
+
const timezone = bookingService.timezone.get();
|
|
59
60
|
const displayName = getTimezoneDisplayName(timezone);
|
|
60
61
|
return props.children({ timezone, displayName });
|
|
61
62
|
}
|
|
@@ -16,18 +16,18 @@ export type { LocationData, StaffMemberData, } from '../core/time-slot-list/Time
|
|
|
16
16
|
export interface StartDateProps {
|
|
17
17
|
asChild?: boolean;
|
|
18
18
|
children?: AsChildChildren<{
|
|
19
|
-
startDate:
|
|
19
|
+
startDate: Date | undefined;
|
|
20
20
|
}>;
|
|
21
21
|
className?: string;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* Displays the time slot start date/time as a
|
|
24
|
+
* Displays the time slot start date/time as a Date.
|
|
25
25
|
* Consumer is responsible for formatting.
|
|
26
26
|
*
|
|
27
27
|
* @component
|
|
28
28
|
* @example
|
|
29
29
|
* ```tsx
|
|
30
|
-
* // Default usage - displays
|
|
30
|
+
* // Default usage - displays Date.toString()
|
|
31
31
|
* <TimeSlot.StartDate className="text-foreground" />
|
|
32
32
|
*
|
|
33
33
|
* // With custom formatting
|
|
@@ -45,18 +45,18 @@ export declare const StartDate: React.ForwardRefExoticComponent<StartDateProps &
|
|
|
45
45
|
export interface EndDateProps {
|
|
46
46
|
asChild?: boolean;
|
|
47
47
|
children?: AsChildChildren<{
|
|
48
|
-
endDate:
|
|
48
|
+
endDate: Date | undefined;
|
|
49
49
|
}>;
|
|
50
50
|
className?: string;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
* Displays the time slot end date/time as a
|
|
53
|
+
* Displays the time slot end date/time as a Date.
|
|
54
54
|
* Consumer is responsible for formatting.
|
|
55
55
|
*
|
|
56
56
|
* @component
|
|
57
57
|
* @example
|
|
58
58
|
* ```tsx
|
|
59
|
-
* // Default usage - displays
|
|
59
|
+
* // Default usage - displays Date.toString()
|
|
60
60
|
* <TimeSlot.EndDate className="text-foreground" />
|
|
61
61
|
*
|
|
62
62
|
* // With custom formatting
|
|
@@ -34,13 +34,13 @@ var TestIds;
|
|
|
34
34
|
TestIds["timeSlotStaffMemberActionSelect"] = "time-slot-staff-member-action-select";
|
|
35
35
|
})(TestIds || (TestIds = {}));
|
|
36
36
|
/**
|
|
37
|
-
* Displays the time slot start date/time as a
|
|
37
|
+
* Displays the time slot start date/time as a Date.
|
|
38
38
|
* Consumer is responsible for formatting.
|
|
39
39
|
*
|
|
40
40
|
* @component
|
|
41
41
|
* @example
|
|
42
42
|
* ```tsx
|
|
43
|
-
* // Default usage - displays
|
|
43
|
+
* // Default usage - displays Date.toString()
|
|
44
44
|
* <TimeSlot.StartDate className="text-foreground" />
|
|
45
45
|
*
|
|
46
46
|
* // With custom formatting
|
|
@@ -53,17 +53,17 @@ var TestIds;
|
|
|
53
53
|
*/
|
|
54
54
|
export const StartDate = React.forwardRef((props, ref) => {
|
|
55
55
|
const { asChild, children, className } = props;
|
|
56
|
-
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 }) })) }));
|
|
56
|
+
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?.toString() }) })) }));
|
|
57
57
|
});
|
|
58
58
|
StartDate.displayName = 'TimeSlot.StartDate';
|
|
59
59
|
/**
|
|
60
|
-
* Displays the time slot end date/time as a
|
|
60
|
+
* Displays the time slot end date/time as a Date.
|
|
61
61
|
* Consumer is responsible for formatting.
|
|
62
62
|
*
|
|
63
63
|
* @component
|
|
64
64
|
* @example
|
|
65
65
|
* ```tsx
|
|
66
|
-
* // Default usage - displays
|
|
66
|
+
* // Default usage - displays Date.toString()
|
|
67
67
|
* <TimeSlot.EndDate className="text-foreground" />
|
|
68
68
|
*
|
|
69
69
|
* // With custom formatting
|
|
@@ -76,7 +76,7 @@ StartDate.displayName = 'TimeSlot.StartDate';
|
|
|
76
76
|
*/
|
|
77
77
|
export const EndDate = React.forwardRef((props, ref) => {
|
|
78
78
|
const { asChild, children, className } = props;
|
|
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 }) })) }));
|
|
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?.toString() }) })) }));
|
|
80
80
|
});
|
|
81
81
|
EndDate.displayName = 'TimeSlot.EndDate';
|
|
82
82
|
/**
|
|
@@ -113,10 +113,10 @@ export interface TimeSlotRepeaterChildProps {
|
|
|
113
113
|
isSelected: boolean;
|
|
114
114
|
/** Whether the slot can be booked */
|
|
115
115
|
bookable: boolean;
|
|
116
|
-
/** Start date/time as
|
|
117
|
-
startDate:
|
|
118
|
-
/** End date/time as
|
|
119
|
-
endDate:
|
|
116
|
+
/** Start date/time as Date (constructed from the raw localStartDate string) */
|
|
117
|
+
startDate: Date | undefined;
|
|
118
|
+
/** End date/time as Date (constructed from the raw localEndDate string) */
|
|
119
|
+
endDate: Date | undefined;
|
|
120
120
|
/** Duration in minutes */
|
|
121
121
|
durationInMinutes: number | null;
|
|
122
122
|
/** The raw time slot data */
|
|
@@ -58,6 +58,7 @@ export interface BookingServiceActions {
|
|
|
58
58
|
getResourceById(resourceId: string, instanceId?: string): Resource | undefined;
|
|
59
59
|
setLocation(location: Location | null): void;
|
|
60
60
|
clearLocation(): void;
|
|
61
|
+
setTimezone(timezone: string): void;
|
|
61
62
|
setFormSubmission(values: FormValues | null): void;
|
|
62
63
|
clearFormSubmission(): void;
|
|
63
64
|
clearAll(): void;
|
|
@@ -71,6 +72,8 @@ export interface BookingService {
|
|
|
71
72
|
serviceSelections: Signal<ServiceSelection[]>;
|
|
72
73
|
/** Signal containing the selected location */
|
|
73
74
|
location: Signal<Location | null>;
|
|
75
|
+
/** Signal containing the selected timezone */
|
|
76
|
+
timezone: Signal<string | undefined>;
|
|
74
77
|
/** Signal containing the form submission */
|
|
75
78
|
formSubmission: Signal<FormValues | null>;
|
|
76
79
|
/** Actions for modifying booking state */
|
|
@@ -93,6 +96,8 @@ export interface BookingServiceConfig {
|
|
|
93
96
|
serviceSelections?: ServiceSelection[];
|
|
94
97
|
/** Optional initial location */
|
|
95
98
|
location?: Location | null;
|
|
99
|
+
/** Optional initial timezone */
|
|
100
|
+
timezone?: string;
|
|
96
101
|
/** Optional initial form submission */
|
|
97
102
|
formSubmission?: FormValues | null;
|
|
98
103
|
}
|
|
@@ -10,7 +10,7 @@ import { SignalsServiceDefinition, } from '@wix/services-definitions/core-servic
|
|
|
10
10
|
// ============================================================================
|
|
11
11
|
export const ANY_STAFF_MEMBER_ID = '__any_staff_member__';
|
|
12
12
|
// ============================================================================
|
|
13
|
-
//
|
|
13
|
+
// Helpers
|
|
14
14
|
// ============================================================================
|
|
15
15
|
/**
|
|
16
16
|
* Generates a unique instance ID for service selections
|
|
@@ -18,6 +18,14 @@ export const ANY_STAFF_MEMBER_ID = '__any_staff_member__';
|
|
|
18
18
|
function generateInstanceId() {
|
|
19
19
|
return crypto.randomUUID();
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Gets the client's timezone from Intl API
|
|
23
|
+
*/
|
|
24
|
+
function getClientTimezone() {
|
|
25
|
+
return typeof Intl !== 'undefined'
|
|
26
|
+
? Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
27
|
+
: undefined;
|
|
28
|
+
}
|
|
21
29
|
/**
|
|
22
30
|
* Service definition for Booking
|
|
23
31
|
*/
|
|
@@ -59,6 +67,7 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
59
67
|
}));
|
|
60
68
|
const serviceSelectionsSignal = signalsService.signal(initialItemsWithIds);
|
|
61
69
|
const locationSignal = signalsService.signal(config.location || null);
|
|
70
|
+
const timezoneSignal = signalsService.signal(config.timezone ?? getClientTimezone());
|
|
62
71
|
const formSubmissionSignal = signalsService.signal(config.formSubmission || null);
|
|
63
72
|
// ========================================================================
|
|
64
73
|
// Actions Implementation
|
|
@@ -146,6 +155,12 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
146
155
|
clearLocation() {
|
|
147
156
|
locationSignal.set(null);
|
|
148
157
|
},
|
|
158
|
+
// Timezone actions
|
|
159
|
+
setTimezone(timezone) {
|
|
160
|
+
if (timezoneSignal.get() !== timezone) {
|
|
161
|
+
timezoneSignal.set(timezone);
|
|
162
|
+
}
|
|
163
|
+
},
|
|
149
164
|
// Form Submission actions
|
|
150
165
|
setFormSubmission(values) {
|
|
151
166
|
formSubmissionSignal.set(values);
|
|
@@ -163,6 +178,7 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
163
178
|
return {
|
|
164
179
|
serviceSelections: serviceSelectionsSignal,
|
|
165
180
|
location: locationSignal,
|
|
181
|
+
timezone: timezoneSignal,
|
|
166
182
|
formSubmission: formSubmissionSignal,
|
|
167
183
|
actions,
|
|
168
184
|
};
|
|
@@ -65,11 +65,21 @@ export interface TimeSlotsFilters {
|
|
|
65
65
|
* Default: 1000. Max: 1000.
|
|
66
66
|
* @example
|
|
67
67
|
* ```ts
|
|
68
|
-
* // Load
|
|
69
|
-
* filters: { slotsPerPage:
|
|
68
|
+
* // Load 10 slots at a time
|
|
69
|
+
* filters: { slotsPerPage: 10 }
|
|
70
70
|
* ```
|
|
71
71
|
*/
|
|
72
72
|
slotsPerPage?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Maximum number of time slots to return per day.
|
|
75
|
+
* Useful for fetching availability indicators (set to 1 to get one slot per day).
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* // Get one slot per day for calendar indicators
|
|
79
|
+
* filters: { timeSlotsPerDay: 1 }
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
timeSlotsPerDay?: number;
|
|
73
83
|
}
|
|
74
84
|
export interface TimeSlotListServiceConfig {
|
|
75
85
|
/** Services to fetch time slots for */
|
|
@@ -92,7 +102,6 @@ export interface TimeSlotListServiceActions {
|
|
|
92
102
|
clearPreferences: () => void;
|
|
93
103
|
loadMore: () => Promise<void>;
|
|
94
104
|
setDateRange: (start: Date, end: Date) => void;
|
|
95
|
-
setTimezone: (tz: string) => void;
|
|
96
105
|
}
|
|
97
106
|
export interface TimeSlotListServiceAPI {
|
|
98
107
|
timeSlots: Signal<TimeSlot[]>;
|
|
@@ -102,7 +111,6 @@ export interface TimeSlotListServiceAPI {
|
|
|
102
111
|
hasMore: Signal<boolean>;
|
|
103
112
|
startDate: Signal<Date>;
|
|
104
113
|
endDate: Signal<Date>;
|
|
105
|
-
timezone: Signal<string | undefined>;
|
|
106
114
|
actions: TimeSlotListServiceActions;
|
|
107
115
|
}
|
|
108
116
|
export declare const TimeSlotListServiceDefinition: string & {
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { implementService } from '@wix/services-definitions';
|
|
6
6
|
import { SignalsServiceDefinition } from '@wix/services-definitions/core-services/signals';
|
|
7
|
-
import { format } from 'date-fns';
|
|
8
7
|
import { TimeSlotListServiceDefinition, ViewMode, } from './time-slot-list.def.js';
|
|
9
8
|
import { fetchAvailability } from '../../api/fetch-availability/index.js';
|
|
10
9
|
import { BookingServiceDefinition, ANY_STAFF_MEMBER_ID, } from '../booking/booking.js';
|
|
@@ -19,13 +18,6 @@ function getClientTimezone() {
|
|
|
19
18
|
? Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
20
19
|
: undefined;
|
|
21
20
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Formats a Date to local ISO string (YYYY-MM-DDThh:mm:ss)
|
|
24
|
-
*/
|
|
25
|
-
function toLocalDateString(date) {
|
|
26
|
-
const pattern = "yyyy-MM-dd'T'HH:mm:ss";
|
|
27
|
-
return format(date, pattern);
|
|
28
|
-
}
|
|
29
21
|
/**
|
|
30
22
|
* Merges new grouped slots with existing ones
|
|
31
23
|
* Slots with the same start time are merged
|
|
@@ -68,7 +60,6 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
68
60
|
const viewModeSignal = signalsService.signal(ViewMode.CLASSIC);
|
|
69
61
|
const startDateSignal = signalsService.signal(config.startDate || currentDate);
|
|
70
62
|
const endDateSignal = signalsService.signal(config.endDate || getDefaultEndDate());
|
|
71
|
-
const timezoneSignal = signalsService.signal(config.timezone ?? getClientTimezone());
|
|
72
63
|
const cursorSignal = signalsService.signal(undefined);
|
|
73
64
|
/**
|
|
74
65
|
* Helper: Get services from config or BookingService
|
|
@@ -86,7 +77,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
86
77
|
const location = bookingService.location.get();
|
|
87
78
|
const serviceSelections = bookingService.serviceSelections.get();
|
|
88
79
|
const selectedStaffMemberIds = serviceSelections
|
|
89
|
-
.map((
|
|
80
|
+
.map((serviceSelection) => serviceSelection.resources.map((r) => r._id))
|
|
90
81
|
.flat();
|
|
91
82
|
const selectedLocation = location ? { _id: location.id } : null;
|
|
92
83
|
// Merge config filters with BookingService selections (selections win)
|
|
@@ -114,9 +105,9 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
114
105
|
try {
|
|
115
106
|
const response = await fetchAvailability({
|
|
116
107
|
services: selectedServices,
|
|
117
|
-
fromLocalDate:
|
|
118
|
-
toLocalDate:
|
|
119
|
-
timeZone:
|
|
108
|
+
fromLocalDate: startDateSignal.get(),
|
|
109
|
+
toLocalDate: endDateSignal.get(),
|
|
110
|
+
timeZone: bookingService.timezone.get(),
|
|
120
111
|
viewMode: viewModeSignal.get(),
|
|
121
112
|
cursor: cursorSignal.get(),
|
|
122
113
|
...filters,
|
|
@@ -312,16 +303,14 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
312
303
|
if (!cursor || isLoadingSignal.get())
|
|
313
304
|
return;
|
|
314
305
|
const selectedServices = getServices();
|
|
315
|
-
if (selectedServices.length === 0)
|
|
316
|
-
return;
|
|
317
306
|
const filters = getFilters();
|
|
318
307
|
isLoadingSignal.set(true);
|
|
319
308
|
try {
|
|
320
309
|
const response = await fetchAvailability({
|
|
321
310
|
services: selectedServices,
|
|
322
|
-
fromLocalDate:
|
|
323
|
-
toLocalDate:
|
|
324
|
-
timeZone:
|
|
311
|
+
fromLocalDate: startDateSignal.get(),
|
|
312
|
+
toLocalDate: endDateSignal.get(),
|
|
313
|
+
timeZone: bookingService.timezone.get(),
|
|
325
314
|
cursor,
|
|
326
315
|
viewMode: viewModeSignal.get(),
|
|
327
316
|
...filters,
|
|
@@ -357,17 +346,21 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
357
346
|
endDateSignal.set(newEndDate);
|
|
358
347
|
fetchTimeSlots();
|
|
359
348
|
};
|
|
360
|
-
/**
|
|
361
|
-
* Action: Update timezone and refetch slots
|
|
362
|
-
*/
|
|
363
|
-
const setTimezone = (newTimezone) => {
|
|
364
|
-
timezoneSignal.set(newTimezone);
|
|
365
|
-
fetchTimeSlots();
|
|
366
|
-
};
|
|
367
349
|
// Auto-fetch on initialization if no pre-loaded data provided
|
|
368
|
-
if (!config.timeSlots
|
|
350
|
+
if (!config.timeSlots) {
|
|
369
351
|
fetchTimeSlots();
|
|
370
352
|
}
|
|
353
|
+
// Subscribe to timezone changes from BookingService to auto-refetch
|
|
354
|
+
// NOTE: This should depend ONLY on bookingService.timezone.
|
|
355
|
+
let firstTimezoneEffectRun = true;
|
|
356
|
+
signalsService.effect(() => {
|
|
357
|
+
bookingService.timezone.get();
|
|
358
|
+
if (firstTimezoneEffectRun) {
|
|
359
|
+
firstTimezoneEffectRun = false;
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
fetchTimeSlots();
|
|
363
|
+
});
|
|
371
364
|
// Return service API
|
|
372
365
|
return {
|
|
373
366
|
// State Signals
|
|
@@ -379,7 +372,6 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
379
372
|
// Configuration Signals (viewMode is internal only)
|
|
380
373
|
startDate: startDateSignal,
|
|
381
374
|
endDate: endDateSignal,
|
|
382
|
-
timezone: timezoneSignal,
|
|
383
375
|
// Actions
|
|
384
376
|
actions: {
|
|
385
377
|
selectSlot,
|
|
@@ -388,7 +380,6 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
388
380
|
clearPreferences,
|
|
389
381
|
loadMore,
|
|
390
382
|
setDateRange,
|
|
391
|
-
setTimezone,
|
|
392
383
|
},
|
|
393
384
|
};
|
|
394
385
|
});
|
|
@@ -420,8 +411,8 @@ export async function loadTimeSlotListConfig(options) {
|
|
|
420
411
|
try {
|
|
421
412
|
const response = await fetchAvailability({
|
|
422
413
|
services: options.services || [],
|
|
423
|
-
fromLocalDate:
|
|
424
|
-
toLocalDate:
|
|
414
|
+
fromLocalDate: startDate,
|
|
415
|
+
toLocalDate: endDate,
|
|
425
416
|
timeZone: options.timezone ?? getClientTimezone(),
|
|
426
417
|
...options.filters,
|
|
427
418
|
});
|
|
@@ -52,10 +52,10 @@ export interface FetchAvailabilityResponse {
|
|
|
52
52
|
* // response.timeSlots are grouped by time
|
|
53
53
|
* ```
|
|
54
54
|
*/
|
|
55
|
-
export declare function fetchAvailability({ services: selectedServices, viewMode, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, }: {
|
|
55
|
+
export declare function fetchAvailability({ services: selectedServices, viewMode, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, timeSlotsPerDay, }: {
|
|
56
56
|
services: Service[];
|
|
57
|
-
fromLocalDate:
|
|
58
|
-
toLocalDate:
|
|
57
|
+
fromLocalDate: Date;
|
|
58
|
+
toLocalDate: Date;
|
|
59
59
|
timeZone?: string;
|
|
60
60
|
locations?: Location[];
|
|
61
61
|
staffMemberIds?: string[];
|
|
@@ -68,4 +68,5 @@ export declare function fetchAvailability({ services: selectedServices, viewMode
|
|
|
68
68
|
bookOnlineDisabled?: boolean;
|
|
69
69
|
};
|
|
70
70
|
slotsPerPage?: number;
|
|
71
|
+
timeSlotsPerDay?: number;
|
|
71
72
|
}): Promise<FetchAvailabilityResponse>;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { listAvailabilityTimeSlots, } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
6
6
|
import { listEventTimeSlots } from '@wix/auto_sdk_bookings_event-time-slots';
|
|
7
7
|
import { ServiceType } from '@wix/auto_sdk_bookings_services';
|
|
8
|
+
import { format } from 'date-fns';
|
|
8
9
|
import { ANY_STAFF_MEMBER_ID } from '../../services/booking/booking.js';
|
|
9
10
|
import { ViewMode, } from '../../services/time-slot-list/time-slot-list.def.js';
|
|
10
11
|
// ============================================================================
|
|
@@ -18,6 +19,16 @@ var FlowType;
|
|
|
18
19
|
})(FlowType || (FlowType = {}));
|
|
19
20
|
const STAFF_RESOURCE_TYPE_ID = '1cd44cf8-756f-41c3-bd90-3e2ffcaf1155';
|
|
20
21
|
// ============================================================================
|
|
22
|
+
// Helpers
|
|
23
|
+
// ============================================================================
|
|
24
|
+
/**
|
|
25
|
+
* Formats a Date to local ISO string (YYYY-MM-DDThh:mm:ss)
|
|
26
|
+
*/
|
|
27
|
+
function toLocalDateString(date) {
|
|
28
|
+
const pattern = "yyyy-MM-dd'T'HH:mm:ss";
|
|
29
|
+
return format(date, pattern);
|
|
30
|
+
}
|
|
31
|
+
// ============================================================================
|
|
21
32
|
// Grouping Functions
|
|
22
33
|
// ============================================================================
|
|
23
34
|
/**
|
|
@@ -104,9 +115,19 @@ function detectFlow(bookingServices, viewMode) {
|
|
|
104
115
|
* // response.timeSlots are grouped by time
|
|
105
116
|
* ```
|
|
106
117
|
*/
|
|
107
|
-
export async function fetchAvailability({ services: selectedServices, viewMode = ViewMode.CLASSIC, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, }) {
|
|
118
|
+
export async function fetchAvailability({ services: selectedServices, viewMode = ViewMode.CLASSIC, fromLocalDate, toLocalDate, timeZone, locations, staffMemberIds, cursor, slotsPerPage, bookable, bookingPolicyViolations, timeSlotsPerDay, }) {
|
|
108
119
|
const flow = detectFlow(selectedServices, viewMode);
|
|
109
120
|
const serviceIds = selectedServices.map((s) => s._id);
|
|
121
|
+
// Convert Date objects to local ISO strings
|
|
122
|
+
const fromLocalDateStr = toLocalDateString(fromLocalDate);
|
|
123
|
+
const toLocalDateStr = toLocalDateString(toLocalDate);
|
|
124
|
+
const isFetchAvailabileDays = timeSlotsPerDay === 1;
|
|
125
|
+
const cursorPaging = isFetchAvailabileDays
|
|
126
|
+
? undefined
|
|
127
|
+
: {
|
|
128
|
+
cursor,
|
|
129
|
+
limit: slotsPerPage,
|
|
130
|
+
};
|
|
110
131
|
if (flow === FlowType.SINGLE_SERVICE) {
|
|
111
132
|
const isAnyStaffMemberSelected = staffMemberIds?.length === 1 && staffMemberIds[0] === ANY_STAFF_MEMBER_ID;
|
|
112
133
|
const resourceTypes = staffMemberIds?.length && !isAnyStaffMemberSelected
|
|
@@ -119,20 +140,18 @@ export async function fetchAvailability({ services: selectedServices, viewMode =
|
|
|
119
140
|
: undefined;
|
|
120
141
|
const response = await listAvailabilityTimeSlots({
|
|
121
142
|
serviceId: serviceIds[0],
|
|
122
|
-
fromLocalDate,
|
|
123
|
-
toLocalDate,
|
|
143
|
+
fromLocalDate: fromLocalDateStr,
|
|
144
|
+
toLocalDate: toLocalDateStr,
|
|
124
145
|
timeZone,
|
|
125
146
|
locations,
|
|
126
|
-
cursorPaging
|
|
127
|
-
cursor,
|
|
128
|
-
limit: slotsPerPage,
|
|
129
|
-
},
|
|
147
|
+
cursorPaging,
|
|
130
148
|
resourceTypes,
|
|
131
149
|
bookable,
|
|
132
150
|
bookingPolicyViolations,
|
|
133
|
-
includeResourceTypeIds: isAnyStaffMemberSelected
|
|
151
|
+
includeResourceTypeIds: isAnyStaffMemberSelected || isFetchAvailabileDays
|
|
134
152
|
? undefined
|
|
135
153
|
: [STAFF_RESOURCE_TYPE_ID],
|
|
154
|
+
timeSlotsPerDay,
|
|
136
155
|
});
|
|
137
156
|
return {
|
|
138
157
|
timeSlots: groupTimeSlotsByTime(response.timeSlots || []),
|
|
@@ -154,17 +173,15 @@ export async function fetchAvailability({ services: selectedServices, viewMode =
|
|
|
154
173
|
eventFilter['resources.id'] = { $hasSome: staffMemberIds };
|
|
155
174
|
}
|
|
156
175
|
const response = await listEventTimeSlots({
|
|
157
|
-
fromLocalDate,
|
|
158
|
-
toLocalDate,
|
|
176
|
+
fromLocalDate: fromLocalDateStr,
|
|
177
|
+
toLocalDate: toLocalDateStr,
|
|
159
178
|
timeZone,
|
|
160
179
|
serviceIds: serviceIds.length ? serviceIds : undefined,
|
|
161
|
-
cursorPaging
|
|
162
|
-
cursor,
|
|
163
|
-
limit: slotsPerPage,
|
|
164
|
-
},
|
|
180
|
+
cursorPaging,
|
|
165
181
|
eventFilter: Object.keys(eventFilter).length ? eventFilter : undefined,
|
|
166
182
|
includeNonBookable: bookable ? !bookable : undefined,
|
|
167
183
|
bookingPolicyViolations,
|
|
184
|
+
maxSlotsPerDay: timeSlotsPerDay,
|
|
168
185
|
});
|
|
169
186
|
return {
|
|
170
187
|
timeSlots: groupTimeSlotsByTime(response.timeSlots || []),
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import type { BookingServiceConfig } from '../../services/booking/booking.js';
|
|
7
|
+
export type { BookingData, DataProps } from '../core/booking/Booking.js';
|
|
7
8
|
/**
|
|
8
9
|
* Props for Booking.Root component
|
|
9
10
|
*/
|
|
10
11
|
export interface BookingRootProps {
|
|
11
12
|
children: React.ReactNode;
|
|
12
|
-
bookingServiceConfig
|
|
13
|
+
bookingServiceConfig?: BookingServiceConfig;
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Root component that provides booking context to the entire app.
|
|
@@ -19,7 +19,7 @@ import * as CoreBooking from '../core/booking/Booking.js';
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
export const Root = (props) => {
|
|
22
|
-
const { children, bookingServiceConfig } = props;
|
|
22
|
+
const { children, bookingServiceConfig = {} } = props;
|
|
23
23
|
return (_jsx(CoreBooking.Root, { bookingServiceConfig: bookingServiceConfig, children: children }));
|
|
24
24
|
};
|
|
25
25
|
Root.displayName = 'Booking.Root';
|
|
@@ -10,7 +10,7 @@ import type { FormValues } from '@wix/form-public';
|
|
|
10
10
|
*/
|
|
11
11
|
export interface RootProps {
|
|
12
12
|
children: React.ReactNode;
|
|
13
|
-
bookingServiceConfig
|
|
13
|
+
bookingServiceConfig?: BookingServiceConfig;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Root component that provides booking context to the entire app.
|
|
@@ -35,6 +35,7 @@ export declare function Root(props: RootProps): React.ReactNode;
|
|
|
35
35
|
export interface BookingData {
|
|
36
36
|
serviceSelections: ServiceSelection[];
|
|
37
37
|
location: Location | null;
|
|
38
|
+
timezone: string | undefined;
|
|
38
39
|
formSubmission: FormValues | null;
|
|
39
40
|
actions: BookingServiceActions;
|
|
40
41
|
}
|
|
@@ -52,7 +53,7 @@ export interface DataProps {
|
|
|
52
53
|
* @example
|
|
53
54
|
* ```tsx
|
|
54
55
|
* <Booking.Data>
|
|
55
|
-
* {({ serviceSelections, location, formSubmission, actions }) => (
|
|
56
|
+
* {({ serviceSelections, location, timezone, formSubmission, actions }) => (
|
|
56
57
|
* serviceSelections.length > 0 ? (
|
|
57
58
|
* <div>{serviceSelections.map(item => item.service.name).join(', ')}</div>
|
|
58
59
|
* ) : (
|
|
@@ -18,7 +18,8 @@ import { BookingServiceDefinition, BookingService, } from '../../../services/boo
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
export function Root(props) {
|
|
21
|
-
|
|
21
|
+
const { bookingServiceConfig = {}, children } = props;
|
|
22
|
+
return (_jsx(WixServices, { servicesMap: createServicesMap().addService(BookingServiceDefinition, BookingService, bookingServiceConfig), children: children }));
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Core component that provides access to booking data via render props.
|
|
@@ -28,7 +29,7 @@ export function Root(props) {
|
|
|
28
29
|
* @example
|
|
29
30
|
* ```tsx
|
|
30
31
|
* <Booking.Data>
|
|
31
|
-
* {({ serviceSelections, location, formSubmission, actions }) => (
|
|
32
|
+
* {({ serviceSelections, location, timezone, formSubmission, actions }) => (
|
|
32
33
|
* serviceSelections.length > 0 ? (
|
|
33
34
|
* <div>{serviceSelections.map(item => item.service.name).join(', ')}</div>
|
|
34
35
|
* ) : (
|
|
@@ -43,11 +44,13 @@ export function Data(props) {
|
|
|
43
44
|
const bookingService = useService(BookingServiceDefinition);
|
|
44
45
|
const serviceSelections = bookingService.serviceSelections.get();
|
|
45
46
|
const location = bookingService.location.get();
|
|
47
|
+
const timezone = bookingService.timezone.get();
|
|
46
48
|
const formSubmission = bookingService.formSubmission.get();
|
|
47
49
|
const { actions } = bookingService;
|
|
48
50
|
return children({
|
|
49
51
|
serviceSelections,
|
|
50
52
|
location,
|
|
53
|
+
timezone,
|
|
51
54
|
formSubmission,
|
|
52
55
|
actions,
|
|
53
56
|
});
|
|
@@ -88,10 +88,10 @@ export declare function StaffMemberProvider(props: StaffMemberProviderProps): Re
|
|
|
88
88
|
*/
|
|
89
89
|
export interface InfoProps {
|
|
90
90
|
children: (data: {
|
|
91
|
-
/**
|
|
92
|
-
startDate:
|
|
93
|
-
/**
|
|
94
|
-
endDate:
|
|
91
|
+
/** Parsed localStartDate as Date (constructed from the raw localStartDate string) */
|
|
92
|
+
startDate: Date | undefined;
|
|
93
|
+
/** Parsed localEndDate as Date (constructed from the raw localEndDate string) */
|
|
94
|
+
endDate: Date | undefined;
|
|
95
95
|
/** Calculated duration in minutes (null if dates unavailable) */
|
|
96
96
|
durationInMinutes: number | null;
|
|
97
97
|
/** Whether the timeSlot is bookable */
|
|
@@ -116,15 +116,17 @@ export function StaffMemberProvider(props) {
|
|
|
116
116
|
*/
|
|
117
117
|
export function Info(props) {
|
|
118
118
|
const { timeSlot, isSelected } = useTimeSlotContext();
|
|
119
|
-
const startDate = timeSlot.localStartDate
|
|
120
|
-
|
|
119
|
+
const startDate = timeSlot.localStartDate
|
|
120
|
+
? new Date(timeSlot.localStartDate)
|
|
121
|
+
: undefined;
|
|
122
|
+
const endDate = timeSlot.localEndDate
|
|
123
|
+
? new Date(timeSlot.localEndDate)
|
|
124
|
+
: undefined;
|
|
121
125
|
const bookable = timeSlot.bookable ?? false;
|
|
122
126
|
// Calculate duration from start and end dates
|
|
123
127
|
let durationInMinutes = null;
|
|
124
128
|
if (startDate && endDate) {
|
|
125
|
-
|
|
126
|
-
const endTime = new Date(endDate).getTime();
|
|
127
|
-
durationInMinutes = Math.round((endTime - startTime) / (1000 * 60));
|
|
129
|
+
durationInMinutes = Math.round((endDate.getTime() - startDate.getTime()) / (1000 * 60));
|
|
128
130
|
}
|
|
129
131
|
return props.children({
|
|
130
132
|
startDate,
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useService, WixServices } from '@wix/services-manager-react';
|
|
3
3
|
import { createServicesMap } from '@wix/services-manager';
|
|
4
4
|
import { TimeSlotListServiceDefinition, TimeSlotListService, } from '../../../services/time-slot-list/index.js';
|
|
5
|
+
import { BookingServiceDefinition } from '../../../services/booking/booking.js';
|
|
5
6
|
/**
|
|
6
7
|
* Root component that provides TimeSlotListService context to its children.
|
|
7
8
|
*
|
|
@@ -54,8 +55,8 @@ function getTimezoneDisplayName(timezone) {
|
|
|
54
55
|
* ```
|
|
55
56
|
*/
|
|
56
57
|
export function Timezone(props) {
|
|
57
|
-
const
|
|
58
|
-
const timezone =
|
|
58
|
+
const bookingService = useService(BookingServiceDefinition);
|
|
59
|
+
const timezone = bookingService.timezone.get();
|
|
59
60
|
const displayName = getTimezoneDisplayName(timezone);
|
|
60
61
|
return props.children({ timezone, displayName });
|
|
61
62
|
}
|
|
@@ -16,18 +16,18 @@ export type { LocationData, StaffMemberData, } from '../core/time-slot-list/Time
|
|
|
16
16
|
export interface StartDateProps {
|
|
17
17
|
asChild?: boolean;
|
|
18
18
|
children?: AsChildChildren<{
|
|
19
|
-
startDate:
|
|
19
|
+
startDate: Date | undefined;
|
|
20
20
|
}>;
|
|
21
21
|
className?: string;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* Displays the time slot start date/time as a
|
|
24
|
+
* Displays the time slot start date/time as a Date.
|
|
25
25
|
* Consumer is responsible for formatting.
|
|
26
26
|
*
|
|
27
27
|
* @component
|
|
28
28
|
* @example
|
|
29
29
|
* ```tsx
|
|
30
|
-
* // Default usage - displays
|
|
30
|
+
* // Default usage - displays Date.toString()
|
|
31
31
|
* <TimeSlot.StartDate className="text-foreground" />
|
|
32
32
|
*
|
|
33
33
|
* // With custom formatting
|
|
@@ -45,18 +45,18 @@ export declare const StartDate: React.ForwardRefExoticComponent<StartDateProps &
|
|
|
45
45
|
export interface EndDateProps {
|
|
46
46
|
asChild?: boolean;
|
|
47
47
|
children?: AsChildChildren<{
|
|
48
|
-
endDate:
|
|
48
|
+
endDate: Date | undefined;
|
|
49
49
|
}>;
|
|
50
50
|
className?: string;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
* Displays the time slot end date/time as a
|
|
53
|
+
* Displays the time slot end date/time as a Date.
|
|
54
54
|
* Consumer is responsible for formatting.
|
|
55
55
|
*
|
|
56
56
|
* @component
|
|
57
57
|
* @example
|
|
58
58
|
* ```tsx
|
|
59
|
-
* // Default usage - displays
|
|
59
|
+
* // Default usage - displays Date.toString()
|
|
60
60
|
* <TimeSlot.EndDate className="text-foreground" />
|
|
61
61
|
*
|
|
62
62
|
* // With custom formatting
|
|
@@ -34,13 +34,13 @@ var TestIds;
|
|
|
34
34
|
TestIds["timeSlotStaffMemberActionSelect"] = "time-slot-staff-member-action-select";
|
|
35
35
|
})(TestIds || (TestIds = {}));
|
|
36
36
|
/**
|
|
37
|
-
* Displays the time slot start date/time as a
|
|
37
|
+
* Displays the time slot start date/time as a Date.
|
|
38
38
|
* Consumer is responsible for formatting.
|
|
39
39
|
*
|
|
40
40
|
* @component
|
|
41
41
|
* @example
|
|
42
42
|
* ```tsx
|
|
43
|
-
* // Default usage - displays
|
|
43
|
+
* // Default usage - displays Date.toString()
|
|
44
44
|
* <TimeSlot.StartDate className="text-foreground" />
|
|
45
45
|
*
|
|
46
46
|
* // With custom formatting
|
|
@@ -53,17 +53,17 @@ var TestIds;
|
|
|
53
53
|
*/
|
|
54
54
|
export const StartDate = React.forwardRef((props, ref) => {
|
|
55
55
|
const { asChild, children, className } = props;
|
|
56
|
-
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 }) })) }));
|
|
56
|
+
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?.toString() }) })) }));
|
|
57
57
|
});
|
|
58
58
|
StartDate.displayName = 'TimeSlot.StartDate';
|
|
59
59
|
/**
|
|
60
|
-
* Displays the time slot end date/time as a
|
|
60
|
+
* Displays the time slot end date/time as a Date.
|
|
61
61
|
* Consumer is responsible for formatting.
|
|
62
62
|
*
|
|
63
63
|
* @component
|
|
64
64
|
* @example
|
|
65
65
|
* ```tsx
|
|
66
|
-
* // Default usage - displays
|
|
66
|
+
* // Default usage - displays Date.toString()
|
|
67
67
|
* <TimeSlot.EndDate className="text-foreground" />
|
|
68
68
|
*
|
|
69
69
|
* // With custom formatting
|
|
@@ -76,7 +76,7 @@ StartDate.displayName = 'TimeSlot.StartDate';
|
|
|
76
76
|
*/
|
|
77
77
|
export const EndDate = React.forwardRef((props, ref) => {
|
|
78
78
|
const { asChild, children, className } = props;
|
|
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 }) })) }));
|
|
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?.toString() }) })) }));
|
|
80
80
|
});
|
|
81
81
|
EndDate.displayName = 'TimeSlot.EndDate';
|
|
82
82
|
/**
|
|
@@ -113,10 +113,10 @@ export interface TimeSlotRepeaterChildProps {
|
|
|
113
113
|
isSelected: boolean;
|
|
114
114
|
/** Whether the slot can be booked */
|
|
115
115
|
bookable: boolean;
|
|
116
|
-
/** Start date/time as
|
|
117
|
-
startDate:
|
|
118
|
-
/** End date/time as
|
|
119
|
-
endDate:
|
|
116
|
+
/** Start date/time as Date (constructed from the raw localStartDate string) */
|
|
117
|
+
startDate: Date | undefined;
|
|
118
|
+
/** End date/time as Date (constructed from the raw localEndDate string) */
|
|
119
|
+
endDate: Date | undefined;
|
|
120
120
|
/** Duration in minutes */
|
|
121
121
|
durationInMinutes: number | null;
|
|
122
122
|
/** The raw time slot data */
|
|
@@ -58,6 +58,7 @@ export interface BookingServiceActions {
|
|
|
58
58
|
getResourceById(resourceId: string, instanceId?: string): Resource | undefined;
|
|
59
59
|
setLocation(location: Location | null): void;
|
|
60
60
|
clearLocation(): void;
|
|
61
|
+
setTimezone(timezone: string): void;
|
|
61
62
|
setFormSubmission(values: FormValues | null): void;
|
|
62
63
|
clearFormSubmission(): void;
|
|
63
64
|
clearAll(): void;
|
|
@@ -71,6 +72,8 @@ export interface BookingService {
|
|
|
71
72
|
serviceSelections: Signal<ServiceSelection[]>;
|
|
72
73
|
/** Signal containing the selected location */
|
|
73
74
|
location: Signal<Location | null>;
|
|
75
|
+
/** Signal containing the selected timezone */
|
|
76
|
+
timezone: Signal<string | undefined>;
|
|
74
77
|
/** Signal containing the form submission */
|
|
75
78
|
formSubmission: Signal<FormValues | null>;
|
|
76
79
|
/** Actions for modifying booking state */
|
|
@@ -93,6 +96,8 @@ export interface BookingServiceConfig {
|
|
|
93
96
|
serviceSelections?: ServiceSelection[];
|
|
94
97
|
/** Optional initial location */
|
|
95
98
|
location?: Location | null;
|
|
99
|
+
/** Optional initial timezone */
|
|
100
|
+
timezone?: string;
|
|
96
101
|
/** Optional initial form submission */
|
|
97
102
|
formSubmission?: FormValues | null;
|
|
98
103
|
}
|
|
@@ -10,7 +10,7 @@ import { SignalsServiceDefinition, } from '@wix/services-definitions/core-servic
|
|
|
10
10
|
// ============================================================================
|
|
11
11
|
export const ANY_STAFF_MEMBER_ID = '__any_staff_member__';
|
|
12
12
|
// ============================================================================
|
|
13
|
-
//
|
|
13
|
+
// Helpers
|
|
14
14
|
// ============================================================================
|
|
15
15
|
/**
|
|
16
16
|
* Generates a unique instance ID for service selections
|
|
@@ -18,6 +18,14 @@ export const ANY_STAFF_MEMBER_ID = '__any_staff_member__';
|
|
|
18
18
|
function generateInstanceId() {
|
|
19
19
|
return crypto.randomUUID();
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Gets the client's timezone from Intl API
|
|
23
|
+
*/
|
|
24
|
+
function getClientTimezone() {
|
|
25
|
+
return typeof Intl !== 'undefined'
|
|
26
|
+
? Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
27
|
+
: undefined;
|
|
28
|
+
}
|
|
21
29
|
/**
|
|
22
30
|
* Service definition for Booking
|
|
23
31
|
*/
|
|
@@ -59,6 +67,7 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
59
67
|
}));
|
|
60
68
|
const serviceSelectionsSignal = signalsService.signal(initialItemsWithIds);
|
|
61
69
|
const locationSignal = signalsService.signal(config.location || null);
|
|
70
|
+
const timezoneSignal = signalsService.signal(config.timezone ?? getClientTimezone());
|
|
62
71
|
const formSubmissionSignal = signalsService.signal(config.formSubmission || null);
|
|
63
72
|
// ========================================================================
|
|
64
73
|
// Actions Implementation
|
|
@@ -146,6 +155,12 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
146
155
|
clearLocation() {
|
|
147
156
|
locationSignal.set(null);
|
|
148
157
|
},
|
|
158
|
+
// Timezone actions
|
|
159
|
+
setTimezone(timezone) {
|
|
160
|
+
if (timezoneSignal.get() !== timezone) {
|
|
161
|
+
timezoneSignal.set(timezone);
|
|
162
|
+
}
|
|
163
|
+
},
|
|
149
164
|
// Form Submission actions
|
|
150
165
|
setFormSubmission(values) {
|
|
151
166
|
formSubmissionSignal.set(values);
|
|
@@ -163,6 +178,7 @@ export const BookingService = implementService.withConfig()(BookingServiceDefini
|
|
|
163
178
|
return {
|
|
164
179
|
serviceSelections: serviceSelectionsSignal,
|
|
165
180
|
location: locationSignal,
|
|
181
|
+
timezone: timezoneSignal,
|
|
166
182
|
formSubmission: formSubmissionSignal,
|
|
167
183
|
actions,
|
|
168
184
|
};
|
|
@@ -65,11 +65,21 @@ export interface TimeSlotsFilters {
|
|
|
65
65
|
* Default: 1000. Max: 1000.
|
|
66
66
|
* @example
|
|
67
67
|
* ```ts
|
|
68
|
-
* // Load
|
|
69
|
-
* filters: { slotsPerPage:
|
|
68
|
+
* // Load 10 slots at a time
|
|
69
|
+
* filters: { slotsPerPage: 10 }
|
|
70
70
|
* ```
|
|
71
71
|
*/
|
|
72
72
|
slotsPerPage?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Maximum number of time slots to return per day.
|
|
75
|
+
* Useful for fetching availability indicators (set to 1 to get one slot per day).
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* // Get one slot per day for calendar indicators
|
|
79
|
+
* filters: { timeSlotsPerDay: 1 }
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
timeSlotsPerDay?: number;
|
|
73
83
|
}
|
|
74
84
|
export interface TimeSlotListServiceConfig {
|
|
75
85
|
/** Services to fetch time slots for */
|
|
@@ -92,7 +102,6 @@ export interface TimeSlotListServiceActions {
|
|
|
92
102
|
clearPreferences: () => void;
|
|
93
103
|
loadMore: () => Promise<void>;
|
|
94
104
|
setDateRange: (start: Date, end: Date) => void;
|
|
95
|
-
setTimezone: (tz: string) => void;
|
|
96
105
|
}
|
|
97
106
|
export interface TimeSlotListServiceAPI {
|
|
98
107
|
timeSlots: Signal<TimeSlot[]>;
|
|
@@ -102,7 +111,6 @@ export interface TimeSlotListServiceAPI {
|
|
|
102
111
|
hasMore: Signal<boolean>;
|
|
103
112
|
startDate: Signal<Date>;
|
|
104
113
|
endDate: Signal<Date>;
|
|
105
|
-
timezone: Signal<string | undefined>;
|
|
106
114
|
actions: TimeSlotListServiceActions;
|
|
107
115
|
}
|
|
108
116
|
export declare const TimeSlotListServiceDefinition: string & {
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { implementService } from '@wix/services-definitions';
|
|
6
6
|
import { SignalsServiceDefinition } from '@wix/services-definitions/core-services/signals';
|
|
7
|
-
import { format } from 'date-fns';
|
|
8
7
|
import { TimeSlotListServiceDefinition, ViewMode, } from './time-slot-list.def.js';
|
|
9
8
|
import { fetchAvailability } from '../../api/fetch-availability/index.js';
|
|
10
9
|
import { BookingServiceDefinition, ANY_STAFF_MEMBER_ID, } from '../booking/booking.js';
|
|
@@ -19,13 +18,6 @@ function getClientTimezone() {
|
|
|
19
18
|
? Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
20
19
|
: undefined;
|
|
21
20
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Formats a Date to local ISO string (YYYY-MM-DDThh:mm:ss)
|
|
24
|
-
*/
|
|
25
|
-
function toLocalDateString(date) {
|
|
26
|
-
const pattern = "yyyy-MM-dd'T'HH:mm:ss";
|
|
27
|
-
return format(date, pattern);
|
|
28
|
-
}
|
|
29
21
|
/**
|
|
30
22
|
* Merges new grouped slots with existing ones
|
|
31
23
|
* Slots with the same start time are merged
|
|
@@ -68,7 +60,6 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
68
60
|
const viewModeSignal = signalsService.signal(ViewMode.CLASSIC);
|
|
69
61
|
const startDateSignal = signalsService.signal(config.startDate || currentDate);
|
|
70
62
|
const endDateSignal = signalsService.signal(config.endDate || getDefaultEndDate());
|
|
71
|
-
const timezoneSignal = signalsService.signal(config.timezone ?? getClientTimezone());
|
|
72
63
|
const cursorSignal = signalsService.signal(undefined);
|
|
73
64
|
/**
|
|
74
65
|
* Helper: Get services from config or BookingService
|
|
@@ -86,7 +77,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
86
77
|
const location = bookingService.location.get();
|
|
87
78
|
const serviceSelections = bookingService.serviceSelections.get();
|
|
88
79
|
const selectedStaffMemberIds = serviceSelections
|
|
89
|
-
.map((
|
|
80
|
+
.map((serviceSelection) => serviceSelection.resources.map((r) => r._id))
|
|
90
81
|
.flat();
|
|
91
82
|
const selectedLocation = location ? { _id: location.id } : null;
|
|
92
83
|
// Merge config filters with BookingService selections (selections win)
|
|
@@ -114,9 +105,9 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
114
105
|
try {
|
|
115
106
|
const response = await fetchAvailability({
|
|
116
107
|
services: selectedServices,
|
|
117
|
-
fromLocalDate:
|
|
118
|
-
toLocalDate:
|
|
119
|
-
timeZone:
|
|
108
|
+
fromLocalDate: startDateSignal.get(),
|
|
109
|
+
toLocalDate: endDateSignal.get(),
|
|
110
|
+
timeZone: bookingService.timezone.get(),
|
|
120
111
|
viewMode: viewModeSignal.get(),
|
|
121
112
|
cursor: cursorSignal.get(),
|
|
122
113
|
...filters,
|
|
@@ -312,16 +303,14 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
312
303
|
if (!cursor || isLoadingSignal.get())
|
|
313
304
|
return;
|
|
314
305
|
const selectedServices = getServices();
|
|
315
|
-
if (selectedServices.length === 0)
|
|
316
|
-
return;
|
|
317
306
|
const filters = getFilters();
|
|
318
307
|
isLoadingSignal.set(true);
|
|
319
308
|
try {
|
|
320
309
|
const response = await fetchAvailability({
|
|
321
310
|
services: selectedServices,
|
|
322
|
-
fromLocalDate:
|
|
323
|
-
toLocalDate:
|
|
324
|
-
timeZone:
|
|
311
|
+
fromLocalDate: startDateSignal.get(),
|
|
312
|
+
toLocalDate: endDateSignal.get(),
|
|
313
|
+
timeZone: bookingService.timezone.get(),
|
|
325
314
|
cursor,
|
|
326
315
|
viewMode: viewModeSignal.get(),
|
|
327
316
|
...filters,
|
|
@@ -357,17 +346,21 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
357
346
|
endDateSignal.set(newEndDate);
|
|
358
347
|
fetchTimeSlots();
|
|
359
348
|
};
|
|
360
|
-
/**
|
|
361
|
-
* Action: Update timezone and refetch slots
|
|
362
|
-
*/
|
|
363
|
-
const setTimezone = (newTimezone) => {
|
|
364
|
-
timezoneSignal.set(newTimezone);
|
|
365
|
-
fetchTimeSlots();
|
|
366
|
-
};
|
|
367
349
|
// Auto-fetch on initialization if no pre-loaded data provided
|
|
368
|
-
if (!config.timeSlots
|
|
350
|
+
if (!config.timeSlots) {
|
|
369
351
|
fetchTimeSlots();
|
|
370
352
|
}
|
|
353
|
+
// Subscribe to timezone changes from BookingService to auto-refetch
|
|
354
|
+
// NOTE: This should depend ONLY on bookingService.timezone.
|
|
355
|
+
let firstTimezoneEffectRun = true;
|
|
356
|
+
signalsService.effect(() => {
|
|
357
|
+
bookingService.timezone.get();
|
|
358
|
+
if (firstTimezoneEffectRun) {
|
|
359
|
+
firstTimezoneEffectRun = false;
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
fetchTimeSlots();
|
|
363
|
+
});
|
|
371
364
|
// Return service API
|
|
372
365
|
return {
|
|
373
366
|
// State Signals
|
|
@@ -379,7 +372,6 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
379
372
|
// Configuration Signals (viewMode is internal only)
|
|
380
373
|
startDate: startDateSignal,
|
|
381
374
|
endDate: endDateSignal,
|
|
382
|
-
timezone: timezoneSignal,
|
|
383
375
|
// Actions
|
|
384
376
|
actions: {
|
|
385
377
|
selectSlot,
|
|
@@ -388,7 +380,6 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
388
380
|
clearPreferences,
|
|
389
381
|
loadMore,
|
|
390
382
|
setDateRange,
|
|
391
|
-
setTimezone,
|
|
392
383
|
},
|
|
393
384
|
};
|
|
394
385
|
});
|
|
@@ -420,8 +411,8 @@ export async function loadTimeSlotListConfig(options) {
|
|
|
420
411
|
try {
|
|
421
412
|
const response = await fetchAvailability({
|
|
422
413
|
services: options.services || [],
|
|
423
|
-
fromLocalDate:
|
|
424
|
-
toLocalDate:
|
|
414
|
+
fromLocalDate: startDate,
|
|
415
|
+
toLocalDate: endDate,
|
|
425
416
|
timeZone: options.timezone ?? getClientTimezone(),
|
|
426
417
|
...options.filters,
|
|
427
418
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-bookings",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"groupId": "com.wixpress.headless-components"
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
|
-
"falconPackageHash": "
|
|
73
|
+
"falconPackageHash": "0404aa644e83255c1641503e68142fb002318cef90f00e879d419ac9"
|
|
74
74
|
}
|