@wix/headless-bookings 0.0.9 → 0.0.11
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/index.d.ts +10 -1
- package/cjs/dist/api/index.js +10 -1
- package/cjs/dist/api/query-services/index.d.ts +78 -0
- package/cjs/dist/api/query-services/index.js +137 -0
- package/cjs/dist/react/core/booking/Booking.d.ts +4 -4
- package/cjs/dist/react/core/booking/Booking.js +3 -3
- package/cjs/dist/react/core/location/Location.d.ts +44 -0
- package/cjs/dist/react/core/location/Location.js +78 -0
- package/cjs/dist/react/core/service/Service.d.ts +160 -17
- package/cjs/dist/react/core/service/Service.js +211 -13
- package/cjs/dist/react/core/service-list/ServiceList.d.ts +75 -0
- package/cjs/dist/react/core/service-list/ServiceList.js +58 -0
- package/cjs/dist/react/index.d.ts +2 -0
- package/cjs/dist/react/index.js +2 -0
- package/cjs/dist/react/location/Location.d.ts +77 -0
- package/cjs/dist/react/location/Location.js +89 -0
- package/cjs/dist/react/service/Service.d.ts +1036 -31
- package/cjs/dist/react/service/Service.js +1060 -47
- package/cjs/dist/react/service-list/ServiceList.d.ts +185 -0
- package/cjs/dist/react/service-list/ServiceList.js +184 -0
- package/cjs/dist/services/booking/booking.d.ts +50 -33
- package/cjs/dist/services/booking/booking.js +72 -38
- package/cjs/dist/services/index.d.ts +3 -2
- package/cjs/dist/services/index.js +3 -2
- package/cjs/dist/services/service/service.d.ts +71 -18
- package/cjs/dist/services/service/service.js +187 -52
- package/cjs/dist/services/service-list/service-list.d.ts +157 -0
- package/cjs/dist/services/service-list/service-list.js +274 -0
- package/cjs/dist/utils/dateAndTime.d.ts +2 -0
- package/cjs/dist/utils/dateAndTime.js +18 -0
- package/cjs/dist/utils/money.d.ts +21 -0
- package/cjs/dist/utils/money.js +34 -0
- package/dist/api/index.d.ts +10 -1
- package/dist/api/index.js +10 -1
- package/dist/api/query-services/index.d.ts +78 -0
- package/dist/api/query-services/index.js +137 -0
- package/dist/react/core/booking/Booking.d.ts +4 -4
- package/dist/react/core/booking/Booking.js +3 -3
- package/dist/react/core/location/Location.d.ts +44 -0
- package/dist/react/core/location/Location.js +78 -0
- package/dist/react/core/service/Service.d.ts +160 -17
- package/dist/react/core/service/Service.js +211 -13
- package/dist/react/core/service-list/ServiceList.d.ts +75 -0
- package/dist/react/core/service-list/ServiceList.js +58 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +2 -0
- package/dist/react/location/Location.d.ts +77 -0
- package/dist/react/location/Location.js +89 -0
- package/dist/react/service/Service.d.ts +1036 -31
- package/dist/react/service/Service.js +1060 -47
- package/dist/react/service-list/ServiceList.d.ts +185 -0
- package/dist/react/service-list/ServiceList.js +184 -0
- package/dist/services/booking/booking.d.ts +50 -33
- package/dist/services/booking/booking.js +72 -38
- package/dist/services/index.d.ts +3 -2
- package/dist/services/index.js +3 -2
- package/dist/services/service/service.d.ts +71 -18
- package/dist/services/service/service.js +187 -52
- package/dist/services/service-list/service-list.d.ts +157 -0
- package/dist/services/service-list/service-list.js +274 -0
- package/dist/utils/dateAndTime.d.ts +2 -0
- package/dist/utils/dateAndTime.js +18 -0
- package/dist/utils/money.d.ts +21 -0
- package/dist/utils/money.js +34 -0
- package/package.json +3 -2
- package/cjs/dist/api/query-service/index.d.ts +0 -23
- package/cjs/dist/api/query-service/index.js +0 -41
- package/dist/api/query-service/index.d.ts +0 -23
- package/dist/api/query-service/index.js +0 -41
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Location Component - Parses service location data
|
|
3
|
+
* Handles business, custom, and customer location types
|
|
4
|
+
* Converts location data to Address component format
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { services } from '@wix/bookings';
|
|
8
|
+
import type { AddressData as AddressDataType } from '@wix/headless-components/react';
|
|
9
|
+
/**
|
|
10
|
+
* Re-export AddressData type from Address component for convenience
|
|
11
|
+
*/
|
|
12
|
+
export type AddressData = AddressDataType;
|
|
13
|
+
/**
|
|
14
|
+
* Location render props passed to children
|
|
15
|
+
*/
|
|
16
|
+
export interface LocationRenderProps {
|
|
17
|
+
locationType: services.LocationType;
|
|
18
|
+
address: AddressData | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Props for the core Location component
|
|
22
|
+
*/
|
|
23
|
+
export interface LocationProps {
|
|
24
|
+
location: NonNullable<services.Service['locations']>[number];
|
|
25
|
+
children: (props: LocationRenderProps) => React.ReactNode;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Core Location component that parses service location data
|
|
29
|
+
* and provides location type and address data to children
|
|
30
|
+
*
|
|
31
|
+
* @component
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* <CoreLocation location={serviceLocation}>
|
|
35
|
+
* {({ locationType, address }) => (
|
|
36
|
+
* <div>
|
|
37
|
+
* <span>Type: {locationType}</span>
|
|
38
|
+
* {address && <Address.Root address={address}><Address.Formatted /></Address.Root>}
|
|
39
|
+
* </div>
|
|
40
|
+
* )}
|
|
41
|
+
* </CoreLocation>
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function Location(props: LocationProps): React.ReactNode;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Location Component - Parses service location data
|
|
3
|
+
* Handles business, custom, and customer location types
|
|
4
|
+
* Converts location data to Address component format
|
|
5
|
+
*/
|
|
6
|
+
import { services } from '@wix/bookings';
|
|
7
|
+
/**
|
|
8
|
+
* Parses location data into Address component format
|
|
9
|
+
*/
|
|
10
|
+
function parseLocationAddress(location, locationType) {
|
|
11
|
+
// Handle customer location specially
|
|
12
|
+
if (locationType === services.LocationType.CUSTOMER) {
|
|
13
|
+
// For customer location, return an AddressData object with Customer's Place
|
|
14
|
+
return {
|
|
15
|
+
address: {
|
|
16
|
+
addressLine: "Customer's Place",
|
|
17
|
+
formattedAddress: "Customer's Place",
|
|
18
|
+
city: '',
|
|
19
|
+
subdivision: '',
|
|
20
|
+
postalCode: '',
|
|
21
|
+
country: '',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Get the common address from the appropriate location type
|
|
26
|
+
const commonAddress = location.calculatedAddress ||
|
|
27
|
+
location.business?.address ||
|
|
28
|
+
location.custom?.address;
|
|
29
|
+
if (!commonAddress) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
// Convert CommonAddress to AddressData format (compatible with Address.Root from @wix/headless-components)
|
|
33
|
+
return {
|
|
34
|
+
address: {
|
|
35
|
+
addressLine: commonAddress.streetAddress?.name ||
|
|
36
|
+
commonAddress.addressLine ||
|
|
37
|
+
commonAddress.addressLine1 ||
|
|
38
|
+
'',
|
|
39
|
+
city: commonAddress.city || '',
|
|
40
|
+
subdivision: commonAddress.subdivision || '',
|
|
41
|
+
postalCode: commonAddress.postalCode || '',
|
|
42
|
+
country: commonAddress.country || '',
|
|
43
|
+
streetAddress: commonAddress.streetAddress
|
|
44
|
+
? {
|
|
45
|
+
number: commonAddress.streetAddress.number,
|
|
46
|
+
name: commonAddress.streetAddress.name,
|
|
47
|
+
apt: commonAddress.streetAddress.apt,
|
|
48
|
+
}
|
|
49
|
+
: undefined,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// ==========================================
|
|
54
|
+
// Component
|
|
55
|
+
// ==========================================
|
|
56
|
+
/**
|
|
57
|
+
* Core Location component that parses service location data
|
|
58
|
+
* and provides location type and address data to children
|
|
59
|
+
*
|
|
60
|
+
* @component
|
|
61
|
+
* @example
|
|
62
|
+
* ```tsx
|
|
63
|
+
* <CoreLocation location={serviceLocation}>
|
|
64
|
+
* {({ locationType, address }) => (
|
|
65
|
+
* <div>
|
|
66
|
+
* <span>Type: {locationType}</span>
|
|
67
|
+
* {address && <Address.Root address={address}><Address.Formatted /></Address.Root>}
|
|
68
|
+
* </div>
|
|
69
|
+
* )}
|
|
70
|
+
* </CoreLocation>
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export function Location(props) {
|
|
74
|
+
const { location, children } = props;
|
|
75
|
+
const locationType = location.type;
|
|
76
|
+
const address = parseLocationAddress(location, locationType);
|
|
77
|
+
return children({ locationType, address });
|
|
78
|
+
}
|
|
@@ -4,25 +4,18 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { type ServiceServiceConfig } from '../../../services/service/service.js';
|
|
7
|
-
import
|
|
7
|
+
import { services } from '@wix/bookings';
|
|
8
|
+
import type { MoneyData } from '@wix/headless-components/react';
|
|
8
9
|
/**
|
|
9
10
|
* Props for Service.Root component
|
|
10
11
|
*/
|
|
11
12
|
export interface RootProps {
|
|
12
|
-
children: React.ReactNode
|
|
13
|
+
children: React.ReactNode | ((props: {
|
|
14
|
+
selected: boolean;
|
|
15
|
+
bookable: boolean;
|
|
16
|
+
}) => React.ReactNode);
|
|
13
17
|
serviceServiceConfig: ServiceServiceConfig;
|
|
14
18
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Root component that provides service context.
|
|
17
|
-
*
|
|
18
|
-
* @component
|
|
19
|
-
* @example
|
|
20
|
-
* ```tsx
|
|
21
|
-
* <Service.Root serviceServiceConfig={{ service }}>
|
|
22
|
-
* {children}
|
|
23
|
-
* </Service.Root>
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
19
|
export declare function Root(props: RootProps): React.ReactNode;
|
|
27
20
|
/**
|
|
28
21
|
* Props for Service render prop component
|
|
@@ -55,26 +48,101 @@ export interface InfoProps {
|
|
|
55
48
|
children: (data: {
|
|
56
49
|
name: string;
|
|
57
50
|
description: string;
|
|
51
|
+
tagline?: string;
|
|
52
|
+
duration?: number;
|
|
53
|
+
type?: string;
|
|
54
|
+
category?: {
|
|
55
|
+
name?: string;
|
|
56
|
+
_id?: string;
|
|
57
|
+
};
|
|
58
|
+
locations?: Array<NonNullable<services.Service['locations']>[number]>;
|
|
59
|
+
staff?: Array<{
|
|
60
|
+
name?: string;
|
|
61
|
+
_id?: string;
|
|
62
|
+
}>;
|
|
63
|
+
media?: Array<{
|
|
64
|
+
url?: string;
|
|
65
|
+
_id?: string;
|
|
66
|
+
}>;
|
|
67
|
+
mainMedia?: {
|
|
68
|
+
image?: string;
|
|
69
|
+
};
|
|
70
|
+
coverMedia?: {
|
|
71
|
+
image?: string;
|
|
72
|
+
};
|
|
73
|
+
mediaItems?: Array<{
|
|
74
|
+
image?: string | null;
|
|
75
|
+
altText?: string | null;
|
|
76
|
+
}>;
|
|
77
|
+
price?: {
|
|
78
|
+
amount?: string;
|
|
79
|
+
formattedAmount?: string;
|
|
80
|
+
currency?: string;
|
|
81
|
+
};
|
|
82
|
+
defaultCapacity?: number;
|
|
83
|
+
selected: boolean;
|
|
58
84
|
}) => React.ReactNode;
|
|
59
85
|
}
|
|
60
86
|
/**
|
|
61
87
|
* Core component that provides access to service catalog info via render props.
|
|
62
|
-
* Includes name
|
|
88
|
+
* Includes name, description, tagline, duration, type, category, locations, staff, media, and price.
|
|
63
89
|
*
|
|
64
90
|
* @component
|
|
65
91
|
* @example
|
|
66
92
|
* ```tsx
|
|
67
93
|
* <Service.Info>
|
|
68
|
-
* {({ name, description }) => (
|
|
94
|
+
* {({ name, description, tagline, duration, type, category, locations, staff, media, price }) => (
|
|
69
95
|
* <div>
|
|
70
96
|
* <h1>{name}</h1>
|
|
71
97
|
* <p>{description}</p>
|
|
98
|
+
* {tagline && <p>{tagline}</p>}
|
|
99
|
+
* {duration && <p>Duration: {duration} minutes</p>}
|
|
100
|
+
* {type && <p>Type: {type}</p>}
|
|
101
|
+
* {category && <p>Category: {category.name}</p>}
|
|
72
102
|
* </div>
|
|
73
103
|
* )}
|
|
74
104
|
* </Service.Info>
|
|
75
105
|
* ```
|
|
76
106
|
*/
|
|
77
107
|
export declare function Info(props: InfoProps): React.ReactNode;
|
|
108
|
+
/**
|
|
109
|
+
* Props for Price render prop component
|
|
110
|
+
*/
|
|
111
|
+
export interface PriceProps {
|
|
112
|
+
children: (data: {
|
|
113
|
+
servicePayment: services.Payment | undefined;
|
|
114
|
+
rateType: services.Payment['rateType'] | undefined;
|
|
115
|
+
money: MoneyData | null;
|
|
116
|
+
minPrice: MoneyData | null;
|
|
117
|
+
maxPrice: MoneyData | null;
|
|
118
|
+
priceDescription: string | null;
|
|
119
|
+
hasAddOns: boolean;
|
|
120
|
+
hasPricingPlans: boolean;
|
|
121
|
+
}) => React.ReactNode;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Core component that provides access to service price via render props.
|
|
125
|
+
* Returns comprehensive pricing data including money, rate type, and service capabilities.
|
|
126
|
+
*
|
|
127
|
+
* @component
|
|
128
|
+
* @example
|
|
129
|
+
* ```tsx
|
|
130
|
+
* <Service.Price>
|
|
131
|
+
* {({ money, rateType, minPrice, maxPrice, priceDescription, hasAddOns, hasPricingPlans }) => (
|
|
132
|
+
* rateType === 'FIXED' && money ? (
|
|
133
|
+
* <Money money={money} />
|
|
134
|
+
* ) : rateType === 'VARIED' && minPrice && maxPrice ? (
|
|
135
|
+
* <span><Money money={minPrice} /> - <Money money={maxPrice} /></span>
|
|
136
|
+
* ) : rateType === 'CUSTOM' && priceDescription ? (
|
|
137
|
+
* <span>{priceDescription}</span>
|
|
138
|
+
* ) : rateType === 'NO_FEE' ? (
|
|
139
|
+
* <span>Free</span>
|
|
140
|
+
* ) : null
|
|
141
|
+
* )}
|
|
142
|
+
* </Service.Price>
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export declare function Price(props: PriceProps): React.ReactNode;
|
|
78
146
|
/**
|
|
79
147
|
* Props for Actions render prop component
|
|
80
148
|
*/
|
|
@@ -83,7 +151,10 @@ export interface ActionsProps {
|
|
|
83
151
|
select: () => void;
|
|
84
152
|
add: () => void;
|
|
85
153
|
remove: () => void;
|
|
86
|
-
|
|
154
|
+
rawService: services.Service;
|
|
155
|
+
bookable: boolean;
|
|
156
|
+
selected: boolean;
|
|
157
|
+
requiresAvailability: boolean;
|
|
87
158
|
}) => React.ReactNode;
|
|
88
159
|
}
|
|
89
160
|
/**
|
|
@@ -93,7 +164,7 @@ export interface ActionsProps {
|
|
|
93
164
|
* @example
|
|
94
165
|
* ```tsx
|
|
95
166
|
* <Service.Actions>
|
|
96
|
-
* {({ select, add, remove,
|
|
167
|
+
* {({ select, add, remove, rawService, requiresAvailability }) => (
|
|
97
168
|
* <div>
|
|
98
169
|
* <button onClick={select}>Select</button>
|
|
99
170
|
* <button onClick={add}>Add</button>
|
|
@@ -104,3 +175,75 @@ export interface ActionsProps {
|
|
|
104
175
|
* ```
|
|
105
176
|
*/
|
|
106
177
|
export declare function Actions(props: ActionsProps): React.ReactNode;
|
|
178
|
+
/**
|
|
179
|
+
* Props for Policy render prop component
|
|
180
|
+
*/
|
|
181
|
+
export interface PolicyProps {
|
|
182
|
+
children: (data: {
|
|
183
|
+
bookingPolicy: services.BookingPolicy | null;
|
|
184
|
+
cancellationPolicyEnabled: boolean;
|
|
185
|
+
limitLatestCancellationEnabled: boolean;
|
|
186
|
+
latestCancellationInMinutes: number | undefined;
|
|
187
|
+
latestBookingPolicyEnabled: boolean;
|
|
188
|
+
latestBookingInMinutes: number | undefined;
|
|
189
|
+
earliestBookingPolicyEnabled: boolean;
|
|
190
|
+
earliestBookingInMinutes: number | undefined;
|
|
191
|
+
reschedulePolicyEnabled: boolean;
|
|
192
|
+
limitLatestRescheduleEnabled: boolean;
|
|
193
|
+
latestRescheduleInMinutes: number | undefined;
|
|
194
|
+
waitlistPolicyEnabled: boolean;
|
|
195
|
+
waitlistCapacity: number | undefined;
|
|
196
|
+
bookAfterStartEnabled: boolean;
|
|
197
|
+
maxParticipantsPerBooking: number | undefined;
|
|
198
|
+
}) => React.ReactNode;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Core component that provides access to service booking policy via render props.
|
|
202
|
+
* Computes all enabled flags and values from the booking policy.
|
|
203
|
+
*
|
|
204
|
+
* @component
|
|
205
|
+
* @example
|
|
206
|
+
* ```tsx
|
|
207
|
+
* <Service.Policy>
|
|
208
|
+
* {({ bookingPolicy, cancellationPolicyEnabled, latestCancellationInMinutes }) => (
|
|
209
|
+
* cancellationPolicyEnabled ? (
|
|
210
|
+
* <div>Cancel up to {latestCancellationInMinutes} minutes before</div>
|
|
211
|
+
* ) : null
|
|
212
|
+
* )}
|
|
213
|
+
* </Service.Policy>
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
export declare function Policy(props: PolicyProps): React.ReactNode;
|
|
217
|
+
/**
|
|
218
|
+
* Props for Schedule render prop component
|
|
219
|
+
*/
|
|
220
|
+
export interface ScheduleProps {
|
|
221
|
+
children: (data: {
|
|
222
|
+
schedule: services.Schedule | null;
|
|
223
|
+
isClassOrCourse: boolean;
|
|
224
|
+
}) => React.ReactNode;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Core component that provides access to service schedule via render props.
|
|
228
|
+
* Uses the SDK's Schedule type directly. Only renders for CLASS or COURSE service types.
|
|
229
|
+
*
|
|
230
|
+
* @component
|
|
231
|
+
* @example
|
|
232
|
+
* ```tsx
|
|
233
|
+
* <Service.Schedule>
|
|
234
|
+
* {({ schedule, isClassOrCourse }) => (
|
|
235
|
+
* schedule && isClassOrCourse ? (
|
|
236
|
+
* <div>
|
|
237
|
+
* {schedule.firstSessionStart && (
|
|
238
|
+
* <span>Starts: {new Date(schedule.firstSessionStart).toLocaleDateString()}</span>
|
|
239
|
+
* )}
|
|
240
|
+
* {schedule.lastSessionEnd && (
|
|
241
|
+
* <span>Ends: {new Date(schedule.lastSessionEnd).toLocaleDateString()}</span>
|
|
242
|
+
* )}
|
|
243
|
+
* </div>
|
|
244
|
+
* ) : null
|
|
245
|
+
* )}
|
|
246
|
+
* </Service.Schedule>
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
export declare function Schedule(props: ScheduleProps): React.ReactNode;
|
|
@@ -13,8 +13,17 @@ import { ServiceServiceDefinition, ServiceService, } from '../../../services/ser
|
|
|
13
13
|
* </Service.Root>
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
|
+
function RootContent({ children, }) {
|
|
17
|
+
const serviceService = useService(ServiceServiceDefinition);
|
|
18
|
+
const selected = serviceService.selectedSignal.get();
|
|
19
|
+
const bookable = serviceService.bookableSignal.get();
|
|
20
|
+
if (typeof children === 'function') {
|
|
21
|
+
return children({ selected, bookable });
|
|
22
|
+
}
|
|
23
|
+
return children;
|
|
24
|
+
}
|
|
16
25
|
export function Root(props) {
|
|
17
|
-
return (_jsx(WixServices, { servicesMap: createServicesMap().addService(ServiceServiceDefinition, ServiceService, props.serviceServiceConfig), children: props.children }));
|
|
26
|
+
return (_jsx(WixServices, { servicesMap: createServicesMap().addService(ServiceServiceDefinition, ServiceService, props.serviceServiceConfig), children: _jsx(RootContent, { children: props.children }) }));
|
|
18
27
|
}
|
|
19
28
|
/**
|
|
20
29
|
* Core component that provides access to full service data via render props.
|
|
@@ -31,23 +40,27 @@ export function Root(props) {
|
|
|
31
40
|
*/
|
|
32
41
|
export function Service(props) {
|
|
33
42
|
const serviceService = useService(ServiceServiceDefinition);
|
|
34
|
-
const service = serviceService.
|
|
35
|
-
const isLoading = serviceService.
|
|
36
|
-
const error = serviceService.
|
|
43
|
+
const service = serviceService.serviceSignal.get();
|
|
44
|
+
const isLoading = serviceService.isLoadingSignal.get();
|
|
45
|
+
const error = serviceService.errorSignal.get();
|
|
37
46
|
return props.children({ service, isLoading, error });
|
|
38
47
|
}
|
|
39
48
|
/**
|
|
40
49
|
* Core component that provides access to service catalog info via render props.
|
|
41
|
-
* Includes name
|
|
50
|
+
* Includes name, description, tagline, duration, type, category, locations, staff, media, and price.
|
|
42
51
|
*
|
|
43
52
|
* @component
|
|
44
53
|
* @example
|
|
45
54
|
* ```tsx
|
|
46
55
|
* <Service.Info>
|
|
47
|
-
* {({ name, description }) => (
|
|
56
|
+
* {({ name, description, tagline, duration, type, category, locations, staff, media, price }) => (
|
|
48
57
|
* <div>
|
|
49
58
|
* <h1>{name}</h1>
|
|
50
59
|
* <p>{description}</p>
|
|
60
|
+
* {tagline && <p>{tagline}</p>}
|
|
61
|
+
* {duration && <p>Duration: {duration} minutes</p>}
|
|
62
|
+
* {type && <p>Type: {type}</p>}
|
|
63
|
+
* {category && <p>Category: {category.name}</p>}
|
|
51
64
|
* </div>
|
|
52
65
|
* )}
|
|
53
66
|
* </Service.Info>
|
|
@@ -55,12 +68,99 @@ export function Service(props) {
|
|
|
55
68
|
*/
|
|
56
69
|
export function Info(props) {
|
|
57
70
|
const serviceService = useService(ServiceServiceDefinition);
|
|
58
|
-
const service = serviceService.
|
|
71
|
+
const service = serviceService.serviceSignal.get();
|
|
72
|
+
const selected = serviceService.selectedSignal.get();
|
|
73
|
+
// Extract price from service.payment for Info component
|
|
74
|
+
const payment = service.payment;
|
|
75
|
+
let priceInfo = undefined;
|
|
76
|
+
if (payment?.rateType === 'FIXED' && payment.fixed?.price) {
|
|
77
|
+
const priceValue = payment.fixed.price.value;
|
|
78
|
+
const currency = payment.fixed.price.currency || 'USD';
|
|
79
|
+
priceInfo = {
|
|
80
|
+
amount: priceValue || undefined,
|
|
81
|
+
currency,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// Extract duration from schedule.availabilityConstraints.sessionDurations
|
|
85
|
+
// Use the first duration if multiple are available
|
|
86
|
+
const duration = service.schedule?.availabilityConstraints?.sessionDurations?.[0];
|
|
87
|
+
// Get staff members from staffMemberDetails.staffMembers
|
|
88
|
+
// (available when using conditionalFields: ['STAFF_MEMBER_DETAILS'] in queryServices)
|
|
89
|
+
const staff = service.staffMembers?.map((member) => ({
|
|
90
|
+
_id: member.staffMemberId,
|
|
91
|
+
name: member.name || undefined,
|
|
92
|
+
})) || [];
|
|
93
|
+
// Convert mainMedia to WixMediaImage format
|
|
94
|
+
// Handle both snake_case (main_media) and camelCase (mainMedia) from API
|
|
95
|
+
// Handle both string and object formats for image
|
|
96
|
+
const serviceMedia = service.media;
|
|
97
|
+
const mainMedia = {
|
|
98
|
+
image: serviceMedia?.mainMedia?.image,
|
|
99
|
+
};
|
|
100
|
+
// Convert coverMedia to WixMediaImage format
|
|
101
|
+
// Handle both snake_case (cover_media) and camelCase (coverMedia) from API
|
|
102
|
+
// Handle both string and object formats for image
|
|
103
|
+
const coverMedia = {
|
|
104
|
+
image: serviceMedia?.coverMedia?.image,
|
|
105
|
+
};
|
|
106
|
+
// Convert media items to MediaGallery format
|
|
107
|
+
// Handle both string and object formats for image
|
|
108
|
+
const mediaItems = service.media?.items?.map((item) => {
|
|
109
|
+
return {
|
|
110
|
+
image: item.image,
|
|
111
|
+
};
|
|
112
|
+
}) || [];
|
|
113
|
+
// Extract default capacity directly from service (convert null to undefined)
|
|
114
|
+
const defaultCapacity = service.defaultCapacity ?? undefined;
|
|
59
115
|
return props.children({
|
|
60
116
|
name: service.name || '',
|
|
61
117
|
description: service.description || '',
|
|
118
|
+
tagline: service.tagLine || undefined,
|
|
119
|
+
duration: duration,
|
|
120
|
+
type: service.type,
|
|
121
|
+
category: service.category
|
|
122
|
+
? {
|
|
123
|
+
name: service.category.name || undefined,
|
|
124
|
+
_id: service.category._id || undefined,
|
|
125
|
+
}
|
|
126
|
+
: undefined,
|
|
127
|
+
locations: service.locations,
|
|
128
|
+
staff: staff,
|
|
129
|
+
mainMedia: mainMedia,
|
|
130
|
+
coverMedia: coverMedia,
|
|
131
|
+
mediaItems: mediaItems.length > 0 ? mediaItems : undefined,
|
|
132
|
+
price: priceInfo,
|
|
133
|
+
defaultCapacity: defaultCapacity,
|
|
134
|
+
selected,
|
|
62
135
|
});
|
|
63
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Core component that provides access to service price via render props.
|
|
139
|
+
* Returns comprehensive pricing data including money, rate type, and service capabilities.
|
|
140
|
+
*
|
|
141
|
+
* @component
|
|
142
|
+
* @example
|
|
143
|
+
* ```tsx
|
|
144
|
+
* <Service.Price>
|
|
145
|
+
* {({ money, rateType, minPrice, maxPrice, priceDescription, hasAddOns, hasPricingPlans }) => (
|
|
146
|
+
* rateType === 'FIXED' && money ? (
|
|
147
|
+
* <Money money={money} />
|
|
148
|
+
* ) : rateType === 'VARIED' && minPrice && maxPrice ? (
|
|
149
|
+
* <span><Money money={minPrice} /> - <Money money={maxPrice} /></span>
|
|
150
|
+
* ) : rateType === 'CUSTOM' && priceDescription ? (
|
|
151
|
+
* <span>{priceDescription}</span>
|
|
152
|
+
* ) : rateType === 'NO_FEE' ? (
|
|
153
|
+
* <span>Free</span>
|
|
154
|
+
* ) : null
|
|
155
|
+
* )}
|
|
156
|
+
* </Service.Price>
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export function Price(props) {
|
|
160
|
+
const serviceService = useService(ServiceServiceDefinition);
|
|
161
|
+
const paymentData = serviceService.paymentDataSignal.get();
|
|
162
|
+
return props.children(paymentData);
|
|
163
|
+
}
|
|
64
164
|
/**
|
|
65
165
|
* Core component that provides booking selection actions via render props.
|
|
66
166
|
*
|
|
@@ -68,7 +168,7 @@ export function Info(props) {
|
|
|
68
168
|
* @example
|
|
69
169
|
* ```tsx
|
|
70
170
|
* <Service.Actions>
|
|
71
|
-
* {({ select, add, remove,
|
|
171
|
+
* {({ select, add, remove, rawService, requiresAvailability }) => (
|
|
72
172
|
* <div>
|
|
73
173
|
* <button onClick={select}>Select</button>
|
|
74
174
|
* <button onClick={add}>Add</button>
|
|
@@ -80,11 +180,109 @@ export function Info(props) {
|
|
|
80
180
|
*/
|
|
81
181
|
export function Actions(props) {
|
|
82
182
|
const serviceService = useService(ServiceServiceDefinition);
|
|
83
|
-
const
|
|
183
|
+
const rawService = serviceService.serviceSignal.get();
|
|
184
|
+
const bookable = serviceService.bookableSignal.get();
|
|
185
|
+
const selected = serviceService.selectedSignal.get();
|
|
186
|
+
const requiresAvailability = serviceService.requiresAvailabilitySignal.get();
|
|
187
|
+
return props.children({
|
|
188
|
+
select: serviceService.actions.select,
|
|
189
|
+
add: serviceService.actions.add,
|
|
190
|
+
remove: serviceService.actions.remove,
|
|
191
|
+
rawService,
|
|
192
|
+
bookable,
|
|
193
|
+
selected,
|
|
194
|
+
requiresAvailability,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Core component that provides access to service booking policy via render props.
|
|
199
|
+
* Computes all enabled flags and values from the booking policy.
|
|
200
|
+
*
|
|
201
|
+
* @component
|
|
202
|
+
* @example
|
|
203
|
+
* ```tsx
|
|
204
|
+
* <Service.Policy>
|
|
205
|
+
* {({ bookingPolicy, cancellationPolicyEnabled, latestCancellationInMinutes }) => (
|
|
206
|
+
* cancellationPolicyEnabled ? (
|
|
207
|
+
* <div>Cancel up to {latestCancellationInMinutes} minutes before</div>
|
|
208
|
+
* ) : null
|
|
209
|
+
* )}
|
|
210
|
+
* </Service.Policy>
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
export function Policy(props) {
|
|
214
|
+
const serviceService = useService(ServiceServiceDefinition);
|
|
215
|
+
const service = serviceService.serviceSignal.get();
|
|
216
|
+
const bookingPolicy = service.bookingPolicy || null;
|
|
217
|
+
// Compute all flags here (not in headless components)
|
|
218
|
+
const cancellationPolicy = bookingPolicy?.cancellationPolicy;
|
|
219
|
+
const cancellationPolicyEnabled = cancellationPolicy?.enabled ?? false;
|
|
220
|
+
const limitLatestCancellationEnabled = cancellationPolicy?.limitLatestCancellation ?? false;
|
|
221
|
+
const latestCancellationInMinutes = cancellationPolicy?.latestCancellationInMinutes;
|
|
222
|
+
const lateBookingPolicy = bookingPolicy?.limitLateBookingPolicy;
|
|
223
|
+
const latestBookingPolicyEnabled = lateBookingPolicy?.enabled ?? false;
|
|
224
|
+
const latestBookingInMinutes = lateBookingPolicy?.latestBookingInMinutes;
|
|
225
|
+
const earlyBookingPolicy = bookingPolicy?.limitEarlyBookingPolicy;
|
|
226
|
+
const earliestBookingPolicyEnabled = earlyBookingPolicy?.enabled ?? false;
|
|
227
|
+
const earliestBookingInMinutes = earlyBookingPolicy?.earliestBookingInMinutes;
|
|
228
|
+
const reschedulePolicy = bookingPolicy?.reschedulePolicy;
|
|
229
|
+
const reschedulePolicyEnabled = reschedulePolicy?.enabled ?? false;
|
|
230
|
+
const limitLatestRescheduleEnabled = reschedulePolicy?.limitLatestReschedule ?? false;
|
|
231
|
+
const latestRescheduleInMinutes = reschedulePolicy?.latestRescheduleInMinutes;
|
|
232
|
+
const waitlistPolicy = bookingPolicy?.waitlistPolicy;
|
|
233
|
+
const waitlistPolicyEnabled = waitlistPolicy?.enabled ?? false;
|
|
234
|
+
const waitlistCapacity = waitlistPolicy?.capacity;
|
|
235
|
+
const bookAfterStartPolicy = bookingPolicy?.bookAfterStartPolicy;
|
|
236
|
+
const bookAfterStartEnabled = bookAfterStartPolicy?.enabled ?? false;
|
|
237
|
+
const participantsPolicy = bookingPolicy?.participantsPolicy;
|
|
238
|
+
const maxParticipantsPerBooking = participantsPolicy?.maxParticipantsPerBooking;
|
|
239
|
+
return props.children({
|
|
240
|
+
bookingPolicy,
|
|
241
|
+
cancellationPolicyEnabled,
|
|
242
|
+
limitLatestCancellationEnabled,
|
|
243
|
+
latestCancellationInMinutes,
|
|
244
|
+
latestBookingPolicyEnabled,
|
|
245
|
+
latestBookingInMinutes,
|
|
246
|
+
earliestBookingPolicyEnabled,
|
|
247
|
+
earliestBookingInMinutes,
|
|
248
|
+
reschedulePolicyEnabled,
|
|
249
|
+
limitLatestRescheduleEnabled,
|
|
250
|
+
latestRescheduleInMinutes,
|
|
251
|
+
waitlistPolicyEnabled,
|
|
252
|
+
waitlistCapacity,
|
|
253
|
+
bookAfterStartEnabled,
|
|
254
|
+
maxParticipantsPerBooking,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Core component that provides access to service schedule via render props.
|
|
259
|
+
* Uses the SDK's Schedule type directly. Only renders for CLASS or COURSE service types.
|
|
260
|
+
*
|
|
261
|
+
* @component
|
|
262
|
+
* @example
|
|
263
|
+
* ```tsx
|
|
264
|
+
* <Service.Schedule>
|
|
265
|
+
* {({ schedule, isClassOrCourse }) => (
|
|
266
|
+
* schedule && isClassOrCourse ? (
|
|
267
|
+
* <div>
|
|
268
|
+
* {schedule.firstSessionStart && (
|
|
269
|
+
* <span>Starts: {new Date(schedule.firstSessionStart).toLocaleDateString()}</span>
|
|
270
|
+
* )}
|
|
271
|
+
* {schedule.lastSessionEnd && (
|
|
272
|
+
* <span>Ends: {new Date(schedule.lastSessionEnd).toLocaleDateString()}</span>
|
|
273
|
+
* )}
|
|
274
|
+
* </div>
|
|
275
|
+
* ) : null
|
|
276
|
+
* )}
|
|
277
|
+
* </Service.Schedule>
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
export function Schedule(props) {
|
|
281
|
+
const serviceService = useService(ServiceServiceDefinition);
|
|
282
|
+
const service = serviceService.serviceSignal.get();
|
|
283
|
+
const isClassOrCourse = service.type === 'CLASS' || service.type === 'COURSE';
|
|
84
284
|
return props.children({
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
remove: serviceService.remove,
|
|
88
|
-
service,
|
|
285
|
+
schedule: service.schedule || null,
|
|
286
|
+
isClassOrCourse,
|
|
89
287
|
});
|
|
90
288
|
}
|