@wix/headless-bookings 0.0.61 → 0.0.63
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__/time-slots.js +1 -0
- package/cjs/dist/api/index.d.ts +2 -2
- package/cjs/dist/api/index.js +2 -2
- package/cjs/dist/api/query-locations/index.d.ts +14 -0
- package/cjs/dist/api/query-locations/index.js +27 -0
- package/cjs/dist/api/query-services/index.d.ts +5 -0
- package/cjs/dist/api/query-services/index.js +2 -2
- package/cjs/dist/react/booking/Book.d.ts +2 -0
- package/cjs/dist/react/booking/Book.js +11 -1
- package/cjs/dist/react/core/location/Location.d.ts +2 -0
- package/cjs/dist/react/core/location/Location.js +14 -1
- package/cjs/dist/react/core/time-slot-list/TimeSlot.js +0 -2
- package/cjs/dist/react/core/time-slot-list/TimeSlotList.d.ts +47 -3
- package/cjs/dist/react/core/time-slot-list/TimeSlotList.js +60 -5
- package/cjs/dist/react/index.d.ts +0 -1
- package/cjs/dist/react/index.js +1 -1
- package/cjs/dist/react/location/Location.d.ts +4 -0
- package/cjs/dist/react/location/Location.js +20 -2
- package/cjs/dist/react/location/LocationList.d.ts +4 -0
- package/cjs/dist/react/location/LocationList.js +33 -7
- package/cjs/dist/react/time-slot-list/TimeSlotList.d.ts +30 -7
- package/cjs/dist/react/time-slot-list/TimeSlotList.js +27 -8
- package/cjs/dist/services/index.d.ts +2 -1
- package/cjs/dist/services/index.js +2 -1
- package/cjs/dist/services/location-list/location-list.d.ts +1 -0
- package/cjs/dist/services/location-list/location-list.js +3 -2
- package/cjs/dist/services/time-slot-list/time-slot-list.def.d.ts +1 -0
- package/cjs/dist/services/time-slot-list/time-slot-list.js +2 -9
- package/cjs/dist/utils/index.d.ts +5 -0
- package/cjs/dist/utils/index.js +5 -0
- package/cjs/dist/utils/navigation.d.ts +7 -0
- package/cjs/dist/utils/navigation.js +75 -0
- package/dist/__mocks__/time-slots.js +1 -0
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.js +2 -2
- package/dist/api/query-locations/index.d.ts +14 -0
- package/dist/api/query-locations/index.js +27 -0
- package/dist/api/query-services/index.d.ts +5 -0
- package/dist/api/query-services/index.js +2 -2
- package/dist/react/booking/Book.d.ts +2 -0
- package/dist/react/booking/Book.js +11 -1
- package/dist/react/core/location/Location.d.ts +2 -0
- package/dist/react/core/location/Location.js +14 -1
- package/dist/react/core/time-slot-list/TimeSlot.js +0 -2
- package/dist/react/core/time-slot-list/TimeSlotList.d.ts +47 -3
- package/dist/react/core/time-slot-list/TimeSlotList.js +60 -5
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.js +1 -1
- package/dist/react/location/Location.d.ts +4 -0
- package/dist/react/location/Location.js +20 -2
- package/dist/react/location/LocationList.d.ts +4 -0
- package/dist/react/location/LocationList.js +33 -7
- package/dist/react/time-slot-list/TimeSlotList.d.ts +30 -7
- package/dist/react/time-slot-list/TimeSlotList.js +27 -8
- package/dist/services/index.d.ts +2 -1
- package/dist/services/index.js +2 -1
- package/dist/services/location-list/location-list.d.ts +1 -0
- package/dist/services/location-list/location-list.js +3 -2
- package/dist/services/time-slot-list/time-slot-list.def.d.ts +1 -0
- package/dist/services/time-slot-list/time-slot-list.js +2 -9
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/navigation.d.ts +7 -0
- package/dist/utils/navigation.js +75 -0
- package/package.json +2 -2
|
@@ -6,8 +6,9 @@ export { ServiceService, ServiceServiceDefinition, type ServiceServiceAPI, type
|
|
|
6
6
|
export { BookingService, BookingServiceDefinition, type BookingServiceConfig, type BookingServiceActions, type ServiceSelection, type ServiceSelectionTimeSlot, } from './booking/booking.js';
|
|
7
7
|
export { TimeSlotListService, TimeSlotListServiceDefinition, TimeSlotService, TimeSlotServiceDefinition, ViewMode, loadTimeSlotListConfig, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type TimeSlotServiceAPI, type TimeSlotServiceConfig, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list/index.js';
|
|
8
8
|
export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListFilters, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
|
|
9
|
+
export { SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from '../api/query-services/index.js';
|
|
9
10
|
export { executeBookAction, canBook, buildBookingRequest, buildCheckoutRequest, type CanBookParams, type BookResult, type BookingError, type BookActionParams, type BookChildProps, type BookProps, type BuildCheckoutParams, } from './booking/book-action/index.js';
|
|
10
11
|
export { BookingFormService, BookingFormServiceDefinition, type BookingFormServiceAPI, type BookingFormServiceConfig, type BookingFormServiceInternalAPI, type ValidationResult, } from './booking-form/booking-form.js';
|
|
11
12
|
export { PaymentService, PaymentServiceDefinition, type PaymentServiceAPI, type PaymentServiceConfig, type PaymentDetails, type PricingServiceSelection, type LineItemAdditionalInfo, loadPaymentConfig, type SuccessPaymentConfigResult, type ErrorPaymentConfigResult, } from './payment/payment.js';
|
|
12
|
-
export { LocationListService, LocationListServiceDefinition, LocationType, loadLocationListServiceInitialData, locationListServiceBinding, type LocationListServiceAPI, type LocationListServiceConfig, type LocationListActions, type LocationData, type DisplayLocation, type LoadLocationListServiceResult, } from './location-list/location-list.js';
|
|
13
|
+
export { LocationListService, LocationListServiceDefinition, LocationType, loadLocationListServiceInitialData, locationListServiceBinding, type LocationListServiceAPI, type LocationListServiceConfig, type LocationListActions, type LocationData, type DisplayLocation, type LoadLocationListServiceResult, getLocationById, } from './location-list/location-list.js';
|
|
13
14
|
export { StaffMemberListService, StaffMemberListServiceDefinition, loadStaffMemberListServiceInitialData, staffMemberListServiceBinding, type StaffMemberListServiceAPI, type StaffMemberListServiceConfig, type StaffMemberListActions, type StaffMemberData, type LoadStaffMemberListServiceResult, } from './staff-member-list/staff-member-list.js';
|
|
@@ -6,8 +6,9 @@ export { ServiceService, ServiceServiceDefinition, loadServiceServiceInitialData
|
|
|
6
6
|
export { BookingService, BookingServiceDefinition, } from './booking/booking.js';
|
|
7
7
|
export { TimeSlotListService, TimeSlotListServiceDefinition, TimeSlotService, TimeSlotServiceDefinition, ViewMode, loadTimeSlotListConfig, } from './time-slot-list/index.js';
|
|
8
8
|
export { ServiceListService, ServiceListServiceDefinition, loadServiceListServiceInitialData, } from './service-list/service-list.js';
|
|
9
|
+
export { SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from '../api/query-services/index.js';
|
|
9
10
|
export { executeBookAction, canBook, buildBookingRequest, buildCheckoutRequest, } from './booking/book-action/index.js';
|
|
10
11
|
export { BookingFormService, BookingFormServiceDefinition, } from './booking-form/booking-form.js';
|
|
11
12
|
export { PaymentService, PaymentServiceDefinition, loadPaymentConfig, } from './payment/payment.js';
|
|
12
|
-
export { LocationListService, LocationListServiceDefinition, LocationType, loadLocationListServiceInitialData, locationListServiceBinding, } from './location-list/location-list.js';
|
|
13
|
+
export { LocationListService, LocationListServiceDefinition, LocationType, loadLocationListServiceInitialData, locationListServiceBinding, getLocationById, } from './location-list/location-list.js';
|
|
13
14
|
export { StaffMemberListService, StaffMemberListServiceDefinition, loadStaffMemberListServiceInitialData, staffMemberListServiceBinding, } from './staff-member-list/staff-member-list.js';
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { ServiceFactoryConfig } from '@wix/services-definitions';
|
|
8
8
|
import { type LocationData } from './location-list.def.js';
|
|
9
|
+
export { getLocationById } from '../../api/query-locations/index.js';
|
|
9
10
|
/**
|
|
10
11
|
* Configuration interface for LocationListService
|
|
11
12
|
*/
|
|
@@ -10,14 +10,15 @@ import { 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';
|
|
13
|
+
export { getLocationById } from '../../api/query-locations/index.js';
|
|
13
14
|
// ============================================================================
|
|
14
15
|
// Constants
|
|
15
16
|
// ============================================================================
|
|
16
17
|
/**
|
|
17
18
|
* Synthetic location IDs for non-business locations
|
|
18
19
|
*/
|
|
19
|
-
const SYNTHETIC_CUSTOM_ID = '
|
|
20
|
-
const SYNTHETIC_CUSTOMER_ID = '
|
|
20
|
+
const SYNTHETIC_CUSTOM_ID = 'custom';
|
|
21
|
+
const SYNTHETIC_CUSTOMER_ID = 'customer';
|
|
21
22
|
// ============================================================================
|
|
22
23
|
// Helper Functions
|
|
23
24
|
// ============================================================================
|
|
@@ -88,6 +88,7 @@ export interface TimeSlotListServiceAPI {
|
|
|
88
88
|
hasMore: Signal<boolean>;
|
|
89
89
|
dateRange: Signal<DateRange>;
|
|
90
90
|
actions: TimeSlotListServiceActions;
|
|
91
|
+
dispose: () => void;
|
|
91
92
|
}
|
|
92
93
|
export declare const TimeSlotListServiceDefinition: string & {
|
|
93
94
|
__api: TimeSlotListServiceAPI;
|
|
@@ -19,15 +19,12 @@ function getClientTimezone() {
|
|
|
19
19
|
// Service Implementation
|
|
20
20
|
// ============================================================================
|
|
21
21
|
export const TimeSlotListService = implementService.withConfig()(TimeSlotListServiceDefinition, ({ getService, config }) => {
|
|
22
|
-
// Get dependent services
|
|
23
22
|
const signalsService = getService(SignalsServiceDefinition);
|
|
24
23
|
const bookingService = getService(BookingServiceDefinition);
|
|
25
|
-
// Initialize Signals
|
|
26
24
|
const timeSlotsSignal = signalsService.signal(config.timeSlots || []);
|
|
27
25
|
const isLoadingSignal = signalsService.signal(false);
|
|
28
26
|
const errorSignal = signalsService.signal(null);
|
|
29
27
|
const hasMoreSignal = signalsService.signal(false);
|
|
30
|
-
// Internal only - viewMode is auto-detected based on service types
|
|
31
28
|
const viewModeSignal = signalsService.signal(ViewMode.CLASSIC);
|
|
32
29
|
const dateRangeSignal = signalsService.signal({
|
|
33
30
|
start: config.startDate,
|
|
@@ -201,8 +198,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
201
198
|
: undefined;
|
|
202
199
|
});
|
|
203
200
|
let isFirstRun = true;
|
|
204
|
-
signalsService.effect(() => {
|
|
205
|
-
// Dependencies - trigger refetch when these change
|
|
201
|
+
const dispose = signalsService.effect(() => {
|
|
206
202
|
dateRangeSignal.get();
|
|
207
203
|
bookingService.timezone.get();
|
|
208
204
|
bookingService.location.get();
|
|
@@ -215,16 +211,12 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
215
211
|
}
|
|
216
212
|
fetchTimeSlots();
|
|
217
213
|
});
|
|
218
|
-
// Return service API
|
|
219
214
|
return {
|
|
220
|
-
// State Signals
|
|
221
215
|
timeSlots: timeSlotsSignal,
|
|
222
216
|
isLoading: isLoadingSignal,
|
|
223
217
|
error: errorSignal,
|
|
224
218
|
hasMore: hasMoreSignal,
|
|
225
|
-
// Configuration Signals (viewMode is internal only)
|
|
226
219
|
dateRange: dateRangeSignal,
|
|
227
|
-
// Actions
|
|
228
220
|
actions: {
|
|
229
221
|
selectSlot,
|
|
230
222
|
selectStaffMember,
|
|
@@ -232,6 +224,7 @@ export const TimeSlotListService = implementService.withConfig()(TimeSlotListSer
|
|
|
232
224
|
loadMore,
|
|
233
225
|
setDateRange,
|
|
234
226
|
},
|
|
227
|
+
dispose,
|
|
235
228
|
};
|
|
236
229
|
});
|
|
237
230
|
/**
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ServiceSelectionTimeSlot } from '../services/booking/booking.js';
|
|
2
|
+
import type { Service } from '@wix/auto_sdk_bookings_services';
|
|
3
|
+
export declare function navigateToCheckout(checkoutId: string): Promise<void>;
|
|
4
|
+
export declare function navigateToBookingForm({ timeSlot, service, }: {
|
|
5
|
+
timeSlot: ServiceSelectionTimeSlot;
|
|
6
|
+
service: Service;
|
|
7
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { redirects } from '@wix/redirects';
|
|
2
|
+
import { LocationType as TimeSlotLocationType } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
3
|
+
import { LocationType as BookingsLocationType } from '@wix/auto_sdk_redirects_redirects';
|
|
4
|
+
export async function navigateToCheckout(checkoutId) {
|
|
5
|
+
const { redirectSession } = await redirects.createRedirectSession({
|
|
6
|
+
ecomCheckout: { checkoutId },
|
|
7
|
+
callbacks: {
|
|
8
|
+
postFlowUrl: window.location.href,
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
if (redirectSession?.fullUrl) {
|
|
12
|
+
window.location.href = redirectSession.fullUrl;
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
throw new Error('Failed to create redirect session');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function mapTimeSlotLocationToBookingLocation(timeSlotLocationType) {
|
|
19
|
+
switch (timeSlotLocationType) {
|
|
20
|
+
case TimeSlotLocationType.BUSINESS:
|
|
21
|
+
return BookingsLocationType.OWNER_BUSINESS;
|
|
22
|
+
case TimeSlotLocationType.CUSTOMER:
|
|
23
|
+
return BookingsLocationType.CUSTOM;
|
|
24
|
+
case TimeSlotLocationType.CUSTOM:
|
|
25
|
+
return BookingsLocationType.OWNER_CUSTOM;
|
|
26
|
+
default:
|
|
27
|
+
return BookingsLocationType.UNDEFINED;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export async function navigateToBookingForm({ timeSlot, service, }) {
|
|
31
|
+
if (!timeSlot) {
|
|
32
|
+
throw new Error('No time slot provided');
|
|
33
|
+
}
|
|
34
|
+
if (!service) {
|
|
35
|
+
throw new Error('No service provided');
|
|
36
|
+
}
|
|
37
|
+
// Get the first resource (staff member) if available
|
|
38
|
+
const selectedResource = timeSlot.resources?.[0];
|
|
39
|
+
const { redirectSession } = await redirects.createRedirectSession({
|
|
40
|
+
bookingsCheckout: {
|
|
41
|
+
slotAvailability: {
|
|
42
|
+
bookable: true,
|
|
43
|
+
slot: {
|
|
44
|
+
...timeSlot,
|
|
45
|
+
serviceId: service._id ?? undefined,
|
|
46
|
+
scheduleId: timeSlot.scheduleId,
|
|
47
|
+
startDate: timeSlot.startDate,
|
|
48
|
+
endDate: timeSlot.endDate,
|
|
49
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
50
|
+
resource: selectedResource
|
|
51
|
+
? {
|
|
52
|
+
_id: selectedResource._id,
|
|
53
|
+
name: selectedResource.name,
|
|
54
|
+
}
|
|
55
|
+
: undefined,
|
|
56
|
+
location: {
|
|
57
|
+
_id: timeSlot.location?._id,
|
|
58
|
+
name: timeSlot.location?.name,
|
|
59
|
+
formattedAddress: timeSlot.location?.formattedAddress,
|
|
60
|
+
locationType: mapTimeSlotLocationToBookingLocation(timeSlot.location?.locationType),
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
callbacks: {
|
|
66
|
+
postFlowUrl: window.location.href,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
if (redirectSession?.fullUrl) {
|
|
70
|
+
window.location.href = redirectSession.fullUrl;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
throw new Error('Failed to create redirect session');
|
|
74
|
+
}
|
|
75
|
+
}
|
package/dist/api/index.d.ts
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @module API
|
|
6
6
|
*/
|
|
7
|
-
export { getServiceById, getServiceBySlug, queryServices, type PagingMetadata, type ServiceListFilters, type QueryServicesRequest, } from './query-services/index.js';
|
|
7
|
+
export { getServiceById, getServiceBySlug, queryServices, type PagingMetadata, type ServiceListFilters, type QueryServicesRequest, SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from './query-services/index.js';
|
|
8
8
|
export { fetchAvailability } from './fetch-availability/index.js';
|
|
9
9
|
export { createBooking, type CreateBookingOptions, type Booking, type CreateBookingRequest, type CreateBookingResponse, type ParticipantNotification, type ContactDetails, } from './create-booking/index.js';
|
|
10
10
|
export { createCheckout, ChannelType, type CreateCheckoutRequest, type CreateCheckoutResponse, } from './create-checkout/index.js';
|
|
11
11
|
export { createOrder, type CreateOrderOptions, type CreateOrderResponse, } from './create-order/index.js';
|
|
12
|
-
export { queryLocations, type QueryLocationsResult, type LocationData, } from './query-locations/index.js';
|
|
12
|
+
export { queryLocations, getLocationById, type QueryLocationsResult, type LocationData, } from './query-locations/index.js';
|
|
13
13
|
export { queryCategories, type QueryCategoriesResult, type Category, } from './query-categories/index.js';
|
|
14
14
|
/**
|
|
15
15
|
* PagingMetadata includes:
|
package/dist/api/index.js
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @module API
|
|
6
6
|
*/
|
|
7
|
-
export { getServiceById, getServiceBySlug, queryServices, } from './query-services/index.js';
|
|
7
|
+
export { getServiceById, getServiceBySlug, queryServices, SYNTHETIC_CUSTOM_ID, SYNTHETIC_CUSTOMER_ID, } from './query-services/index.js';
|
|
8
8
|
export { fetchAvailability } from './fetch-availability/index.js';
|
|
9
9
|
export { createBooking, } from './create-booking/index.js';
|
|
10
10
|
export { createCheckout, ChannelType, } from './create-checkout/index.js';
|
|
11
11
|
export { createOrder, } from './create-order/index.js';
|
|
12
|
-
export { queryLocations, } from './query-locations/index.js';
|
|
12
|
+
export { queryLocations, getLocationById, } from './query-locations/index.js';
|
|
13
13
|
export { queryCategories, } from './query-categories/index.js';
|
|
14
14
|
/**
|
|
15
15
|
* PagingMetadata includes:
|
|
@@ -36,3 +36,17 @@ export interface QueryLocationsResult {
|
|
|
36
36
|
* ```
|
|
37
37
|
*/
|
|
38
38
|
export declare function queryLocations(): Promise<QueryLocationsResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Fetches a single location by its business location ID
|
|
41
|
+
* @param locationId - The business location ID (GUID)
|
|
42
|
+
* @returns Promise resolving to the location or null if not found
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const location = await getLocationById('12345678-1234-1234-1234-123456789012');
|
|
47
|
+
* if (location) {
|
|
48
|
+
* console.log(location.business?.name);
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function getLocationById(locationId: string): Promise<LocationData | null>;
|
|
@@ -28,3 +28,30 @@ export async function queryLocations() {
|
|
|
28
28
|
hasCustomerLocations: response.customerLocations?.exists || false,
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Fetches a single location by its business location ID
|
|
33
|
+
* @param locationId - The business location ID (GUID)
|
|
34
|
+
* @returns Promise resolving to the location or null if not found
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const location = await getLocationById('12345678-1234-1234-1234-123456789012');
|
|
39
|
+
* if (location) {
|
|
40
|
+
* console.log(location.business?.name);
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export async function getLocationById(locationId) {
|
|
45
|
+
try {
|
|
46
|
+
const response = await queryLocationsApi({
|
|
47
|
+
filter: {
|
|
48
|
+
businessLocationIds: [locationId],
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
return response.businessLocations?.locations?.[0] ?? null;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error(`Failed to load location for id "${locationId}":`, error);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
* Fetches single or multiple services with pagination and sorting
|
|
4
4
|
*/
|
|
5
5
|
import { type Service, type Paging, type Sorting } from '@wix/auto_sdk_bookings_services';
|
|
6
|
+
/**
|
|
7
|
+
* Synthetic location ID constants for non-business locations
|
|
8
|
+
*/
|
|
9
|
+
export declare const SYNTHETIC_CUSTOM_ID = "custom";
|
|
10
|
+
export declare const SYNTHETIC_CUSTOMER_ID = "customer";
|
|
6
11
|
/**
|
|
7
12
|
* Paging metadata from query services response.
|
|
8
13
|
* Extends the SDK's Paging type with hasNext flag to indicate if more services are available.
|
|
@@ -10,8 +10,8 @@ const BOOKING_APP_ID = '13d21c63-b5ec-5912-8397-c3a5ddb27a97';
|
|
|
10
10
|
/**
|
|
11
11
|
* Synthetic location ID constants for non-business locations
|
|
12
12
|
*/
|
|
13
|
-
const SYNTHETIC_CUSTOM_ID = '
|
|
14
|
-
const SYNTHETIC_CUSTOMER_ID = '
|
|
13
|
+
export const SYNTHETIC_CUSTOM_ID = 'custom';
|
|
14
|
+
export const SYNTHETIC_CUSTOMER_ID = 'customer';
|
|
15
15
|
/**
|
|
16
16
|
* Fetches a service by its ID
|
|
17
17
|
* @param id - The service ID
|
|
@@ -16,9 +16,11 @@ export interface BookProps {
|
|
|
16
16
|
disabled?: boolean;
|
|
17
17
|
onCheckout?: (result: {
|
|
18
18
|
checkoutId: string;
|
|
19
|
+
navigateToCheckout: (checkoutId: string) => void;
|
|
19
20
|
}) => void;
|
|
20
21
|
onComplete?: (result: {
|
|
21
22
|
orderId: string;
|
|
23
|
+
navigateToThankYou: (orderId: string) => void;
|
|
22
24
|
}) => void;
|
|
23
25
|
onError?: (error: BookingError) => void;
|
|
24
26
|
}
|
|
@@ -6,6 +6,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
8
8
|
import * as CoreBook from '../core/booking/Book.js';
|
|
9
|
+
import { navigateToCheckout } from '../../utils/navigation.js';
|
|
9
10
|
var TestIds;
|
|
10
11
|
(function (TestIds) {
|
|
11
12
|
TestIds["bookingActionBook"] = "booking-action-book";
|
|
@@ -49,6 +50,15 @@ var TestIds;
|
|
|
49
50
|
*/
|
|
50
51
|
export const Book = React.forwardRef((props, ref) => {
|
|
51
52
|
const { asChild, children, className, label = 'Book Now', loadingState = 'Booking...', disabled, onCheckout, onComplete, onError, } = props;
|
|
52
|
-
return (_jsx(CoreBook.Book, { onCheckout:
|
|
53
|
+
return (_jsx(CoreBook.Book, { onCheckout: (result) => {
|
|
54
|
+
onCheckout?.({ ...result, navigateToCheckout });
|
|
55
|
+
}, onComplete: (result) => {
|
|
56
|
+
onComplete?.({
|
|
57
|
+
...result,
|
|
58
|
+
navigateToThankYou: (orderId) => {
|
|
59
|
+
console.log('navigateToThankYou', orderId);
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}, onError: onError, disabled: disabled, children: (childProps) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.bookingActionBook, "data-in-progress": childProps.isLoading, customElement: children, customElementProps: childProps, children: _jsx("button", { onClick: childProps.onClick, disabled: childProps.disabled, children: childProps.isLoading ? loadingState : label }) })) }));
|
|
53
63
|
});
|
|
54
64
|
Book.displayName = 'Booking.Actions.Book';
|
|
@@ -27,6 +27,8 @@ export interface SyntheticLocation {
|
|
|
27
27
|
synthetic: true;
|
|
28
28
|
/** Optional ID for tracking (matches ServiceLocation._id) */
|
|
29
29
|
_id?: string;
|
|
30
|
+
/** Optional name/label for the location */
|
|
31
|
+
name?: string;
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
32
34
|
* Union type - location prop accepts Service or Synthetic location
|
|
@@ -106,10 +106,23 @@ function computeLocationRenderProps(rawLocation) {
|
|
|
106
106
|
if (isSyntheticLocation(rawLocation)) {
|
|
107
107
|
const syntheticLoc = rawLocation;
|
|
108
108
|
const locationType = syntheticLoc.locationType;
|
|
109
|
+
// Use provided name or default based on location type
|
|
110
|
+
let name;
|
|
111
|
+
if (syntheticLoc.name) {
|
|
112
|
+
name = syntheticLoc.name;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
name =
|
|
116
|
+
locationType === LocationType.CUSTOM
|
|
117
|
+
? 'Custom Locations'
|
|
118
|
+
: locationType === LocationType.CUSTOMER
|
|
119
|
+
? "Client's Place"
|
|
120
|
+
: undefined;
|
|
121
|
+
}
|
|
109
122
|
return {
|
|
110
123
|
locationType,
|
|
111
124
|
address: null,
|
|
112
|
-
name
|
|
125
|
+
name,
|
|
113
126
|
locationId: syntheticLoc._id,
|
|
114
127
|
isCustomerLocation: locationType === LocationType.CUSTOMER,
|
|
115
128
|
isCustomLocation: locationType === LocationType.CUSTOM,
|
|
@@ -82,7 +82,6 @@ export function Info(props) {
|
|
|
82
82
|
const endDate = new Date(timeSlot.localEndDate);
|
|
83
83
|
const locationName = timeSlot.location?.name ?? '';
|
|
84
84
|
const bookable = timeSlot.bookable ?? false;
|
|
85
|
-
// Calculate duration from start and end dates
|
|
86
85
|
const durationInMinutes = Math.round((endDate.getTime() - startDate.getTime()) / (1000 * 60));
|
|
87
86
|
return props.children({
|
|
88
87
|
startDate,
|
|
@@ -166,7 +165,6 @@ export function StaffMembers(props) {
|
|
|
166
165
|
const bookingService = useService(BookingServiceDefinition);
|
|
167
166
|
const selectedStaffMemberId = bookingService.serviceSelections.get()[0]?.timeSlot?.resources?.[0]?._id;
|
|
168
167
|
const staffMembers = timeSlot.availableResources[0].resources;
|
|
169
|
-
// Return null if only 1 staff member - nothing to choose from
|
|
170
168
|
if (staffMembers.length <= 1) {
|
|
171
169
|
return null;
|
|
172
170
|
}
|
|
@@ -5,12 +5,49 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { type TimeSlotListServiceConfig } from '../../../services/time-slot-list/index.js';
|
|
7
7
|
import type { TimeSlot } from '@wix/auto_sdk_bookings_availability-time-slots';
|
|
8
|
+
import { type ServiceSelectionTimeSlot } from '../../../services/booking/booking.js';
|
|
9
|
+
import type { Service } from '@wix/auto_sdk_bookings_services';
|
|
10
|
+
/**
|
|
11
|
+
* Render props data provided to TimeSlotList.Root children
|
|
12
|
+
*/
|
|
13
|
+
export interface TimeSlotListRootRenderProps {
|
|
14
|
+
/** Function to navigate to the booking form with the selected time slot */
|
|
15
|
+
navigateToBookingForm: ({ timeSlot, service, }: {
|
|
16
|
+
timeSlot?: ServiceSelectionTimeSlot;
|
|
17
|
+
service?: Service;
|
|
18
|
+
}) => Promise<void>;
|
|
19
|
+
}
|
|
8
20
|
/**
|
|
9
21
|
* Props for Root component
|
|
10
22
|
*/
|
|
11
23
|
export interface RootProps {
|
|
12
|
-
children: React.ReactNode;
|
|
24
|
+
children: (data: TimeSlotListRootRenderProps) => React.ReactNode;
|
|
13
25
|
config: TimeSlotListServiceConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Function to navigate to the booking form with the selected time slot.
|
|
28
|
+
* The timeSlot and service parameters are optional - if not provided,
|
|
29
|
+
* BookingService will be used to get them from the current service selections.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* <Root
|
|
34
|
+
* config={config}
|
|
35
|
+
* navigateToBookingForm={async ({ timeSlot, service }) => {
|
|
36
|
+
* // Custom navigation logic
|
|
37
|
+
* await navigateToBookingForm({ timeSlot, service });
|
|
38
|
+
* }}
|
|
39
|
+
* >
|
|
40
|
+
* {({ navigateToBookingForm }) => (
|
|
41
|
+
* // Use navigateToBookingForm in children
|
|
42
|
+
* // Can call with: navigateToBookingForm() or navigateToBookingForm({ timeSlot, service })
|
|
43
|
+
* )}
|
|
44
|
+
* </Root>
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
navigateToBookingForm: ({ timeSlot, service, }: {
|
|
48
|
+
timeSlot?: ServiceSelectionTimeSlot;
|
|
49
|
+
service?: Service;
|
|
50
|
+
}) => Promise<void>;
|
|
14
51
|
}
|
|
15
52
|
/**
|
|
16
53
|
* Root component that provides TimeSlotListService context to its children.
|
|
@@ -18,8 +55,15 @@ export interface RootProps {
|
|
|
18
55
|
* @component
|
|
19
56
|
* @example
|
|
20
57
|
* ```tsx
|
|
21
|
-
* <Root
|
|
22
|
-
* {
|
|
58
|
+
* <Root
|
|
59
|
+
* config={{ services, timeSlots, timezone }}
|
|
60
|
+
* navigateToBookingForm={async (selection) => {
|
|
61
|
+
* await navigateToBookingForm(selection);
|
|
62
|
+
* }}
|
|
63
|
+
* >
|
|
64
|
+
* {({ navigateToBookingForm }) => (
|
|
65
|
+
* // Your components that can use navigateToBookingForm
|
|
66
|
+
* )}
|
|
23
67
|
* </Root>
|
|
24
68
|
* ```
|
|
25
69
|
*/
|
|
@@ -1,21 +1,76 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Core TimeSlotList Components
|
|
4
|
+
* Provides low-level access to TimeSlotListService via render props
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
2
7
|
import { useService, WixServices } from '@wix/services-manager-react';
|
|
3
8
|
import { createServicesMap } from '@wix/services-manager';
|
|
4
9
|
import { TimeSlotListServiceDefinition, TimeSlotListService, } from '../../../services/time-slot-list/index.js';
|
|
5
|
-
import { BookingServiceDefinition } from '../../../services/booking/booking.js';
|
|
10
|
+
import { BookingServiceDefinition, } from '../../../services/booking/booking.js';
|
|
6
11
|
/**
|
|
7
12
|
* Root component that provides TimeSlotListService context to its children.
|
|
8
13
|
*
|
|
9
14
|
* @component
|
|
10
15
|
* @example
|
|
11
16
|
* ```tsx
|
|
12
|
-
* <Root
|
|
13
|
-
* {
|
|
17
|
+
* <Root
|
|
18
|
+
* config={{ services, timeSlots, timezone }}
|
|
19
|
+
* navigateToBookingForm={async (selection) => {
|
|
20
|
+
* await navigateToBookingForm(selection);
|
|
21
|
+
* }}
|
|
22
|
+
* >
|
|
23
|
+
* {({ navigateToBookingForm }) => (
|
|
24
|
+
* // Your components that can use navigateToBookingForm
|
|
25
|
+
* )}
|
|
14
26
|
* </Root>
|
|
15
27
|
* ```
|
|
16
28
|
*/
|
|
17
29
|
export function Root(props) {
|
|
18
|
-
return (_jsx(WixServices, { servicesMap: createServicesMap().addService(TimeSlotListServiceDefinition, TimeSlotListService, props.config), children: props.children }));
|
|
30
|
+
return (_jsx(WixServices, { servicesMap: createServicesMap().addService(TimeSlotListServiceDefinition, TimeSlotListService, props.config), children: _jsx(RootContent, { navigateToBookingForm: props.navigateToBookingForm, children: props.children }) }));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Internal component that wraps navigateToBookingForm to handle optional parameters
|
|
34
|
+
*/
|
|
35
|
+
function RootContent({ navigateToBookingForm: providedNavigateToBookingForm, children, }) {
|
|
36
|
+
const timeSlotListService = useService(TimeSlotListServiceDefinition);
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
return () => {
|
|
39
|
+
timeSlotListService.dispose();
|
|
40
|
+
};
|
|
41
|
+
}, [timeSlotListService]);
|
|
42
|
+
let bookingService = null;
|
|
43
|
+
try {
|
|
44
|
+
bookingService = useService(BookingServiceDefinition);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
bookingService = null;
|
|
48
|
+
}
|
|
49
|
+
const navigateToBookingForm = async ({ timeSlot, service, }) => {
|
|
50
|
+
let resolvedTimeSlot = timeSlot;
|
|
51
|
+
let resolvedService = service;
|
|
52
|
+
if ((!resolvedTimeSlot || !resolvedService) && bookingService) {
|
|
53
|
+
const serviceSelections = bookingService.serviceSelections.get();
|
|
54
|
+
if (serviceSelections.length > 0) {
|
|
55
|
+
const firstSelection = serviceSelections[0];
|
|
56
|
+
if (firstSelection) {
|
|
57
|
+
resolvedService = resolvedService || firstSelection.service;
|
|
58
|
+
resolvedTimeSlot = resolvedTimeSlot || firstSelection.timeSlot;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (!resolvedTimeSlot) {
|
|
63
|
+
throw new Error('timeSlot is required. Either provide it as a parameter or ensure BookingService has a service selection with a timeSlot.');
|
|
64
|
+
}
|
|
65
|
+
if (!resolvedService) {
|
|
66
|
+
throw new Error('service is required. Either provide it as a parameter or ensure BookingService has a service selection.');
|
|
67
|
+
}
|
|
68
|
+
return providedNavigateToBookingForm({
|
|
69
|
+
timeSlot: resolvedTimeSlot,
|
|
70
|
+
service: resolvedService,
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
return _jsx(_Fragment, { children: children({ navigateToBookingForm }) });
|
|
19
74
|
}
|
|
20
75
|
// ============================================================================
|
|
21
76
|
// Timezone Component
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export * as Booking from './booking/Booking.js';
|
|
2
|
-
export * as BookingForm from './booking-form/BookingForm.js';
|
|
3
2
|
export * as Service from './service/Service.js';
|
|
4
3
|
export * as ServiceMedia from './service/ServiceMedia.js';
|
|
5
4
|
export * as TimeSlotList from './time-slot-list/TimeSlotList.js';
|
package/dist/react/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * as Booking from './booking/Booking.js';
|
|
2
|
-
export * as BookingForm from './booking-form/BookingForm.js';
|
|
2
|
+
// export * as BookingForm from './booking-form/BookingForm.js';
|
|
3
3
|
export * as Service from './service/Service.js';
|
|
4
4
|
export * as ServiceMedia from './service/ServiceMedia.js';
|
|
5
5
|
export * as TimeSlotList from './time-slot-list/TimeSlotList.js';
|
|
@@ -23,6 +23,10 @@ export interface RootProps {
|
|
|
23
23
|
children?: React.ReactNode | AsChildChildren<CoreLocation.LocationRenderProps>;
|
|
24
24
|
/** CSS class name */
|
|
25
25
|
className?: string;
|
|
26
|
+
/** Custom label for CUSTOM location type */
|
|
27
|
+
customLocationLabel?: string;
|
|
28
|
+
/** Custom label for CUSTOMER location type */
|
|
29
|
+
customerLocationLabel?: string;
|
|
26
30
|
/** Data attributes */
|
|
27
31
|
[key: `data-${string}`]: string | undefined;
|
|
28
32
|
}
|
|
@@ -9,6 +9,7 @@ import React from 'react';
|
|
|
9
9
|
import * as CoreLocation from '../core/location/Location.js';
|
|
10
10
|
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
11
11
|
import { Address } from '@wix/headless-components/react';
|
|
12
|
+
import { LocationType } from '../../services/location-list/location-list.def.js';
|
|
12
13
|
// Re-export useLocationContext from core
|
|
13
14
|
export { useLocationContext } from '../core/location/Location.js';
|
|
14
15
|
// ==========================================
|
|
@@ -63,10 +64,27 @@ const useLocationContext = CoreLocation.useLocationContext;
|
|
|
63
64
|
* ```
|
|
64
65
|
*/
|
|
65
66
|
export const Root = React.forwardRef((props, ref) => {
|
|
66
|
-
const { location, asChild, children, className, ...attrs } = props;
|
|
67
|
+
const { location, asChild, children, className, customLocationLabel, customerLocationLabel, ...attrs } = props;
|
|
67
68
|
// Extract data-* attributes
|
|
68
69
|
const dataAttrs = Object.fromEntries(Object.entries(attrs).filter(([key]) => key.startsWith('data-')));
|
|
69
|
-
|
|
70
|
+
// If location is synthetic and labels are provided, update the synthetic location's name
|
|
71
|
+
let processedLocation = location;
|
|
72
|
+
if (location && 'synthetic' in location && location.synthetic === true) {
|
|
73
|
+
const syntheticLoc = location;
|
|
74
|
+
const shouldUpdateName = (syntheticLoc.locationType === LocationType.CUSTOM &&
|
|
75
|
+
customLocationLabel !== undefined) ||
|
|
76
|
+
(syntheticLoc.locationType === LocationType.CUSTOMER &&
|
|
77
|
+
customerLocationLabel !== undefined);
|
|
78
|
+
if (shouldUpdateName) {
|
|
79
|
+
processedLocation = {
|
|
80
|
+
...syntheticLoc,
|
|
81
|
+
name: syntheticLoc.locationType === LocationType.CUSTOM
|
|
82
|
+
? customLocationLabel
|
|
83
|
+
: customerLocationLabel,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return (_jsx(CoreLocation.Root, { location: processedLocation, children: _jsx(CoreLocation.Data, { children: (renderProps) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.locationRoot, "data-location-type": renderProps.locationType, customElement: children, customElementProps: renderProps, ...dataAttrs, children: children })) }) }));
|
|
70
88
|
});
|
|
71
89
|
Root.displayName = 'Location.Root';
|
|
72
90
|
/**
|