@wix/headless-bookings 0.0.63 → 0.0.64

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.
@@ -12,9 +12,10 @@ import type { Service } from '@wix/auto_sdk_bookings_services';
12
12
  */
13
13
  export interface TimeSlotListRootRenderProps {
14
14
  /** Function to navigate to the booking form with the selected time slot */
15
- navigateToBookingForm: ({ timeSlot, service, }: {
15
+ navigateToBookingForm: ({ timeSlot, service, timezone, }: {
16
16
  timeSlot?: ServiceSelectionTimeSlot;
17
17
  service?: Service;
18
+ timezone?: string;
18
19
  }) => Promise<void>;
19
20
  }
20
21
  /**
@@ -44,9 +45,10 @@ export interface RootProps {
44
45
  * </Root>
45
46
  * ```
46
47
  */
47
- navigateToBookingForm: ({ timeSlot, service, }: {
48
+ navigateToBookingForm: ({ timeSlot, service, timezone, }: {
48
49
  timeSlot?: ServiceSelectionTimeSlot;
49
50
  service?: Service;
51
+ timezone?: string;
50
52
  }) => Promise<void>;
51
53
  }
52
54
  /**
@@ -46,11 +46,16 @@ function RootContent({ navigateToBookingForm: providedNavigateToBookingForm, chi
46
46
  catch {
47
47
  bookingService = null;
48
48
  }
49
- const navigateToBookingForm = async ({ timeSlot, service, }) => {
49
+ const navigateToBookingForm = async ({ timeSlot, service, timezone, }) => {
50
50
  let resolvedTimeSlot = timeSlot;
51
51
  let resolvedService = service;
52
- if ((!resolvedTimeSlot || !resolvedService) && bookingService) {
52
+ let resolvedTimezone = timezone;
53
+ if ((!resolvedTimeSlot || !resolvedService || !resolvedTimezone) &&
54
+ bookingService) {
53
55
  const serviceSelections = bookingService.serviceSelections.get();
56
+ if (!resolvedTimezone) {
57
+ resolvedTimezone = bookingService.timezone.get();
58
+ }
54
59
  if (serviceSelections.length > 0) {
55
60
  const firstSelection = serviceSelections[0];
56
61
  if (firstSelection) {
@@ -68,6 +73,7 @@ function RootContent({ navigateToBookingForm: providedNavigateToBookingForm, chi
68
73
  return providedNavigateToBookingForm({
69
74
  timeSlot: resolvedTimeSlot,
70
75
  service: resolvedService,
76
+ timezone: resolvedTimezone,
71
77
  });
72
78
  };
73
79
  return _jsx(_Fragment, { children: children({ navigateToBookingForm }) });
@@ -52,21 +52,23 @@ export declare const Timezone: React.ForwardRefExoticComponent<TimezoneProps & R
52
52
  */
53
53
  export interface RootProps {
54
54
  children: React.ReactNode | ((data: {
55
- navigateToBookingForm: ({ timeSlot, service, }: {
55
+ navigateToBookingForm: ({ timeSlot, service, timezone, }: {
56
56
  timeSlot?: ServiceSelectionTimeSlot;
57
57
  service?: Service;
58
+ timezone?: string;
58
59
  }) => Promise<void>;
59
60
  }) => React.ReactNode);
60
61
  config: TimeSlotListServiceConfig;
61
62
  /**
62
63
  * Function to navigate to the booking form.
63
- * The timeSlot and service parameters are optional - if not provided,
64
+ * The timeSlot, service, and timezone parameters are optional - if not provided,
64
65
  * BookingService will be used to get them from the current service selections.
65
66
  * If not provided, will use the default navigation utility.
66
67
  */
67
- navigateToBookingForm?: ({ timeSlot, service, }: {
68
+ navigateToBookingForm?: ({ timeSlot, service, timezone, }: {
68
69
  timeSlot?: ServiceSelectionTimeSlot;
69
70
  service?: Service;
71
+ timezone?: string;
70
72
  }) => Promise<void>;
71
73
  className?: string;
72
74
  variant?: ListVariant;
@@ -87,11 +87,11 @@ Timezone.displayName = 'TimeSlotList.Timezone';
87
87
  export const Root = React.forwardRef((props, ref) => {
88
88
  const { children, config, className, variant, navigateToBookingForm: providedNavigateToBookingForm, } = props;
89
89
  const navigateToBookingForm = providedNavigateToBookingForm ||
90
- (async ({ timeSlot, service, }) => {
90
+ (async ({ timeSlot, service, timezone, }) => {
91
91
  if (!timeSlot || !service) {
92
92
  throw new Error('timeSlot and service are required when using default navigation. Either provide them or ensure BookingService has service selections.');
93
93
  }
94
- return defaultNavigateToBookingForm({ timeSlot, service });
94
+ return defaultNavigateToBookingForm({ timeSlot, service, timezone });
95
95
  });
96
96
  return (_jsx(CoreTimeSlotList.Root, { config: config, navigateToBookingForm: navigateToBookingForm, children: (coreData) => (_jsx(RootContent, { className: className, ref: ref, variant: variant, children: typeof children === 'function'
97
97
  ? children({
@@ -1,7 +1,17 @@
1
1
  import type { ServiceSelectionTimeSlot } from '../services/booking/booking.js';
2
2
  import type { Service } from '@wix/auto_sdk_bookings_services';
3
3
  export declare function navigateToCheckout(checkoutId: string): Promise<void>;
4
- export declare function navigateToBookingForm({ timeSlot, service, }: {
4
+ /**
5
+ * Cleans a redirect URL by extracting the redirectUrl param and removing:
6
+ * - The 'origin' query parameter
7
+ * - Any query parameters with empty values
8
+ *
9
+ * @param fullUrl - The full URL containing the redirectUrl query parameter
10
+ * @returns The cleaned redirect URL
11
+ */
12
+ export declare function cleanRedirectUrl(fullUrl: string): string;
13
+ export declare function navigateToBookingForm({ timeSlot, service, timezone, }: {
5
14
  timeSlot: ServiceSelectionTimeSlot;
6
15
  service: Service;
16
+ timezone?: string;
7
17
  }): Promise<void>;
@@ -27,7 +27,32 @@ function mapTimeSlotLocationToBookingLocation(timeSlotLocationType) {
27
27
  return BookingsLocationType.UNDEFINED;
28
28
  }
29
29
  }
30
- export async function navigateToBookingForm({ timeSlot, service, }) {
30
+ /**
31
+ * Cleans a redirect URL by extracting the redirectUrl param and removing:
32
+ * - The 'origin' query parameter
33
+ * - Any query parameters with empty values
34
+ *
35
+ * @param fullUrl - The full URL containing the redirectUrl query parameter
36
+ * @returns The cleaned redirect URL
37
+ */
38
+ export function cleanRedirectUrl(fullUrl) {
39
+ const url = new URL(fullUrl);
40
+ const redirectUrl = url.searchParams.get('redirectUrl');
41
+ if (!redirectUrl) {
42
+ return fullUrl;
43
+ }
44
+ const redirectParsed = new URL(redirectUrl);
45
+ // Remove origin param
46
+ redirectParsed.searchParams.delete('origin');
47
+ // Remove empty params
48
+ for (const [key, value] of [...redirectParsed.searchParams.entries()]) {
49
+ if (!value) {
50
+ redirectParsed.searchParams.delete(key);
51
+ }
52
+ }
53
+ return redirectParsed.toString();
54
+ }
55
+ export async function navigateToBookingForm({ timeSlot, service, timezone, }) {
31
56
  if (!timeSlot) {
32
57
  throw new Error('No time slot provided');
33
58
  }
@@ -36,30 +61,36 @@ export async function navigateToBookingForm({ timeSlot, service, }) {
36
61
  }
37
62
  // Get the first resource (staff member) if available
38
63
  const selectedResource = timeSlot.resources?.[0];
64
+ // Destructure to exclude properties we handle separately (location has incompatible types)
65
+ const { location: _location, resources: _resources, ...restTimeSlot } = timeSlot;
66
+ // Build slot object, only including optional properties when they have values
67
+ const slot = {
68
+ ...restTimeSlot,
69
+ scheduleId: timeSlot.scheduleId,
70
+ startDate: timeSlot.startDate,
71
+ endDate: timeSlot.endDate,
72
+ ...(service._id && { serviceId: service._id }),
73
+ ...(timezone && { timezone }),
74
+ ...(selectedResource && {
75
+ resource: {
76
+ _id: selectedResource._id,
77
+ name: selectedResource.name,
78
+ },
79
+ }),
80
+ ...(timeSlot.location && {
81
+ location: {
82
+ _id: timeSlot.location._id,
83
+ name: timeSlot.location.name,
84
+ formattedAddress: timeSlot.location.formattedAddress,
85
+ locationType: mapTimeSlotLocationToBookingLocation(timeSlot.location.locationType),
86
+ },
87
+ }),
88
+ };
39
89
  const { redirectSession } = await redirects.createRedirectSession({
40
90
  bookingsCheckout: {
41
91
  slotAvailability: {
42
92
  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
- },
93
+ slot,
63
94
  },
64
95
  },
65
96
  callbacks: {
@@ -67,7 +98,7 @@ export async function navigateToBookingForm({ timeSlot, service, }) {
67
98
  },
68
99
  });
69
100
  if (redirectSession?.fullUrl) {
70
- window.location.href = redirectSession.fullUrl;
101
+ window.location.href = cleanRedirectUrl(redirectSession.fullUrl);
71
102
  }
72
103
  else {
73
104
  throw new Error('Failed to create redirect session');
@@ -12,9 +12,10 @@ import type { Service } from '@wix/auto_sdk_bookings_services';
12
12
  */
13
13
  export interface TimeSlotListRootRenderProps {
14
14
  /** Function to navigate to the booking form with the selected time slot */
15
- navigateToBookingForm: ({ timeSlot, service, }: {
15
+ navigateToBookingForm: ({ timeSlot, service, timezone, }: {
16
16
  timeSlot?: ServiceSelectionTimeSlot;
17
17
  service?: Service;
18
+ timezone?: string;
18
19
  }) => Promise<void>;
19
20
  }
20
21
  /**
@@ -44,9 +45,10 @@ export interface RootProps {
44
45
  * </Root>
45
46
  * ```
46
47
  */
47
- navigateToBookingForm: ({ timeSlot, service, }: {
48
+ navigateToBookingForm: ({ timeSlot, service, timezone, }: {
48
49
  timeSlot?: ServiceSelectionTimeSlot;
49
50
  service?: Service;
51
+ timezone?: string;
50
52
  }) => Promise<void>;
51
53
  }
52
54
  /**
@@ -46,11 +46,16 @@ function RootContent({ navigateToBookingForm: providedNavigateToBookingForm, chi
46
46
  catch {
47
47
  bookingService = null;
48
48
  }
49
- const navigateToBookingForm = async ({ timeSlot, service, }) => {
49
+ const navigateToBookingForm = async ({ timeSlot, service, timezone, }) => {
50
50
  let resolvedTimeSlot = timeSlot;
51
51
  let resolvedService = service;
52
- if ((!resolvedTimeSlot || !resolvedService) && bookingService) {
52
+ let resolvedTimezone = timezone;
53
+ if ((!resolvedTimeSlot || !resolvedService || !resolvedTimezone) &&
54
+ bookingService) {
53
55
  const serviceSelections = bookingService.serviceSelections.get();
56
+ if (!resolvedTimezone) {
57
+ resolvedTimezone = bookingService.timezone.get();
58
+ }
54
59
  if (serviceSelections.length > 0) {
55
60
  const firstSelection = serviceSelections[0];
56
61
  if (firstSelection) {
@@ -68,6 +73,7 @@ function RootContent({ navigateToBookingForm: providedNavigateToBookingForm, chi
68
73
  return providedNavigateToBookingForm({
69
74
  timeSlot: resolvedTimeSlot,
70
75
  service: resolvedService,
76
+ timezone: resolvedTimezone,
71
77
  });
72
78
  };
73
79
  return _jsx(_Fragment, { children: children({ navigateToBookingForm }) });
@@ -52,21 +52,23 @@ export declare const Timezone: React.ForwardRefExoticComponent<TimezoneProps & R
52
52
  */
53
53
  export interface RootProps {
54
54
  children: React.ReactNode | ((data: {
55
- navigateToBookingForm: ({ timeSlot, service, }: {
55
+ navigateToBookingForm: ({ timeSlot, service, timezone, }: {
56
56
  timeSlot?: ServiceSelectionTimeSlot;
57
57
  service?: Service;
58
+ timezone?: string;
58
59
  }) => Promise<void>;
59
60
  }) => React.ReactNode);
60
61
  config: TimeSlotListServiceConfig;
61
62
  /**
62
63
  * Function to navigate to the booking form.
63
- * The timeSlot and service parameters are optional - if not provided,
64
+ * The timeSlot, service, and timezone parameters are optional - if not provided,
64
65
  * BookingService will be used to get them from the current service selections.
65
66
  * If not provided, will use the default navigation utility.
66
67
  */
67
- navigateToBookingForm?: ({ timeSlot, service, }: {
68
+ navigateToBookingForm?: ({ timeSlot, service, timezone, }: {
68
69
  timeSlot?: ServiceSelectionTimeSlot;
69
70
  service?: Service;
71
+ timezone?: string;
70
72
  }) => Promise<void>;
71
73
  className?: string;
72
74
  variant?: ListVariant;
@@ -87,11 +87,11 @@ Timezone.displayName = 'TimeSlotList.Timezone';
87
87
  export const Root = React.forwardRef((props, ref) => {
88
88
  const { children, config, className, variant, navigateToBookingForm: providedNavigateToBookingForm, } = props;
89
89
  const navigateToBookingForm = providedNavigateToBookingForm ||
90
- (async ({ timeSlot, service, }) => {
90
+ (async ({ timeSlot, service, timezone, }) => {
91
91
  if (!timeSlot || !service) {
92
92
  throw new Error('timeSlot and service are required when using default navigation. Either provide them or ensure BookingService has service selections.');
93
93
  }
94
- return defaultNavigateToBookingForm({ timeSlot, service });
94
+ return defaultNavigateToBookingForm({ timeSlot, service, timezone });
95
95
  });
96
96
  return (_jsx(CoreTimeSlotList.Root, { config: config, navigateToBookingForm: navigateToBookingForm, children: (coreData) => (_jsx(RootContent, { className: className, ref: ref, variant: variant, children: typeof children === 'function'
97
97
  ? children({
@@ -1,7 +1,17 @@
1
1
  import type { ServiceSelectionTimeSlot } from '../services/booking/booking.js';
2
2
  import type { Service } from '@wix/auto_sdk_bookings_services';
3
3
  export declare function navigateToCheckout(checkoutId: string): Promise<void>;
4
- export declare function navigateToBookingForm({ timeSlot, service, }: {
4
+ /**
5
+ * Cleans a redirect URL by extracting the redirectUrl param and removing:
6
+ * - The 'origin' query parameter
7
+ * - Any query parameters with empty values
8
+ *
9
+ * @param fullUrl - The full URL containing the redirectUrl query parameter
10
+ * @returns The cleaned redirect URL
11
+ */
12
+ export declare function cleanRedirectUrl(fullUrl: string): string;
13
+ export declare function navigateToBookingForm({ timeSlot, service, timezone, }: {
5
14
  timeSlot: ServiceSelectionTimeSlot;
6
15
  service: Service;
16
+ timezone?: string;
7
17
  }): Promise<void>;
@@ -27,7 +27,32 @@ function mapTimeSlotLocationToBookingLocation(timeSlotLocationType) {
27
27
  return BookingsLocationType.UNDEFINED;
28
28
  }
29
29
  }
30
- export async function navigateToBookingForm({ timeSlot, service, }) {
30
+ /**
31
+ * Cleans a redirect URL by extracting the redirectUrl param and removing:
32
+ * - The 'origin' query parameter
33
+ * - Any query parameters with empty values
34
+ *
35
+ * @param fullUrl - The full URL containing the redirectUrl query parameter
36
+ * @returns The cleaned redirect URL
37
+ */
38
+ export function cleanRedirectUrl(fullUrl) {
39
+ const url = new URL(fullUrl);
40
+ const redirectUrl = url.searchParams.get('redirectUrl');
41
+ if (!redirectUrl) {
42
+ return fullUrl;
43
+ }
44
+ const redirectParsed = new URL(redirectUrl);
45
+ // Remove origin param
46
+ redirectParsed.searchParams.delete('origin');
47
+ // Remove empty params
48
+ for (const [key, value] of [...redirectParsed.searchParams.entries()]) {
49
+ if (!value) {
50
+ redirectParsed.searchParams.delete(key);
51
+ }
52
+ }
53
+ return redirectParsed.toString();
54
+ }
55
+ export async function navigateToBookingForm({ timeSlot, service, timezone, }) {
31
56
  if (!timeSlot) {
32
57
  throw new Error('No time slot provided');
33
58
  }
@@ -36,30 +61,36 @@ export async function navigateToBookingForm({ timeSlot, service, }) {
36
61
  }
37
62
  // Get the first resource (staff member) if available
38
63
  const selectedResource = timeSlot.resources?.[0];
64
+ // Destructure to exclude properties we handle separately (location has incompatible types)
65
+ const { location: _location, resources: _resources, ...restTimeSlot } = timeSlot;
66
+ // Build slot object, only including optional properties when they have values
67
+ const slot = {
68
+ ...restTimeSlot,
69
+ scheduleId: timeSlot.scheduleId,
70
+ startDate: timeSlot.startDate,
71
+ endDate: timeSlot.endDate,
72
+ ...(service._id && { serviceId: service._id }),
73
+ ...(timezone && { timezone }),
74
+ ...(selectedResource && {
75
+ resource: {
76
+ _id: selectedResource._id,
77
+ name: selectedResource.name,
78
+ },
79
+ }),
80
+ ...(timeSlot.location && {
81
+ location: {
82
+ _id: timeSlot.location._id,
83
+ name: timeSlot.location.name,
84
+ formattedAddress: timeSlot.location.formattedAddress,
85
+ locationType: mapTimeSlotLocationToBookingLocation(timeSlot.location.locationType),
86
+ },
87
+ }),
88
+ };
39
89
  const { redirectSession } = await redirects.createRedirectSession({
40
90
  bookingsCheckout: {
41
91
  slotAvailability: {
42
92
  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
- },
93
+ slot,
63
94
  },
64
95
  },
65
96
  callbacks: {
@@ -67,7 +98,7 @@ export async function navigateToBookingForm({ timeSlot, service, }) {
67
98
  },
68
99
  });
69
100
  if (redirectSession?.fullUrl) {
70
- window.location.href = redirectSession.fullUrl;
101
+ window.location.href = cleanRedirectUrl(redirectSession.fullUrl);
71
102
  }
72
103
  else {
73
104
  throw new Error('Failed to create redirect session');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-bookings",
3
- "version": "0.0.63",
3
+ "version": "0.0.64",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -72,5 +72,5 @@
72
72
  "groupId": "com.wixpress.headless-components"
73
73
  }
74
74
  },
75
- "falconPackageHash": "96083b204b0065686724c6e3b5716295e35a12be6db784974a6c924d"
75
+ "falconPackageHash": "8011be143d4873e29873f1d8216f8638894a024f49336de8a215b152"
76
76
  }