@wix/headless-bookings 1.0.0 → 1.0.1
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/booking-mocks.d.ts +1 -1
- package/cjs/dist/__mocks__/services/services-mocks.d.ts +23 -0
- package/cjs/dist/__mocks__/services/services-mocks.js +65 -1
- package/cjs/dist/api/query-services/index.js +2 -2
- package/cjs/dist/react/core/booking/Booking.d.ts +1 -1
- package/cjs/dist/react/core/booking/Booking.js +14 -5
- package/cjs/dist/react/core/location/Location.d.ts +1 -1
- package/cjs/dist/react/core/location/Location.js +5 -5
- package/cjs/dist/react/core/service/Service.d.ts +10 -1
- package/cjs/dist/react/core/service/Service.js +5 -4
- package/cjs/dist/react/service/Service.d.ts +68 -1
- package/cjs/dist/react/service/Service.js +81 -1
- package/cjs/dist/services/booking/booking.d.ts +1 -1
- package/cjs/dist/services/location-list/location-list.def.d.ts +1 -1
- package/cjs/dist/services/location-list/location-list.def.js +1 -1
- package/cjs/dist/services/location-list/location-list.js +7 -7
- package/dist/__mocks__/booking/booking-mocks.d.ts +1 -1
- package/dist/__mocks__/services/services-mocks.d.ts +23 -0
- package/dist/__mocks__/services/services-mocks.js +64 -0
- package/dist/api/query-services/index.js +1 -1
- package/dist/react/core/booking/Booking.d.ts +1 -1
- package/dist/react/core/booking/Booking.js +14 -5
- package/dist/react/core/location/Location.d.ts +1 -1
- package/dist/react/core/location/Location.js +1 -1
- package/dist/react/core/service/Service.d.ts +10 -1
- package/dist/react/core/service/Service.js +5 -4
- package/dist/react/service/Service.d.ts +68 -1
- package/dist/react/service/Service.js +81 -1
- package/dist/services/booking/booking.d.ts +1 -1
- package/dist/services/location-list/location-list.def.d.ts +1 -1
- package/dist/services/location-list/location-list.def.js +1 -1
- package/dist/services/location-list/location-list.js +1 -1
- package/package.json +4 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FormValues } from '@wix/forms/components';
|
|
2
|
-
import type { Location } from '@wix/auto_sdk_bookings_services';
|
|
2
|
+
import type { V2Location as Location } from '@wix/auto_sdk_bookings_services';
|
|
3
3
|
import type { ServiceSelection, ServiceSelectionTimeSlot, ExtendedResource } from '../../services/booking/booking.js';
|
|
4
4
|
export declare function createMockResource(overrides?: Partial<ExtendedResource>): ExtendedResource;
|
|
5
5
|
export declare function createMockLocation(overrides?: Partial<Location>): Location;
|
|
@@ -35,6 +35,29 @@ export declare const mockServiceCourse: Service;
|
|
|
35
35
|
* Mock service without optional fields (minimal)
|
|
36
36
|
*/
|
|
37
37
|
export declare const mockServiceMinimal: Service;
|
|
38
|
+
/**
|
|
39
|
+
* Mock service driving the fixed-duration path: a single value in
|
|
40
|
+
* `sessionDurations`, no `durationRange`. Drives `Service.DurationInMinutes`;
|
|
41
|
+
* `Service.DurationRange` renders nothing for this shape.
|
|
42
|
+
*
|
|
43
|
+
* `mockService` already carries this shape — this is a renamed copy for
|
|
44
|
+
* tests/examples that want to name the case they're exercising.
|
|
45
|
+
*/
|
|
46
|
+
export declare const mockServiceFixedDuration: Service;
|
|
47
|
+
/**
|
|
48
|
+
* Mock service driving the HOUR-unit DurationRange path. Mirrors the server
|
|
49
|
+
* payload shape: empty `sessionDurations`, range pre-computed at
|
|
50
|
+
* `availabilityConstraints.durationRange`. `durationRange` is not yet in the
|
|
51
|
+
* SDK's `AvailabilityConstraints` typing (runtime is ahead of types), so the
|
|
52
|
+
* service object is cast — matches the read-side cast in
|
|
53
|
+
* `core/service/Service.tsx`.
|
|
54
|
+
*/
|
|
55
|
+
export declare const mockServiceHourRange: Service;
|
|
56
|
+
/**
|
|
57
|
+
* Mock service driving the DAY-unit DurationRange path. Same server shape as
|
|
58
|
+
* {@link mockServiceHourRange}.
|
|
59
|
+
*/
|
|
60
|
+
export declare const mockServiceDayRange: Service;
|
|
38
61
|
/**
|
|
39
62
|
* Array of mock services for list tests
|
|
40
63
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Shared mock data for booking service tests
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.mockServiceInPerson = exports.mockServiceWithDeposit = exports.mockSingleStaffMemberListConfig = exports.mockEmptyStaffMemberListConfig = exports.mockStaffMemberListConfig = exports.mockStaffMembers = exports.mockStaffMemberNoName = exports.mockStaffMemberMinimal = exports.mockStaffMember2 = exports.mockStaffMember = exports.mockServiceListConfigWithCategories = exports.mockCategories = exports.mockServiceConfig = exports.mockEmptyServiceListConfig = exports.mockServiceListConfigWithMore = exports.mockServiceListConfig = exports.mockPagingMetadataEmpty = exports.mockPagingMetadataWithMore = exports.mockPagingMetadata = exports.mockServices = exports.mockServiceMinimal = exports.mockServiceCourse = exports.mockServiceClass = exports.mockServiceNoFee = exports.mockServiceCustomPrice = exports.mockServiceVariedPrice = exports.mockService = void 0;
|
|
6
|
+
exports.mockServiceInPerson = exports.mockServiceWithDeposit = exports.mockSingleStaffMemberListConfig = exports.mockEmptyStaffMemberListConfig = exports.mockStaffMemberListConfig = exports.mockStaffMembers = exports.mockStaffMemberNoName = exports.mockStaffMemberMinimal = exports.mockStaffMember2 = exports.mockStaffMember = exports.mockServiceListConfigWithCategories = exports.mockCategories = exports.mockServiceConfig = exports.mockEmptyServiceListConfig = exports.mockServiceListConfigWithMore = exports.mockServiceListConfig = exports.mockPagingMetadataEmpty = exports.mockPagingMetadataWithMore = exports.mockPagingMetadata = exports.mockServices = exports.mockServiceDayRange = exports.mockServiceHourRange = exports.mockServiceFixedDuration = exports.mockServiceMinimal = exports.mockServiceCourse = exports.mockServiceClass = exports.mockServiceNoFee = exports.mockServiceCustomPrice = exports.mockServiceVariedPrice = exports.mockService = void 0;
|
|
7
7
|
exports.createLargeServiceList = createLargeServiceList;
|
|
8
8
|
exports.createLargeStaffMemberList = createLargeStaffMemberList;
|
|
9
9
|
const auto_sdk_bookings_services_1 = require("@wix/auto_sdk_bookings_services");
|
|
@@ -175,6 +175,70 @@ exports.mockServiceMinimal = {
|
|
|
175
175
|
name: 'Minimal Service',
|
|
176
176
|
description: '',
|
|
177
177
|
};
|
|
178
|
+
/**
|
|
179
|
+
* Mock service driving the fixed-duration path: a single value in
|
|
180
|
+
* `sessionDurations`, no `durationRange`. Drives `Service.DurationInMinutes`;
|
|
181
|
+
* `Service.DurationRange` renders nothing for this shape.
|
|
182
|
+
*
|
|
183
|
+
* `mockService` already carries this shape — this is a renamed copy for
|
|
184
|
+
* tests/examples that want to name the case they're exercising.
|
|
185
|
+
*/
|
|
186
|
+
exports.mockServiceFixedDuration = {
|
|
187
|
+
...exports.mockService,
|
|
188
|
+
_id: 'test-service-fixed-duration',
|
|
189
|
+
name: 'Fixed Duration Service',
|
|
190
|
+
schedule: {
|
|
191
|
+
availabilityConstraints: {
|
|
192
|
+
sessionDurations: [60],
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Mock service driving the HOUR-unit DurationRange path. Mirrors the server
|
|
198
|
+
* payload shape: empty `sessionDurations`, range pre-computed at
|
|
199
|
+
* `availabilityConstraints.durationRange`. `durationRange` is not yet in the
|
|
200
|
+
* SDK's `AvailabilityConstraints` typing (runtime is ahead of types), so the
|
|
201
|
+
* service object is cast — matches the read-side cast in
|
|
202
|
+
* `core/service/Service.tsx`.
|
|
203
|
+
*/
|
|
204
|
+
exports.mockServiceHourRange = {
|
|
205
|
+
...exports.mockService,
|
|
206
|
+
_id: 'test-service-hour-range',
|
|
207
|
+
name: 'Hourly Range Service',
|
|
208
|
+
schedule: {
|
|
209
|
+
availabilityConstraints: {
|
|
210
|
+
sessionDurations: [],
|
|
211
|
+
durationRange: {
|
|
212
|
+
unitType: 'HOUR',
|
|
213
|
+
hourOptions: {
|
|
214
|
+
minDurationInMinutes: 120,
|
|
215
|
+
maxDurationInMinutes: 540,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Mock service driving the DAY-unit DurationRange path. Same server shape as
|
|
223
|
+
* {@link mockServiceHourRange}.
|
|
224
|
+
*/
|
|
225
|
+
exports.mockServiceDayRange = {
|
|
226
|
+
...exports.mockService,
|
|
227
|
+
_id: 'test-service-day-range',
|
|
228
|
+
name: 'Daily Range Service',
|
|
229
|
+
schedule: {
|
|
230
|
+
availabilityConstraints: {
|
|
231
|
+
sessionDurations: [],
|
|
232
|
+
durationRange: {
|
|
233
|
+
unitType: 'DAY',
|
|
234
|
+
dayOptions: {
|
|
235
|
+
minDurationInDays: 2,
|
|
236
|
+
maxDurationInDays: 5,
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
};
|
|
178
242
|
/**
|
|
179
243
|
* Array of mock services for list tests
|
|
180
244
|
*/
|
|
@@ -143,10 +143,10 @@ function convertPlatformFilterToApiFilter(platformFilter) {
|
|
|
143
143
|
const businessLocationIds = [];
|
|
144
144
|
for (const id of locationIds) {
|
|
145
145
|
if (id === exports.SYNTHETIC_CUSTOM_ID) {
|
|
146
|
-
syntheticTypes.push(auto_sdk_bookings_services_1.
|
|
146
|
+
syntheticTypes.push(auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOM);
|
|
147
147
|
}
|
|
148
148
|
else if (id === exports.SYNTHETIC_CUSTOMER_ID) {
|
|
149
|
-
syntheticTypes.push(auto_sdk_bookings_services_1.
|
|
149
|
+
syntheticTypes.push(auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOMER);
|
|
150
150
|
}
|
|
151
151
|
else {
|
|
152
152
|
businessLocationIds.push(id);
|
|
@@ -6,7 +6,7 @@ import React from 'react';
|
|
|
6
6
|
import { type BookingServiceConfig, type ServiceSelection, type BookingServiceActions } from '../../../services/booking/booking.js';
|
|
7
7
|
import type { FormValues } from '@wix/forms/components';
|
|
8
8
|
import type { TimeSlot, Resource } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
9
|
-
import { type Service, type
|
|
9
|
+
import { type Service, type V2Location as ServiceLocationType } from '@wix/auto_sdk_bookings_services';
|
|
10
10
|
import type { PricingServiceSelection } from '../../../services/payment/payment.js';
|
|
11
11
|
/**
|
|
12
12
|
* Props for Booking.Root component
|
|
@@ -65,7 +65,16 @@ function Root(props) {
|
|
|
65
65
|
function mapTimeSlotLocationToServiceLocation(timeSlotLocation) {
|
|
66
66
|
// TimeSlot location has flat name/formattedAddress at top level
|
|
67
67
|
// Convert to nested business/custom structure that CoreLocation expects
|
|
68
|
-
const
|
|
68
|
+
const isCustomer = timeSlotLocation.locationType === auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOMER;
|
|
69
|
+
const isCustom = timeSlotLocation.locationType === auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOM;
|
|
70
|
+
// The two SDKs share BUSINESS/CUSTOM/CUSTOMER but the time-slots enum also
|
|
71
|
+
// exposes UNKNOWN_LOCATION_TYPE, which V2Location.type does not accept.
|
|
72
|
+
// Default to BUSINESS for any unrecognized value.
|
|
73
|
+
const type = isCustomer
|
|
74
|
+
? auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOMER
|
|
75
|
+
: isCustom
|
|
76
|
+
? auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOM
|
|
77
|
+
: auto_sdk_bookings_services_1.LocationTypeEnumLocationType.BUSINESS;
|
|
69
78
|
const location = {
|
|
70
79
|
_id: timeSlotLocation._id ?? undefined,
|
|
71
80
|
name: timeSlotLocation.name ?? undefined,
|
|
@@ -77,10 +86,10 @@ function mapTimeSlotLocationToServiceLocation(timeSlotLocation) {
|
|
|
77
86
|
};
|
|
78
87
|
return {
|
|
79
88
|
_id: timeSlotLocation._id ?? undefined,
|
|
80
|
-
type
|
|
81
|
-
...(
|
|
89
|
+
type,
|
|
90
|
+
...(isCustomer
|
|
82
91
|
? null
|
|
83
|
-
:
|
|
92
|
+
: isCustom
|
|
84
93
|
? { custom: location }
|
|
85
94
|
: { business: location }),
|
|
86
95
|
};
|
|
@@ -91,7 +100,7 @@ function mapTimeSlotLocationToServiceLocation(timeSlotLocation) {
|
|
|
91
100
|
* 2. If not set, fall back to the first service selection's slot location
|
|
92
101
|
*/
|
|
93
102
|
function resolveLocation(bookingLocation, serviceSelections) {
|
|
94
|
-
if (bookingLocation && bookingLocation.type === auto_sdk_bookings_services_1.
|
|
103
|
+
if (bookingLocation && bookingLocation.type === auto_sdk_bookings_services_1.LocationTypeEnumLocationType.BUSINESS) {
|
|
95
104
|
return bookingLocation;
|
|
96
105
|
}
|
|
97
106
|
// Fallback to first slot's location
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module Core/Location
|
|
7
7
|
*/
|
|
8
8
|
import React from 'react';
|
|
9
|
-
import { LocationType } from '@wix/auto_sdk_bookings_services';
|
|
9
|
+
import { LocationTypeEnumLocationType as LocationType } from '@wix/auto_sdk_bookings_services';
|
|
10
10
|
import type { AddressData as AddressDataType } from '@wix/headless-components/react';
|
|
11
11
|
import type { Location } from '../../../services/location-list/location-list.def.js';
|
|
12
12
|
/**
|
|
@@ -90,7 +90,7 @@ function hasLocationData(location) {
|
|
|
90
90
|
*/
|
|
91
91
|
function parseLocationAddress(location) {
|
|
92
92
|
// Handle customer location - return null, let consumer handle
|
|
93
|
-
if (location.type !== auto_sdk_bookings_services_1.
|
|
93
|
+
if (location.type !== auto_sdk_bookings_services_1.LocationTypeEnumLocationType.BUSINESS) {
|
|
94
94
|
return null;
|
|
95
95
|
}
|
|
96
96
|
// Get the common address from the appropriate location type
|
|
@@ -123,7 +123,7 @@ function parseLocationAddress(location) {
|
|
|
123
123
|
* Get location type from location data
|
|
124
124
|
*/
|
|
125
125
|
function getLocationType(location) {
|
|
126
|
-
return location.type || auto_sdk_bookings_services_1.
|
|
126
|
+
return location.type || auto_sdk_bookings_services_1.LocationTypeEnumLocationType.BUSINESS;
|
|
127
127
|
}
|
|
128
128
|
/**
|
|
129
129
|
* Computes LocationRenderProps from location with label fallbacks
|
|
@@ -132,7 +132,7 @@ function computeLocationRenderProps(rawLocation, labels) {
|
|
|
132
132
|
// Handle no location
|
|
133
133
|
if (!rawLocation) {
|
|
134
134
|
return {
|
|
135
|
-
locationType: auto_sdk_bookings_services_1.
|
|
135
|
+
locationType: auto_sdk_bookings_services_1.LocationTypeEnumLocationType.BUSINESS,
|
|
136
136
|
address: null,
|
|
137
137
|
name: undefined,
|
|
138
138
|
locationId: undefined,
|
|
@@ -146,8 +146,8 @@ function computeLocationRenderProps(rawLocation, labels) {
|
|
|
146
146
|
}
|
|
147
147
|
const locationType = getLocationType(rawLocation);
|
|
148
148
|
const hasData = hasLocationData(rawLocation);
|
|
149
|
-
const isCustomLocation = locationType === auto_sdk_bookings_services_1.
|
|
150
|
-
const isCustomerLocation = locationType === auto_sdk_bookings_services_1.
|
|
149
|
+
const isCustomLocation = locationType === auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOM;
|
|
150
|
+
const isCustomerLocation = locationType === auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOMER;
|
|
151
151
|
// Compute name with fallback chain:
|
|
152
152
|
// 1. business.name
|
|
153
153
|
// 2. custom.address.addressLine
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { type ServiceServiceConfig } from '../../../services/service/service.js';
|
|
7
|
-
import type { Service, Payment, BookingPolicy, Schedule, StaffMember } from '@wix/auto_sdk_bookings_services';
|
|
7
|
+
import type { Service, Payment, BookingPolicy, Schedule, StaffMember, DurationRange } from '@wix/auto_sdk_bookings_services';
|
|
8
8
|
import type { MoneyData } from '@wix/headless-components/react';
|
|
9
9
|
/**
|
|
10
10
|
* Props for Service.Root component
|
|
@@ -50,6 +50,15 @@ export interface InfoProps {
|
|
|
50
50
|
description: string;
|
|
51
51
|
tagline?: string;
|
|
52
52
|
duration?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Typed range of supported session durations, derived from
|
|
55
|
+
* `service.schedule.availabilityConstraints.sessionDurations`. Set when the
|
|
56
|
+
* service supports two or more distinct durations; otherwise undefined and
|
|
57
|
+
* the single-value `duration` field above should be used. Consumers branch
|
|
58
|
+
* on `range.unitType` (the SDK `UnitType` enum) and read the matching
|
|
59
|
+
* `range.hourOptions` / `range.dayOptions` config.
|
|
60
|
+
*/
|
|
61
|
+
durationRange?: DurationRange;
|
|
53
62
|
type?: string;
|
|
54
63
|
category?: {
|
|
55
64
|
name?: string;
|
|
@@ -96,9 +96,9 @@ function Info(props) {
|
|
|
96
96
|
currency,
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
99
|
+
const constraints = service.schedule?.availabilityConstraints;
|
|
100
|
+
const duration = constraints?.sessionDurations?.[0];
|
|
101
|
+
const durationRange = constraints?.durationRange;
|
|
102
102
|
// Get staff members from staffMemberDetails.staffMembers
|
|
103
103
|
// (available when using conditionalFields: ['STAFF_MEMBER_DETAILS'] in queryServices)
|
|
104
104
|
const staff = service.staffMembers || [];
|
|
@@ -128,7 +128,8 @@ function Info(props) {
|
|
|
128
128
|
name: service.name || '',
|
|
129
129
|
description: service.description || '',
|
|
130
130
|
tagline: service.tagLine || undefined,
|
|
131
|
-
duration
|
|
131
|
+
duration,
|
|
132
|
+
durationRange,
|
|
132
133
|
type: service.type,
|
|
133
134
|
category: service.category
|
|
134
135
|
? {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { AsChildChildren } from '@wix/headless-utils/react';
|
|
7
7
|
import { MoneyData } from '@wix/headless-components/react';
|
|
8
|
-
import { type Service, type Payment } from '@wix/auto_sdk_bookings_services';
|
|
8
|
+
import { type Service, type Payment, type DurationRange as DurationRangeData } from '@wix/auto_sdk_bookings_services';
|
|
9
9
|
import * as ServiceMediaModule from './ServiceMedia.js';
|
|
10
10
|
export declare const TestIds: {
|
|
11
11
|
readonly serviceRoot: "service-root";
|
|
@@ -14,6 +14,7 @@ export declare const TestIds: {
|
|
|
14
14
|
readonly servicePrice: "service-price";
|
|
15
15
|
readonly serviceTagline: "service-tagline";
|
|
16
16
|
readonly serviceDurationInMinutes: "service-duration-in-minutes";
|
|
17
|
+
readonly serviceDurationRange: "service-duration-range";
|
|
17
18
|
readonly serviceType: "service-type";
|
|
18
19
|
readonly serviceCategory: "service-category";
|
|
19
20
|
readonly serviceLocations: "service-locations";
|
|
@@ -340,6 +341,72 @@ export interface DurationInMinutesProps extends Omit<React.HTMLAttributes<HTMLEl
|
|
|
340
341
|
* ```
|
|
341
342
|
*/
|
|
342
343
|
export declare const DurationInMinutes: React.ForwardRefExoticComponent<DurationInMinutesProps & React.RefAttributes<HTMLElement>>;
|
|
344
|
+
/**
|
|
345
|
+
* Props for Service DurationRange component
|
|
346
|
+
*/
|
|
347
|
+
export interface DurationRangeProps extends Omit<React.HTMLAttributes<HTMLElement>, 'children'> {
|
|
348
|
+
asChild?: boolean;
|
|
349
|
+
children?: AsChildChildren<{
|
|
350
|
+
range: DurationRangeData;
|
|
351
|
+
}>;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Provides the service's supported session duration range as raw SDK data.
|
|
355
|
+
* Renders nothing when the service has fewer than two distinct supported
|
|
356
|
+
* durations — use {@link DurationInMinutes} for that case.
|
|
357
|
+
*
|
|
358
|
+
* The component is intentionally UI-agnostic: it does not pick formatting,
|
|
359
|
+
* units, or labels for you. The default render outputs only the raw
|
|
360
|
+
* `min - max` numbers (no unit text). Consumers wanting unit-aware UI use
|
|
361
|
+
* `asChild` with a custom render and narrow on the SDK `range` payload
|
|
362
|
+
* (`range.hourOptions` / `range.dayOptions` / `range.unitType`, or any
|
|
363
|
+
* other unit configuration the SDK adds in the future).
|
|
364
|
+
*
|
|
365
|
+
* @component
|
|
366
|
+
* @example
|
|
367
|
+
* ```tsx
|
|
368
|
+
* // Default usage - displays raw min/max, e.g. "120 - 540" or "2 - 5"
|
|
369
|
+
* <Service.DurationRange className="text-base text-secondary-foreground" />
|
|
370
|
+
* ```
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```tsx
|
|
374
|
+
* // asChild with primitive element
|
|
375
|
+
* <Service.DurationRange asChild>
|
|
376
|
+
* <span className="font-semibold" />
|
|
377
|
+
* </Service.DurationRange>
|
|
378
|
+
* ```
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```tsx
|
|
382
|
+
* // asChild with custom component - narrow on the SDK range payload to
|
|
383
|
+
* // decide unit-specific formatting.
|
|
384
|
+
* <Service.DurationRange asChild>
|
|
385
|
+
* {React.forwardRef(({ range, ...props }, ref) => {
|
|
386
|
+
* if (range.hourOptions) {
|
|
387
|
+
* const { minDurationInMinutes: min, maxDurationInMinutes: max } =
|
|
388
|
+
* range.hourOptions;
|
|
389
|
+
* return (
|
|
390
|
+
* <span ref={ref} {...props}>
|
|
391
|
+
* {min}-{max} min
|
|
392
|
+
* </span>
|
|
393
|
+
* );
|
|
394
|
+
* }
|
|
395
|
+
* if (range.dayOptions) {
|
|
396
|
+
* const { minDurationInDays: min, maxDurationInDays: max } =
|
|
397
|
+
* range.dayOptions;
|
|
398
|
+
* return (
|
|
399
|
+
* <span ref={ref} {...props}>
|
|
400
|
+
* {min}-{max} days
|
|
401
|
+
* </span>
|
|
402
|
+
* );
|
|
403
|
+
* }
|
|
404
|
+
* return <span ref={ref} {...props} />;
|
|
405
|
+
* })}
|
|
406
|
+
* </Service.DurationRange>
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
export declare const DurationRange: React.ForwardRefExoticComponent<DurationRangeProps & React.RefAttributes<HTMLElement>>;
|
|
343
410
|
/**
|
|
344
411
|
* Props for Service Type component
|
|
345
412
|
*/
|
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.CoverMedia = exports.MainMedia = exports.StaffMembers = exports.Locations = exports.AvailableOnline = exports.DefaultCapacity = exports.Schedule = exports.ScheduleSessionDuration = exports.ScheduleLastSessionEnd = exports.ScheduleFirstSessionStart = exports.Policy = exports.PolicyMaxParticipantsPerBooking = exports.PolicyBookAfterStartEnabled = exports.PolicyWaitlistCapacity = exports.PolicyLatestRescheduleInMinutes = exports.PolicyEarliestBookingInMinutes = exports.PolicyLatestBookingInMinutes = exports.PolicyLatestCancellationInMinutes = exports.Actions = exports.Raw = exports.StaffMemberRepeater = exports.StaffMembersList = exports.LocationRepeater = exports.LocationsList = exports.Category = exports.Type = exports.DurationInMinutes = exports.Tagline = exports.Price = exports.Description = exports.Name = exports.Root = exports.TestIds = void 0;
|
|
39
|
+
exports.CoverMedia = exports.MainMedia = exports.StaffMembers = exports.Locations = exports.AvailableOnline = exports.DefaultCapacity = exports.Schedule = exports.ScheduleSessionDuration = exports.ScheduleLastSessionEnd = exports.ScheduleFirstSessionStart = exports.Policy = exports.PolicyMaxParticipantsPerBooking = exports.PolicyBookAfterStartEnabled = exports.PolicyWaitlistCapacity = exports.PolicyLatestRescheduleInMinutes = exports.PolicyEarliestBookingInMinutes = exports.PolicyLatestBookingInMinutes = exports.PolicyLatestCancellationInMinutes = exports.Actions = exports.Raw = exports.StaffMemberRepeater = exports.StaffMembersList = exports.LocationRepeater = exports.LocationsList = exports.Category = exports.Type = exports.DurationRange = exports.DurationInMinutes = exports.Tagline = exports.Price = exports.Description = exports.Name = exports.Root = exports.TestIds = void 0;
|
|
40
40
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
41
41
|
/**
|
|
42
42
|
* Service - High-level component for displaying service information
|
|
@@ -63,6 +63,7 @@ exports.TestIds = {
|
|
|
63
63
|
servicePrice: 'service-price',
|
|
64
64
|
serviceTagline: 'service-tagline',
|
|
65
65
|
serviceDurationInMinutes: 'service-duration-in-minutes',
|
|
66
|
+
serviceDurationRange: 'service-duration-range',
|
|
66
67
|
serviceType: 'service-type',
|
|
67
68
|
serviceCategory: 'service-category',
|
|
68
69
|
serviceLocations: 'service-locations',
|
|
@@ -341,6 +342,85 @@ exports.DurationInMinutes = react_1.default.forwardRef((props, ref) => {
|
|
|
341
342
|
} }));
|
|
342
343
|
});
|
|
343
344
|
exports.DurationInMinutes.displayName = 'Service.DurationInMinutes';
|
|
345
|
+
/**
|
|
346
|
+
* Provides the service's supported session duration range as raw SDK data.
|
|
347
|
+
* Renders nothing when the service has fewer than two distinct supported
|
|
348
|
+
* durations — use {@link DurationInMinutes} for that case.
|
|
349
|
+
*
|
|
350
|
+
* The component is intentionally UI-agnostic: it does not pick formatting,
|
|
351
|
+
* units, or labels for you. The default render outputs only the raw
|
|
352
|
+
* `min - max` numbers (no unit text). Consumers wanting unit-aware UI use
|
|
353
|
+
* `asChild` with a custom render and narrow on the SDK `range` payload
|
|
354
|
+
* (`range.hourOptions` / `range.dayOptions` / `range.unitType`, or any
|
|
355
|
+
* other unit configuration the SDK adds in the future).
|
|
356
|
+
*
|
|
357
|
+
* @component
|
|
358
|
+
* @example
|
|
359
|
+
* ```tsx
|
|
360
|
+
* // Default usage - displays raw min/max, e.g. "120 - 540" or "2 - 5"
|
|
361
|
+
* <Service.DurationRange className="text-base text-secondary-foreground" />
|
|
362
|
+
* ```
|
|
363
|
+
*
|
|
364
|
+
* @example
|
|
365
|
+
* ```tsx
|
|
366
|
+
* // asChild with primitive element
|
|
367
|
+
* <Service.DurationRange asChild>
|
|
368
|
+
* <span className="font-semibold" />
|
|
369
|
+
* </Service.DurationRange>
|
|
370
|
+
* ```
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```tsx
|
|
374
|
+
* // asChild with custom component - narrow on the SDK range payload to
|
|
375
|
+
* // decide unit-specific formatting.
|
|
376
|
+
* <Service.DurationRange asChild>
|
|
377
|
+
* {React.forwardRef(({ range, ...props }, ref) => {
|
|
378
|
+
* if (range.hourOptions) {
|
|
379
|
+
* const { minDurationInMinutes: min, maxDurationInMinutes: max } =
|
|
380
|
+
* range.hourOptions;
|
|
381
|
+
* return (
|
|
382
|
+
* <span ref={ref} {...props}>
|
|
383
|
+
* {min}-{max} min
|
|
384
|
+
* </span>
|
|
385
|
+
* );
|
|
386
|
+
* }
|
|
387
|
+
* if (range.dayOptions) {
|
|
388
|
+
* const { minDurationInDays: min, maxDurationInDays: max } =
|
|
389
|
+
* range.dayOptions;
|
|
390
|
+
* return (
|
|
391
|
+
* <span ref={ref} {...props}>
|
|
392
|
+
* {min}-{max} days
|
|
393
|
+
* </span>
|
|
394
|
+
* );
|
|
395
|
+
* }
|
|
396
|
+
* return <span ref={ref} {...props} />;
|
|
397
|
+
* })}
|
|
398
|
+
* </Service.DurationRange>
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
exports.DurationRange = react_1.default.forwardRef((props, ref) => {
|
|
402
|
+
const { asChild, children, ...rest } = props;
|
|
403
|
+
return ((0, jsx_runtime_1.jsx)(CoreService.Info, { children: ({ durationRange }) => {
|
|
404
|
+
if (!durationRange)
|
|
405
|
+
return null;
|
|
406
|
+
// When `unitType` is missing or unrecognised, no default content is
|
|
407
|
+
// rendered — consumers can still read the raw `range` via asChild.
|
|
408
|
+
const { unitType, hourOptions, dayOptions } = durationRange;
|
|
409
|
+
let min;
|
|
410
|
+
let max;
|
|
411
|
+
if (unitType === auto_sdk_bookings_services_1.UnitType.HOUR) {
|
|
412
|
+
min = hourOptions?.minDurationInMinutes;
|
|
413
|
+
max = hourOptions?.maxDurationInMinutes;
|
|
414
|
+
}
|
|
415
|
+
else if (unitType === auto_sdk_bookings_services_1.UnitType.DAY) {
|
|
416
|
+
min = dayOptions?.minDurationInDays;
|
|
417
|
+
max = dayOptions?.maxDurationInDays;
|
|
418
|
+
}
|
|
419
|
+
const hasRange = min != null && max != null;
|
|
420
|
+
return ((0, jsx_runtime_1.jsx)(react_2.AsChildSlot, { ref: ref, asChild: asChild, ...rest, "data-testid": exports.TestIds.serviceDurationRange, customElement: children, customElementProps: { range: durationRange }, children: hasRange ? (0, jsx_runtime_1.jsx)("span", { children: `${min} - ${max}` }) : null }));
|
|
421
|
+
} }));
|
|
422
|
+
});
|
|
423
|
+
exports.DurationRange.displayName = 'Service.DurationRange';
|
|
344
424
|
/**
|
|
345
425
|
* Displays the service type (APPOINTMENT, CLASS, COURSE, etc.) with customizable rendering.
|
|
346
426
|
*
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Persists across page navigation in SPA until page refresh
|
|
5
5
|
*/
|
|
6
6
|
import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
7
|
-
import type { Service,
|
|
7
|
+
import type { Service, V2Location as ServiceLocationType } from '@wix/auto_sdk_bookings_services';
|
|
8
8
|
import type { Resource, EventInfo, Location as TimeSlotLocationType } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
9
9
|
import type { FormValues } from '@wix/forms/components';
|
|
10
10
|
import type { BookResult } from './book-action/types.js';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module Services/LocationList/Definition
|
|
6
6
|
*/
|
|
7
7
|
import type { Signal, ReadOnlySignal } from '@wix/services-definitions/core-services/signals';
|
|
8
|
-
import { LocationType, type Service } from '@wix/auto_sdk_bookings_services';
|
|
8
|
+
import { LocationTypeEnumLocationType as LocationType, type Service } from '@wix/auto_sdk_bookings_services';
|
|
9
9
|
/**
|
|
10
10
|
* Location type from the SDK
|
|
11
11
|
* Represents a service location (BUSINESS, CUSTOM, or CUSTOMER type)
|
|
@@ -9,7 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
exports.LocationType = exports.LocationListServiceDefinition = void 0;
|
|
10
10
|
const services_definitions_1 = require("@wix/services-definitions");
|
|
11
11
|
const auto_sdk_bookings_services_1 = require("@wix/auto_sdk_bookings_services");
|
|
12
|
-
Object.defineProperty(exports, "LocationType", { enumerable: true, get: function () { return auto_sdk_bookings_services_1.
|
|
12
|
+
Object.defineProperty(exports, "LocationType", { enumerable: true, get: function () { return auto_sdk_bookings_services_1.LocationTypeEnumLocationType; } });
|
|
13
13
|
// ============================================================================
|
|
14
14
|
// Service Definition
|
|
15
15
|
// ============================================================================
|
|
@@ -35,13 +35,13 @@ const SYNTHETIC_CUSTOMER_ID = 'customer';
|
|
|
35
35
|
function buildDisplayLocations(locations, hasCustomLocations, hasCustomerLocations) {
|
|
36
36
|
const displayLocations = [];
|
|
37
37
|
// 1. Add all BUSINESS locations directly
|
|
38
|
-
const businessLocations = locations.filter((loc) => loc.type === auto_sdk_bookings_services_1.
|
|
38
|
+
const businessLocations = locations.filter((loc) => loc.type === auto_sdk_bookings_services_1.LocationTypeEnumLocationType.BUSINESS || loc.type === undefined);
|
|
39
39
|
for (const loc of businessLocations) {
|
|
40
40
|
displayLocations.push(loc);
|
|
41
41
|
}
|
|
42
42
|
// Check for existing CUSTOM/CUSTOMER in array
|
|
43
|
-
const customFromArray = locations.find((loc) => loc.type === auto_sdk_bookings_services_1.
|
|
44
|
-
const customerFromArray = locations.find((loc) => loc.type === auto_sdk_bookings_services_1.
|
|
43
|
+
const customFromArray = locations.find((loc) => loc.type === auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOM);
|
|
44
|
+
const customerFromArray = locations.find((loc) => loc.type === auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOMER);
|
|
45
45
|
// 2. Add CUSTOM location (from array, or create placeholder)
|
|
46
46
|
if (customFromArray) {
|
|
47
47
|
displayLocations.push(customFromArray);
|
|
@@ -49,7 +49,7 @@ function buildDisplayLocations(locations, hasCustomLocations, hasCustomerLocatio
|
|
|
49
49
|
else if (hasCustomLocations) {
|
|
50
50
|
// Placeholder custom location (only from explicit flag, not array)
|
|
51
51
|
const placeholderCustom = {
|
|
52
|
-
type: auto_sdk_bookings_services_1.
|
|
52
|
+
type: auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOM,
|
|
53
53
|
_id: SYNTHETIC_CUSTOM_ID,
|
|
54
54
|
};
|
|
55
55
|
displayLocations.push(placeholderCustom);
|
|
@@ -61,7 +61,7 @@ function buildDisplayLocations(locations, hasCustomLocations, hasCustomerLocatio
|
|
|
61
61
|
else if (hasCustomerLocations) {
|
|
62
62
|
// Placeholder customer location (only from explicit flag, not array)
|
|
63
63
|
const placeholderCustomer = {
|
|
64
|
-
type: auto_sdk_bookings_services_1.
|
|
64
|
+
type: auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOMER,
|
|
65
65
|
_id: SYNTHETIC_CUSTOMER_ID,
|
|
66
66
|
};
|
|
67
67
|
displayLocations.push(placeholderCustomer);
|
|
@@ -89,8 +89,8 @@ exports.LocationListService = services_definitions_1.implementService.withConfig
|
|
|
89
89
|
// Initialize raw locations signal
|
|
90
90
|
const locationsSignal = signalsService.signal(config.locations || []);
|
|
91
91
|
// Auto-detect from array
|
|
92
|
-
const hasCustomInArray = (config.locations || []).some((loc) => loc.type === auto_sdk_bookings_services_1.
|
|
93
|
-
const hasCustomerInArray = (config.locations || []).some((loc) => loc.type === auto_sdk_bookings_services_1.
|
|
92
|
+
const hasCustomInArray = (config.locations || []).some((loc) => loc.type === auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOM);
|
|
93
|
+
const hasCustomerInArray = (config.locations || []).some((loc) => loc.type === auto_sdk_bookings_services_1.LocationTypeEnumLocationType.CUSTOMER);
|
|
94
94
|
// Final flags: explicit config OR auto-detected
|
|
95
95
|
const hasCustomLocationsSignal = signalsService.computed(() => config.hasCustomLocations ?? hasCustomInArray);
|
|
96
96
|
const hasCustomerLocationsSignal = signalsService.computed(() => config.hasCustomerLocations ?? hasCustomerInArray);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FormValues } from '@wix/forms/components';
|
|
2
|
-
import type { Location } from '@wix/auto_sdk_bookings_services';
|
|
2
|
+
import type { V2Location as Location } from '@wix/auto_sdk_bookings_services';
|
|
3
3
|
import type { ServiceSelection, ServiceSelectionTimeSlot, ExtendedResource } from '../../services/booking/booking.js';
|
|
4
4
|
export declare function createMockResource(overrides?: Partial<ExtendedResource>): ExtendedResource;
|
|
5
5
|
export declare function createMockLocation(overrides?: Partial<Location>): Location;
|
|
@@ -35,6 +35,29 @@ export declare const mockServiceCourse: Service;
|
|
|
35
35
|
* Mock service without optional fields (minimal)
|
|
36
36
|
*/
|
|
37
37
|
export declare const mockServiceMinimal: Service;
|
|
38
|
+
/**
|
|
39
|
+
* Mock service driving the fixed-duration path: a single value in
|
|
40
|
+
* `sessionDurations`, no `durationRange`. Drives `Service.DurationInMinutes`;
|
|
41
|
+
* `Service.DurationRange` renders nothing for this shape.
|
|
42
|
+
*
|
|
43
|
+
* `mockService` already carries this shape — this is a renamed copy for
|
|
44
|
+
* tests/examples that want to name the case they're exercising.
|
|
45
|
+
*/
|
|
46
|
+
export declare const mockServiceFixedDuration: Service;
|
|
47
|
+
/**
|
|
48
|
+
* Mock service driving the HOUR-unit DurationRange path. Mirrors the server
|
|
49
|
+
* payload shape: empty `sessionDurations`, range pre-computed at
|
|
50
|
+
* `availabilityConstraints.durationRange`. `durationRange` is not yet in the
|
|
51
|
+
* SDK's `AvailabilityConstraints` typing (runtime is ahead of types), so the
|
|
52
|
+
* service object is cast — matches the read-side cast in
|
|
53
|
+
* `core/service/Service.tsx`.
|
|
54
|
+
*/
|
|
55
|
+
export declare const mockServiceHourRange: Service;
|
|
56
|
+
/**
|
|
57
|
+
* Mock service driving the DAY-unit DurationRange path. Same server shape as
|
|
58
|
+
* {@link mockServiceHourRange}.
|
|
59
|
+
*/
|
|
60
|
+
export declare const mockServiceDayRange: Service;
|
|
38
61
|
/**
|
|
39
62
|
* Array of mock services for list tests
|
|
40
63
|
*/
|
|
@@ -170,6 +170,70 @@ export const mockServiceMinimal = {
|
|
|
170
170
|
name: 'Minimal Service',
|
|
171
171
|
description: '',
|
|
172
172
|
};
|
|
173
|
+
/**
|
|
174
|
+
* Mock service driving the fixed-duration path: a single value in
|
|
175
|
+
* `sessionDurations`, no `durationRange`. Drives `Service.DurationInMinutes`;
|
|
176
|
+
* `Service.DurationRange` renders nothing for this shape.
|
|
177
|
+
*
|
|
178
|
+
* `mockService` already carries this shape — this is a renamed copy for
|
|
179
|
+
* tests/examples that want to name the case they're exercising.
|
|
180
|
+
*/
|
|
181
|
+
export const mockServiceFixedDuration = {
|
|
182
|
+
...mockService,
|
|
183
|
+
_id: 'test-service-fixed-duration',
|
|
184
|
+
name: 'Fixed Duration Service',
|
|
185
|
+
schedule: {
|
|
186
|
+
availabilityConstraints: {
|
|
187
|
+
sessionDurations: [60],
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Mock service driving the HOUR-unit DurationRange path. Mirrors the server
|
|
193
|
+
* payload shape: empty `sessionDurations`, range pre-computed at
|
|
194
|
+
* `availabilityConstraints.durationRange`. `durationRange` is not yet in the
|
|
195
|
+
* SDK's `AvailabilityConstraints` typing (runtime is ahead of types), so the
|
|
196
|
+
* service object is cast — matches the read-side cast in
|
|
197
|
+
* `core/service/Service.tsx`.
|
|
198
|
+
*/
|
|
199
|
+
export const mockServiceHourRange = {
|
|
200
|
+
...mockService,
|
|
201
|
+
_id: 'test-service-hour-range',
|
|
202
|
+
name: 'Hourly Range Service',
|
|
203
|
+
schedule: {
|
|
204
|
+
availabilityConstraints: {
|
|
205
|
+
sessionDurations: [],
|
|
206
|
+
durationRange: {
|
|
207
|
+
unitType: 'HOUR',
|
|
208
|
+
hourOptions: {
|
|
209
|
+
minDurationInMinutes: 120,
|
|
210
|
+
maxDurationInMinutes: 540,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Mock service driving the DAY-unit DurationRange path. Same server shape as
|
|
218
|
+
* {@link mockServiceHourRange}.
|
|
219
|
+
*/
|
|
220
|
+
export const mockServiceDayRange = {
|
|
221
|
+
...mockService,
|
|
222
|
+
_id: 'test-service-day-range',
|
|
223
|
+
name: 'Daily Range Service',
|
|
224
|
+
schedule: {
|
|
225
|
+
availabilityConstraints: {
|
|
226
|
+
sessionDurations: [],
|
|
227
|
+
durationRange: {
|
|
228
|
+
unitType: 'DAY',
|
|
229
|
+
dayOptions: {
|
|
230
|
+
minDurationInDays: 2,
|
|
231
|
+
maxDurationInDays: 5,
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
};
|
|
173
237
|
/**
|
|
174
238
|
* Array of mock services for list tests
|
|
175
239
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Query Services API
|
|
3
3
|
* Fetches single or multiple services with pagination and sorting
|
|
4
4
|
*/
|
|
5
|
-
import { queryServices as queryServicesApi, LocationType, } from '@wix/auto_sdk_bookings_services';
|
|
5
|
+
import { queryServices as queryServicesApi, LocationTypeEnumLocationType as LocationType, } from '@wix/auto_sdk_bookings_services';
|
|
6
6
|
import { BOOKING_APP_ID } from '../../services/constants.js';
|
|
7
7
|
/**
|
|
8
8
|
* Synthetic location ID constants for non-business locations
|
|
@@ -6,7 +6,7 @@ import React from 'react';
|
|
|
6
6
|
import { type BookingServiceConfig, type ServiceSelection, type BookingServiceActions } from '../../../services/booking/booking.js';
|
|
7
7
|
import type { FormValues } from '@wix/forms/components';
|
|
8
8
|
import type { TimeSlot, Resource } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
9
|
-
import { type Service, type
|
|
9
|
+
import { type Service, type V2Location as ServiceLocationType } from '@wix/auto_sdk_bookings_services';
|
|
10
10
|
import type { PricingServiceSelection } from '../../../services/payment/payment.js';
|
|
11
11
|
/**
|
|
12
12
|
* Props for Booking.Root component
|
|
@@ -7,7 +7,7 @@ import React from 'react';
|
|
|
7
7
|
import { WixServices, useService } from '@wix/services-manager-react';
|
|
8
8
|
import { createServicesMap } from '@wix/services-manager';
|
|
9
9
|
import { BookingServiceDefinition, BookingService, } from '../../../services/booking/booking.js';
|
|
10
|
-
import { LocationType, } from '@wix/auto_sdk_bookings_services';
|
|
10
|
+
import { LocationTypeEnumLocationType as LocationType, } from '@wix/auto_sdk_bookings_services';
|
|
11
11
|
/**
|
|
12
12
|
* Root component that provides booking context to the entire app.
|
|
13
13
|
*
|
|
@@ -53,7 +53,16 @@ export function Root(props) {
|
|
|
53
53
|
function mapTimeSlotLocationToServiceLocation(timeSlotLocation) {
|
|
54
54
|
// TimeSlot location has flat name/formattedAddress at top level
|
|
55
55
|
// Convert to nested business/custom structure that CoreLocation expects
|
|
56
|
-
const
|
|
56
|
+
const isCustomer = timeSlotLocation.locationType === LocationType.CUSTOMER;
|
|
57
|
+
const isCustom = timeSlotLocation.locationType === LocationType.CUSTOM;
|
|
58
|
+
// The two SDKs share BUSINESS/CUSTOM/CUSTOMER but the time-slots enum also
|
|
59
|
+
// exposes UNKNOWN_LOCATION_TYPE, which V2Location.type does not accept.
|
|
60
|
+
// Default to BUSINESS for any unrecognized value.
|
|
61
|
+
const type = isCustomer
|
|
62
|
+
? LocationType.CUSTOMER
|
|
63
|
+
: isCustom
|
|
64
|
+
? LocationType.CUSTOM
|
|
65
|
+
: LocationType.BUSINESS;
|
|
57
66
|
const location = {
|
|
58
67
|
_id: timeSlotLocation._id ?? undefined,
|
|
59
68
|
name: timeSlotLocation.name ?? undefined,
|
|
@@ -65,10 +74,10 @@ function mapTimeSlotLocationToServiceLocation(timeSlotLocation) {
|
|
|
65
74
|
};
|
|
66
75
|
return {
|
|
67
76
|
_id: timeSlotLocation._id ?? undefined,
|
|
68
|
-
type
|
|
69
|
-
...(
|
|
77
|
+
type,
|
|
78
|
+
...(isCustomer
|
|
70
79
|
? null
|
|
71
|
-
:
|
|
80
|
+
: isCustom
|
|
72
81
|
? { custom: location }
|
|
73
82
|
: { business: location }),
|
|
74
83
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module Core/Location
|
|
7
7
|
*/
|
|
8
8
|
import React from 'react';
|
|
9
|
-
import { LocationType } from '@wix/auto_sdk_bookings_services';
|
|
9
|
+
import { LocationTypeEnumLocationType as LocationType } from '@wix/auto_sdk_bookings_services';
|
|
10
10
|
import type { AddressData as AddressDataType } from '@wix/headless-components/react';
|
|
11
11
|
import type { Location } from '../../../services/location-list/location-list.def.js';
|
|
12
12
|
/**
|
|
@@ -8,7 +8,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
8
8
|
*/
|
|
9
9
|
import React, { createContext, useContext } from 'react';
|
|
10
10
|
import { useService } from '@wix/services-manager-react';
|
|
11
|
-
import { LocationType } from '@wix/auto_sdk_bookings_services';
|
|
11
|
+
import { LocationTypeEnumLocationType as LocationType } from '@wix/auto_sdk_bookings_services';
|
|
12
12
|
import { BookingServiceDefinition } from '../../../services/booking/booking.js';
|
|
13
13
|
import { LocationListServiceDefinition } from '../../../services/location-list/location-list.def.js';
|
|
14
14
|
// ==========================================
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { type ServiceServiceConfig } from '../../../services/service/service.js';
|
|
7
|
-
import type { Service, Payment, BookingPolicy, Schedule, StaffMember } from '@wix/auto_sdk_bookings_services';
|
|
7
|
+
import type { Service, Payment, BookingPolicy, Schedule, StaffMember, DurationRange } from '@wix/auto_sdk_bookings_services';
|
|
8
8
|
import type { MoneyData } from '@wix/headless-components/react';
|
|
9
9
|
/**
|
|
10
10
|
* Props for Service.Root component
|
|
@@ -50,6 +50,15 @@ export interface InfoProps {
|
|
|
50
50
|
description: string;
|
|
51
51
|
tagline?: string;
|
|
52
52
|
duration?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Typed range of supported session durations, derived from
|
|
55
|
+
* `service.schedule.availabilityConstraints.sessionDurations`. Set when the
|
|
56
|
+
* service supports two or more distinct durations; otherwise undefined and
|
|
57
|
+
* the single-value `duration` field above should be used. Consumers branch
|
|
58
|
+
* on `range.unitType` (the SDK `UnitType` enum) and read the matching
|
|
59
|
+
* `range.hourOptions` / `range.dayOptions` config.
|
|
60
|
+
*/
|
|
61
|
+
durationRange?: DurationRange;
|
|
53
62
|
type?: string;
|
|
54
63
|
category?: {
|
|
55
64
|
name?: string;
|
|
@@ -87,9 +87,9 @@ export function Info(props) {
|
|
|
87
87
|
currency,
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
90
|
+
const constraints = service.schedule?.availabilityConstraints;
|
|
91
|
+
const duration = constraints?.sessionDurations?.[0];
|
|
92
|
+
const durationRange = constraints?.durationRange;
|
|
93
93
|
// Get staff members from staffMemberDetails.staffMembers
|
|
94
94
|
// (available when using conditionalFields: ['STAFF_MEMBER_DETAILS'] in queryServices)
|
|
95
95
|
const staff = service.staffMembers || [];
|
|
@@ -119,7 +119,8 @@ export function Info(props) {
|
|
|
119
119
|
name: service.name || '',
|
|
120
120
|
description: service.description || '',
|
|
121
121
|
tagline: service.tagLine || undefined,
|
|
122
|
-
duration
|
|
122
|
+
duration,
|
|
123
|
+
durationRange,
|
|
123
124
|
type: service.type,
|
|
124
125
|
category: service.category
|
|
125
126
|
? {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { AsChildChildren } from '@wix/headless-utils/react';
|
|
7
7
|
import { MoneyData } from '@wix/headless-components/react';
|
|
8
|
-
import { type Service, type Payment } from '@wix/auto_sdk_bookings_services';
|
|
8
|
+
import { type Service, type Payment, type DurationRange as DurationRangeData } from '@wix/auto_sdk_bookings_services';
|
|
9
9
|
import * as ServiceMediaModule from './ServiceMedia.js';
|
|
10
10
|
export declare const TestIds: {
|
|
11
11
|
readonly serviceRoot: "service-root";
|
|
@@ -14,6 +14,7 @@ export declare const TestIds: {
|
|
|
14
14
|
readonly servicePrice: "service-price";
|
|
15
15
|
readonly serviceTagline: "service-tagline";
|
|
16
16
|
readonly serviceDurationInMinutes: "service-duration-in-minutes";
|
|
17
|
+
readonly serviceDurationRange: "service-duration-range";
|
|
17
18
|
readonly serviceType: "service-type";
|
|
18
19
|
readonly serviceCategory: "service-category";
|
|
19
20
|
readonly serviceLocations: "service-locations";
|
|
@@ -340,6 +341,72 @@ export interface DurationInMinutesProps extends Omit<React.HTMLAttributes<HTMLEl
|
|
|
340
341
|
* ```
|
|
341
342
|
*/
|
|
342
343
|
export declare const DurationInMinutes: React.ForwardRefExoticComponent<DurationInMinutesProps & React.RefAttributes<HTMLElement>>;
|
|
344
|
+
/**
|
|
345
|
+
* Props for Service DurationRange component
|
|
346
|
+
*/
|
|
347
|
+
export interface DurationRangeProps extends Omit<React.HTMLAttributes<HTMLElement>, 'children'> {
|
|
348
|
+
asChild?: boolean;
|
|
349
|
+
children?: AsChildChildren<{
|
|
350
|
+
range: DurationRangeData;
|
|
351
|
+
}>;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Provides the service's supported session duration range as raw SDK data.
|
|
355
|
+
* Renders nothing when the service has fewer than two distinct supported
|
|
356
|
+
* durations — use {@link DurationInMinutes} for that case.
|
|
357
|
+
*
|
|
358
|
+
* The component is intentionally UI-agnostic: it does not pick formatting,
|
|
359
|
+
* units, or labels for you. The default render outputs only the raw
|
|
360
|
+
* `min - max` numbers (no unit text). Consumers wanting unit-aware UI use
|
|
361
|
+
* `asChild` with a custom render and narrow on the SDK `range` payload
|
|
362
|
+
* (`range.hourOptions` / `range.dayOptions` / `range.unitType`, or any
|
|
363
|
+
* other unit configuration the SDK adds in the future).
|
|
364
|
+
*
|
|
365
|
+
* @component
|
|
366
|
+
* @example
|
|
367
|
+
* ```tsx
|
|
368
|
+
* // Default usage - displays raw min/max, e.g. "120 - 540" or "2 - 5"
|
|
369
|
+
* <Service.DurationRange className="text-base text-secondary-foreground" />
|
|
370
|
+
* ```
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```tsx
|
|
374
|
+
* // asChild with primitive element
|
|
375
|
+
* <Service.DurationRange asChild>
|
|
376
|
+
* <span className="font-semibold" />
|
|
377
|
+
* </Service.DurationRange>
|
|
378
|
+
* ```
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```tsx
|
|
382
|
+
* // asChild with custom component - narrow on the SDK range payload to
|
|
383
|
+
* // decide unit-specific formatting.
|
|
384
|
+
* <Service.DurationRange asChild>
|
|
385
|
+
* {React.forwardRef(({ range, ...props }, ref) => {
|
|
386
|
+
* if (range.hourOptions) {
|
|
387
|
+
* const { minDurationInMinutes: min, maxDurationInMinutes: max } =
|
|
388
|
+
* range.hourOptions;
|
|
389
|
+
* return (
|
|
390
|
+
* <span ref={ref} {...props}>
|
|
391
|
+
* {min}-{max} min
|
|
392
|
+
* </span>
|
|
393
|
+
* );
|
|
394
|
+
* }
|
|
395
|
+
* if (range.dayOptions) {
|
|
396
|
+
* const { minDurationInDays: min, maxDurationInDays: max } =
|
|
397
|
+
* range.dayOptions;
|
|
398
|
+
* return (
|
|
399
|
+
* <span ref={ref} {...props}>
|
|
400
|
+
* {min}-{max} days
|
|
401
|
+
* </span>
|
|
402
|
+
* );
|
|
403
|
+
* }
|
|
404
|
+
* return <span ref={ref} {...props} />;
|
|
405
|
+
* })}
|
|
406
|
+
* </Service.DurationRange>
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
export declare const DurationRange: React.ForwardRefExoticComponent<DurationRangeProps & React.RefAttributes<HTMLElement>>;
|
|
343
410
|
/**
|
|
344
411
|
* Props for Service Type component
|
|
345
412
|
*/
|
|
@@ -9,7 +9,7 @@ import * as CoreServiceMediaGallery from '../core/service/ServiceMediaGallery.js
|
|
|
9
9
|
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
10
10
|
import { Money } from '@wix/headless-components/react';
|
|
11
11
|
import { MediaGallery } from '@wix/headless-media/react';
|
|
12
|
-
import { RateType, } from '@wix/auto_sdk_bookings_services';
|
|
12
|
+
import { RateType, UnitType, } from '@wix/auto_sdk_bookings_services';
|
|
13
13
|
import * as LocationList from '../location/LocationList.js';
|
|
14
14
|
// Note: Location components are used inside LocationList.LocationRepeater
|
|
15
15
|
// Users should import { Location } from '@wix/headless-bookings/react' for Location.Name, Location.Address, etc.
|
|
@@ -24,6 +24,7 @@ export const TestIds = {
|
|
|
24
24
|
servicePrice: 'service-price',
|
|
25
25
|
serviceTagline: 'service-tagline',
|
|
26
26
|
serviceDurationInMinutes: 'service-duration-in-minutes',
|
|
27
|
+
serviceDurationRange: 'service-duration-range',
|
|
27
28
|
serviceType: 'service-type',
|
|
28
29
|
serviceCategory: 'service-category',
|
|
29
30
|
serviceLocations: 'service-locations',
|
|
@@ -302,6 +303,85 @@ export const DurationInMinutes = React.forwardRef((props, ref) => {
|
|
|
302
303
|
} }));
|
|
303
304
|
});
|
|
304
305
|
DurationInMinutes.displayName = 'Service.DurationInMinutes';
|
|
306
|
+
/**
|
|
307
|
+
* Provides the service's supported session duration range as raw SDK data.
|
|
308
|
+
* Renders nothing when the service has fewer than two distinct supported
|
|
309
|
+
* durations — use {@link DurationInMinutes} for that case.
|
|
310
|
+
*
|
|
311
|
+
* The component is intentionally UI-agnostic: it does not pick formatting,
|
|
312
|
+
* units, or labels for you. The default render outputs only the raw
|
|
313
|
+
* `min - max` numbers (no unit text). Consumers wanting unit-aware UI use
|
|
314
|
+
* `asChild` with a custom render and narrow on the SDK `range` payload
|
|
315
|
+
* (`range.hourOptions` / `range.dayOptions` / `range.unitType`, or any
|
|
316
|
+
* other unit configuration the SDK adds in the future).
|
|
317
|
+
*
|
|
318
|
+
* @component
|
|
319
|
+
* @example
|
|
320
|
+
* ```tsx
|
|
321
|
+
* // Default usage - displays raw min/max, e.g. "120 - 540" or "2 - 5"
|
|
322
|
+
* <Service.DurationRange className="text-base text-secondary-foreground" />
|
|
323
|
+
* ```
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```tsx
|
|
327
|
+
* // asChild with primitive element
|
|
328
|
+
* <Service.DurationRange asChild>
|
|
329
|
+
* <span className="font-semibold" />
|
|
330
|
+
* </Service.DurationRange>
|
|
331
|
+
* ```
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```tsx
|
|
335
|
+
* // asChild with custom component - narrow on the SDK range payload to
|
|
336
|
+
* // decide unit-specific formatting.
|
|
337
|
+
* <Service.DurationRange asChild>
|
|
338
|
+
* {React.forwardRef(({ range, ...props }, ref) => {
|
|
339
|
+
* if (range.hourOptions) {
|
|
340
|
+
* const { minDurationInMinutes: min, maxDurationInMinutes: max } =
|
|
341
|
+
* range.hourOptions;
|
|
342
|
+
* return (
|
|
343
|
+
* <span ref={ref} {...props}>
|
|
344
|
+
* {min}-{max} min
|
|
345
|
+
* </span>
|
|
346
|
+
* );
|
|
347
|
+
* }
|
|
348
|
+
* if (range.dayOptions) {
|
|
349
|
+
* const { minDurationInDays: min, maxDurationInDays: max } =
|
|
350
|
+
* range.dayOptions;
|
|
351
|
+
* return (
|
|
352
|
+
* <span ref={ref} {...props}>
|
|
353
|
+
* {min}-{max} days
|
|
354
|
+
* </span>
|
|
355
|
+
* );
|
|
356
|
+
* }
|
|
357
|
+
* return <span ref={ref} {...props} />;
|
|
358
|
+
* })}
|
|
359
|
+
* </Service.DurationRange>
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
export const DurationRange = React.forwardRef((props, ref) => {
|
|
363
|
+
const { asChild, children, ...rest } = props;
|
|
364
|
+
return (_jsx(CoreService.Info, { children: ({ durationRange }) => {
|
|
365
|
+
if (!durationRange)
|
|
366
|
+
return null;
|
|
367
|
+
// When `unitType` is missing or unrecognised, no default content is
|
|
368
|
+
// rendered — consumers can still read the raw `range` via asChild.
|
|
369
|
+
const { unitType, hourOptions, dayOptions } = durationRange;
|
|
370
|
+
let min;
|
|
371
|
+
let max;
|
|
372
|
+
if (unitType === UnitType.HOUR) {
|
|
373
|
+
min = hourOptions?.minDurationInMinutes;
|
|
374
|
+
max = hourOptions?.maxDurationInMinutes;
|
|
375
|
+
}
|
|
376
|
+
else if (unitType === UnitType.DAY) {
|
|
377
|
+
min = dayOptions?.minDurationInDays;
|
|
378
|
+
max = dayOptions?.maxDurationInDays;
|
|
379
|
+
}
|
|
380
|
+
const hasRange = min != null && max != null;
|
|
381
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, ...rest, "data-testid": TestIds.serviceDurationRange, customElement: children, customElementProps: { range: durationRange }, children: hasRange ? _jsx("span", { children: `${min} - ${max}` }) : null }));
|
|
382
|
+
} }));
|
|
383
|
+
});
|
|
384
|
+
DurationRange.displayName = 'Service.DurationRange';
|
|
305
385
|
/**
|
|
306
386
|
* Displays the service type (APPOINTMENT, CLASS, COURSE, etc.) with customizable rendering.
|
|
307
387
|
*
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Persists across page navigation in SPA until page refresh
|
|
5
5
|
*/
|
|
6
6
|
import { type Signal } from '@wix/services-definitions/core-services/signals';
|
|
7
|
-
import type { Service,
|
|
7
|
+
import type { Service, V2Location as ServiceLocationType } from '@wix/auto_sdk_bookings_services';
|
|
8
8
|
import type { Resource, EventInfo, Location as TimeSlotLocationType } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
9
9
|
import type { FormValues } from '@wix/forms/components';
|
|
10
10
|
import type { BookResult } from './book-action/types.js';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module Services/LocationList/Definition
|
|
6
6
|
*/
|
|
7
7
|
import type { Signal, ReadOnlySignal } from '@wix/services-definitions/core-services/signals';
|
|
8
|
-
import { LocationType, type Service } from '@wix/auto_sdk_bookings_services';
|
|
8
|
+
import { LocationTypeEnumLocationType as LocationType, type Service } from '@wix/auto_sdk_bookings_services';
|
|
9
9
|
/**
|
|
10
10
|
* Location type from the SDK
|
|
11
11
|
* Represents a service location (BUSINESS, CUSTOM, or CUSTOMER type)
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module Services/LocationList/Definition
|
|
6
6
|
*/
|
|
7
7
|
import { defineService } from '@wix/services-definitions';
|
|
8
|
-
import { LocationType } from '@wix/auto_sdk_bookings_services';
|
|
8
|
+
import { LocationTypeEnumLocationType as LocationType, } from '@wix/auto_sdk_bookings_services';
|
|
9
9
|
// ============================================================================
|
|
10
10
|
// Service Definition
|
|
11
11
|
// ============================================================================
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { implementService } from '@wix/services-definitions';
|
|
8
8
|
import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
|
|
9
|
-
import { LocationType } from '@wix/auto_sdk_bookings_services';
|
|
9
|
+
import { LocationTypeEnumLocationType as LocationType } from '@wix/auto_sdk_bookings_services';
|
|
10
10
|
import { LocationListServiceDefinition, } from './location-list.def.js';
|
|
11
11
|
import { BookingServiceDefinition } from '../booking/booking.js';
|
|
12
12
|
import { queryLocations } from '../../api/query-locations/index.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/headless-bookings",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
@@ -34,8 +34,9 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@wix/auto_sdk_bookings_bookings": "^1.0.
|
|
37
|
+
"@wix/auto_sdk_bookings_bookings": "^1.0.183",
|
|
38
38
|
"@wix/auto_sdk_bookings_event-time-slots": "^1.0.64",
|
|
39
|
+
"@wix/auto_sdk_bookings_services": "^1.0.226",
|
|
39
40
|
"@wix/auto_sdk_ecom_cart-v-2": "^1.0.86",
|
|
40
41
|
"@wix/forms": "^1.0.384",
|
|
41
42
|
"@wix/headless-media": "0.0.25",
|
|
@@ -73,5 +74,5 @@
|
|
|
73
74
|
"groupId": "com.wixpress.headless-components"
|
|
74
75
|
}
|
|
75
76
|
},
|
|
76
|
-
"falconPackageHash": "
|
|
77
|
+
"falconPackageHash": "c5f3384150eba7e23d903366ac5a1fc164fc30f5172f6fc4bc2d61d8"
|
|
77
78
|
}
|