@wix/headless-bookings 0.0.66 → 0.0.68

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.
@@ -14,13 +14,19 @@ export interface BookProps {
14
14
  label?: string;
15
15
  loadingState?: string;
16
16
  disabled?: boolean;
17
+ /**
18
+ * Called when checkout is required for payment.
19
+ * The navigateToCheckout function already has the checkoutId captured - just call it to navigate.
20
+ */
17
21
  onCheckout?: (result: {
18
- checkoutId: string;
19
- navigateToCheckout: (checkoutId: string) => void;
22
+ navigateToCheckout: () => void;
20
23
  }) => void;
24
+ /**
25
+ * Called when booking completes without requiring checkout (free/offline services).
26
+ * Receives the orderId which can be used to display confirmation or navigate to a thank you page.
27
+ */
21
28
  onComplete?: (result: {
22
29
  orderId: string;
23
- navigateToThankYou: (orderId: string) => void;
24
30
  }) => void;
25
31
  onError?: (error: BookingError) => void;
26
32
  }
@@ -29,20 +35,20 @@ export interface BookProps {
29
35
  *
30
36
  * @example
31
37
  * ```tsx
32
- * // Simple usage
38
+ * // Simple usage - uses default navigation
33
39
  * <Booking.Actions.Book />
34
40
  *
35
- * // With callbacks
41
+ * // With callbacks - navigateToCheckout has checkoutId captured
36
42
  * <Booking.Actions.Book
37
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
38
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
43
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
44
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
39
45
  * onError={(error) => console.error(error.message)}
40
46
  * />
41
47
  *
42
48
  * // With render function
43
49
  * <Booking.Actions.Book
44
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
45
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
50
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
51
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
46
52
  * >
47
53
  * {({ isLoading, error, canBook, onClick, disabled }) => (
48
54
  * <button onClick={onClick} disabled={disabled}>
@@ -54,8 +60,8 @@ export interface BookProps {
54
60
  * // With asChild
55
61
  * <Booking.Actions.Book
56
62
  * asChild
57
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
58
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
63
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
64
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
59
65
  * >
60
66
  * <CustomBookButton />
61
67
  * </Booking.Actions.Book>
@@ -6,7 +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
+ import { navigateToCheckout as navigateToCheckoutUtil } from '../../utils/navigation.js';
10
10
  var TestIds;
11
11
  (function (TestIds) {
12
12
  TestIds["bookingActionBook"] = "booking-action-book";
@@ -16,20 +16,20 @@ var TestIds;
16
16
  *
17
17
  * @example
18
18
  * ```tsx
19
- * // Simple usage
19
+ * // Simple usage - uses default navigation
20
20
  * <Booking.Actions.Book />
21
21
  *
22
- * // With callbacks
22
+ * // With callbacks - navigateToCheckout has checkoutId captured
23
23
  * <Booking.Actions.Book
24
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
25
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
24
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
25
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
26
26
  * onError={(error) => console.error(error.message)}
27
27
  * />
28
28
  *
29
29
  * // With render function
30
30
  * <Booking.Actions.Book
31
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
32
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
31
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
32
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
33
33
  * >
34
34
  * {({ isLoading, error, canBook, onClick, disabled }) => (
35
35
  * <button onClick={onClick} disabled={disabled}>
@@ -41,8 +41,8 @@ var TestIds;
41
41
  * // With asChild
42
42
  * <Booking.Actions.Book
43
43
  * asChild
44
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
45
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
44
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
45
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
46
46
  * >
47
47
  * <CustomBookButton />
48
48
  * </Booking.Actions.Book>
@@ -51,14 +51,11 @@ var TestIds;
51
51
  export const Book = React.forwardRef((props, ref) => {
52
52
  const { asChild, children, className, label = 'Book Now', loadingState = 'Booking...', disabled, onCheckout, onComplete, onError, } = props;
53
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
- },
54
+ onCheckout?.({
55
+ navigateToCheckout: () => navigateToCheckoutUtil(result.checkoutId),
61
56
  });
57
+ }, onComplete: (result) => {
58
+ onComplete?.({ orderId: result.orderId });
62
59
  }, 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 }) })) }));
63
60
  });
64
61
  Book.displayName = 'Booking.Actions.Book';
@@ -44,10 +44,11 @@ export declare const Root: {
44
44
  * // Simple usage
45
45
  * <Booking.Actions.Book />
46
46
  *
47
- * // With callbacks
47
+ * // With callbacks - navigateToCheckout has checkoutId already captured
48
48
  * <Booking.Actions.Book
49
- * onCheckoutRequired={({ checkoutId }) => router.push(`/checkout?id=${checkoutId}`)}
50
- * onError={(error) => toast.error(error)}
49
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
50
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
51
+ * onError={(error) => toast.error(error.message)}
51
52
  * />
52
53
  * ```
53
54
  */
@@ -60,10 +60,11 @@ Root.displayName = 'Booking.Root';
60
60
  * // Simple usage
61
61
  * <Booking.Actions.Book />
62
62
  *
63
- * // With callbacks
63
+ * // With callbacks - navigateToCheckout has checkoutId already captured
64
64
  * <Booking.Actions.Book
65
- * onCheckoutRequired={({ checkoutId }) => router.push(`/checkout?id=${checkoutId}`)}
66
- * onError={(error) => toast.error(error)}
65
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
66
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
67
+ * onError={(error) => toast.error(error.message)}
67
68
  * />
68
69
  * ```
69
70
  */
@@ -32,11 +32,21 @@ export interface BookProps {
32
32
  * Core Book component - provides book action data via render props.
33
33
  * Used internally by higher-level Booking.Actions.Book component.
34
34
  *
35
+ * The onCheckout callback receives the checkoutId for internal use.
36
+ * The high-level component wraps this to provide a simpler API with
37
+ * navigateToCheckout() that has the checkoutId already captured.
38
+ *
35
39
  * @example
36
40
  * ```tsx
37
41
  * <Book
38
- * onCheckout={({ checkoutId }) => navigate(`/checkout?id=${checkoutId}`)}
39
- * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
42
+ * onCheckout={({ checkoutId }) => {
43
+ * // Use checkoutId to navigate to checkout
44
+ * navigateToCheckout(checkoutId);
45
+ * }}
46
+ * onComplete={({ orderId }) => {
47
+ * // Use orderId to navigate to thank you page
48
+ * navigate(`/thank-you?orderId=${orderId}`);
49
+ * }}
40
50
  * >
41
51
  * {({ isLoading, error, canBook, onClick, disabled }) => (
42
52
  * <button onClick={onClick} disabled={disabled}>
@@ -11,11 +11,21 @@ import { BookResultType, } from '../../../services/booking/book-action/types.js'
11
11
  * Core Book component - provides book action data via render props.
12
12
  * Used internally by higher-level Booking.Actions.Book component.
13
13
  *
14
+ * The onCheckout callback receives the checkoutId for internal use.
15
+ * The high-level component wraps this to provide a simpler API with
16
+ * navigateToCheckout() that has the checkoutId already captured.
17
+ *
14
18
  * @example
15
19
  * ```tsx
16
20
  * <Book
17
- * onCheckout={({ checkoutId }) => navigate(`/checkout?id=${checkoutId}`)}
18
- * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
21
+ * onCheckout={({ checkoutId }) => {
22
+ * // Use checkoutId to navigate to checkout
23
+ * navigateToCheckout(checkoutId);
24
+ * }}
25
+ * onComplete={({ orderId }) => {
26
+ * // Use orderId to navigate to thank you page
27
+ * navigate(`/thank-you?orderId=${orderId}`);
28
+ * }}
19
29
  * >
20
30
  * {({ isLoading, error, canBook, onClick, disabled }) => (
21
31
  * <button onClick={onClick} disabled={disabled}>
@@ -59,9 +59,7 @@ export interface DataProps {
59
59
  discount: MoneyData | null;
60
60
  payNow: MoneyData | null;
61
61
  payLater: MoneyData | null;
62
- priceText: string | null;
63
- hasPriceText: boolean;
64
- isFree: boolean;
62
+ customPrice: string | null;
65
63
  hasTax: boolean;
66
64
  hasDiscount: boolean;
67
65
  isDeposit: boolean;
@@ -93,10 +91,8 @@ export interface LineItem {
93
91
  discount: MoneyData | null;
94
92
  /** Deposit amount */
95
93
  deposit: MoneyData | null;
96
- /** Custom price text (for CUSTOM rate type) */
97
- priceText: string | null;
98
- /** Whether this is a free service (NO_FEE rate type) */
99
- isFree: boolean;
94
+ /** Custom price text (for CUSTOM rate type services) */
95
+ customPrice: string | null;
100
96
  }
101
97
  /**
102
98
  * Props for LineItems render prop component
@@ -180,9 +176,7 @@ export interface LineItemInfoProps {
180
176
  tax: MoneyData | null;
181
177
  discount: MoneyData | null;
182
178
  deposit: MoneyData | null;
183
- priceText: string | null;
184
- hasPriceText: boolean;
185
- isFree: boolean;
179
+ customPrice: string | null;
186
180
  hasDiscount: boolean;
187
181
  hasDeposit: boolean;
188
182
  }) => React.ReactNode;
@@ -195,9 +189,9 @@ export interface LineItemInfoProps {
195
189
  * @example
196
190
  * ```tsx
197
191
  * <LineItemInfo>
198
- * {({ totalPrice, priceText, lineItem }) => (
192
+ * {({ totalPrice, customPrice, lineItem }) => (
199
193
  * <div>
200
- * {priceText ? <span>{priceText}</span> : <Money money={totalPrice} />}
194
+ * {customPrice ? <span>{customPrice}</span> : <Money money={totalPrice} />}
201
195
  * </div>
202
196
  * )}
203
197
  * </LineItemInfo>
@@ -77,13 +77,10 @@ export function Data(props) {
77
77
  const discount = convertMultiCurrencyPriceToMoneyData(priceSummary?.discount, currency);
78
78
  const payNow = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payNow?.total, currency);
79
79
  const payLater = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payLater?.total, currency);
80
- // Get priceText and isFree from first line item (single service mode)
80
+ // Get customPrice from first line item (single service mode)
81
81
  const additionalInfoList = paymentDetails?.additionalInfo?.lineItems || [];
82
82
  const firstLineItemInfo = additionalInfoList[0];
83
- const priceText = firstLineItemInfo?.priceText || null;
84
- const hasPriceText = !!priceText;
85
- // Use isFree from service (NO_FEE rate type) if available, otherwise fallback to amount check
86
- const isFree = firstLineItemInfo?.isFree ?? false;
83
+ const customPrice = firstLineItemInfo?.customPrice || null;
87
84
  // Computed flags
88
85
  const hasTax = tax !== null && tax.amount > 0;
89
86
  const hasDiscount = discount !== null && discount.amount > 0;
@@ -95,9 +92,7 @@ export function Data(props) {
95
92
  discount,
96
93
  payNow,
97
94
  payLater,
98
- priceText,
99
- hasPriceText,
100
- isFree,
95
+ customPrice,
101
96
  hasTax,
102
97
  hasDiscount,
103
98
  isDeposit,
@@ -145,8 +140,7 @@ export function LineItems(props) {
145
140
  tax: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.taxDetails?.totalTax, currency),
146
141
  discount: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.totalDiscount, currency),
147
142
  deposit: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.depositAmount, currency),
148
- priceText: additionalInfo?.priceText || null,
149
- isFree: additionalInfo?.isFree ?? false,
143
+ customPrice: additionalInfo?.customPrice || null,
150
144
  };
151
145
  });
152
146
  const hasLineItems = lineItems.length > 0;
@@ -202,9 +196,9 @@ export function LineItemProvider(props) {
202
196
  * @example
203
197
  * ```tsx
204
198
  * <LineItemInfo>
205
- * {({ totalPrice, priceText, lineItem }) => (
199
+ * {({ totalPrice, customPrice, lineItem }) => (
206
200
  * <div>
207
- * {priceText ? <span>{priceText}</span> : <Money money={totalPrice} />}
201
+ * {customPrice ? <span>{customPrice}</span> : <Money money={totalPrice} />}
208
202
  * </div>
209
203
  * )}
210
204
  * </LineItemInfo>
@@ -213,7 +207,6 @@ export function LineItemProvider(props) {
213
207
  export function LineItemInfo(props) {
214
208
  const { lineItem, currency } = useLineItemContext();
215
209
  // Computed flags
216
- const hasPriceText = !!lineItem.priceText;
217
210
  const hasDiscount = lineItem.discount !== null && lineItem.discount.amount > 0;
218
211
  const hasDeposit = lineItem.deposit !== null && lineItem.deposit.amount > 0;
219
212
  return props.children({
@@ -224,9 +217,7 @@ export function LineItemInfo(props) {
224
217
  tax: lineItem.tax,
225
218
  discount: lineItem.discount,
226
219
  deposit: lineItem.deposit,
227
- priceText: lineItem.priceText,
228
- hasPriceText,
229
- isFree: lineItem.isFree,
220
+ customPrice: lineItem.customPrice,
230
221
  hasDiscount,
231
222
  hasDeposit,
232
223
  });
@@ -63,15 +63,13 @@ export interface SubtotalProps {
63
63
  asChild?: boolean;
64
64
  children?: AsChildChildren<{
65
65
  subtotal: MoneyData | null;
66
- priceText: string | null;
67
- hasPriceText: boolean;
68
- isFree: boolean;
66
+ customPrice: string | null;
69
67
  }>;
70
68
  className?: string;
71
69
  }
72
70
  /**
73
71
  * Displays the payment subtotal.
74
- * Always displays the monetary value but exposes priceText, hasPriceText, and isFree for custom rendering.
72
+ * Always displays the monetary value. Exposes customPrice for custom rendering.
75
73
  *
76
74
  * @component
77
75
  * @example
@@ -81,10 +79,9 @@ export interface SubtotalProps {
81
79
  * @example
82
80
  * ```tsx
83
81
  * <Payment.Subtotal asChild>
84
- * {({ subtotal, priceText, hasPriceText, isFree }) => (
82
+ * {({ subtotal, customPrice }) => (
85
83
  * <div>
86
- * <Money money={subtotal} />
87
- * {hasPriceText && <span className="text-sm">({priceText})</span>}
84
+ * {customPrice ? <span>{customPrice}</span> : <Money money={subtotal} />}
88
85
  * </div>
89
86
  * )}
90
87
  * </Payment.Subtotal>
@@ -140,15 +137,13 @@ export interface TotalProps {
140
137
  asChild?: boolean;
141
138
  children?: AsChildChildren<{
142
139
  total: MoneyData | null;
143
- priceText: string | null;
144
- hasPriceText: boolean;
145
- isFree: boolean;
140
+ customPrice: string | null;
146
141
  }>;
147
142
  className?: string;
148
143
  }
149
144
  /**
150
145
  * Displays the payment total.
151
- * Always displays the monetary value but exposes priceText, hasPriceText, and isFree for custom rendering.
146
+ * Always displays the monetary value. Exposes customPrice for custom rendering.
152
147
  *
153
148
  * @component
154
149
  * @example
@@ -158,14 +153,9 @@ export interface TotalProps {
158
153
  * @example
159
154
  * ```tsx
160
155
  * <Payment.Total asChild>
161
- * {({ total, priceText, hasPriceText, isFree }) => (
156
+ * {({ total, customPrice }) => (
162
157
  * <div>
163
- * {isFree ? (
164
- * <span className="text-green-500">Free</span>
165
- * ) : (
166
- * <Money money={total} />
167
- * )}
168
- * {hasPriceText && <span className="text-sm">({priceText})</span>}
158
+ * {customPrice ? <span>{customPrice}</span> : <Money money={total} />}
169
159
  * </div>
170
160
  * )}
171
161
  * </Payment.Total>
@@ -302,15 +292,13 @@ export interface LineItemTotalProps {
302
292
  asChild?: boolean;
303
293
  children?: AsChildChildren<{
304
294
  totalPrice: MoneyData | null;
305
- priceText: string | null;
306
- hasPriceText: boolean;
307
- isFree: boolean;
295
+ customPrice: string | null;
308
296
  }>;
309
297
  className?: string;
310
298
  }
311
299
  /**
312
300
  * Displays the line item total (total price after tax).
313
- * Shows priceText for CUSTOM rate type services, otherwise shows totalPrice.
301
+ * Shows customPrice for CUSTOM rate type services, otherwise shows totalPrice.
314
302
  *
315
303
  * @component
316
304
  * @example
@@ -326,15 +314,13 @@ export interface LineItemSubtotalProps {
326
314
  asChild?: boolean;
327
315
  children?: AsChildChildren<{
328
316
  subtotal: MoneyData | null;
329
- priceText: string | null;
330
- hasPriceText: boolean;
331
- isFree: boolean;
317
+ customPrice: string | null;
332
318
  }>;
333
319
  className?: string;
334
320
  }
335
321
  /**
336
322
  * Displays the line item subtotal (full price before discounts).
337
- * Shows priceText for CUSTOM rate type services, otherwise shows subtotal.
323
+ * Shows customPrice for CUSTOM rate type services, otherwise shows subtotal.
338
324
  *
339
325
  * @component
340
326
  * @example
@@ -65,7 +65,7 @@ export const Root = React.forwardRef((props, ref) => {
65
65
  Root.displayName = 'Payment.Root';
66
66
  /**
67
67
  * Displays the payment subtotal.
68
- * Always displays the monetary value but exposes priceText, hasPriceText, and isFree for custom rendering.
68
+ * Always displays the monetary value. Exposes customPrice for custom rendering.
69
69
  *
70
70
  * @component
71
71
  * @example
@@ -75,10 +75,9 @@ Root.displayName = 'Payment.Root';
75
75
  * @example
76
76
  * ```tsx
77
77
  * <Payment.Subtotal asChild>
78
- * {({ subtotal, priceText, hasPriceText, isFree }) => (
78
+ * {({ subtotal, customPrice }) => (
79
79
  * <div>
80
- * <Money money={subtotal} />
81
- * {hasPriceText && <span className="text-sm">({priceText})</span>}
80
+ * {customPrice ? <span>{customPrice}</span> : <Money money={subtotal} />}
82
81
  * </div>
83
82
  * )}
84
83
  * </Payment.Subtotal>
@@ -86,7 +85,10 @@ Root.displayName = 'Payment.Root';
86
85
  */
87
86
  export const Subtotal = React.forwardRef((props, ref) => {
88
87
  const { asChild, children, className } = props;
89
- return (_jsx(CorePayment.Data, { children: ({ subtotal, priceText, hasPriceText, isFree }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentSubtotal, "data-has-price-text": hasPriceText, "data-is-free": isFree, customElement: children, customElementProps: { subtotal, priceText, hasPriceText, isFree }, children: subtotal && _jsx(Money, { money: subtotal }) })) }));
88
+ return (_jsx(CorePayment.Data, { children: ({ subtotal, customPrice }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentSubtotal, "data-has-custom-price": !!customPrice, customElement: children, customElementProps: {
89
+ subtotal,
90
+ customPrice,
91
+ }, children: subtotal && _jsx(Money, { money: subtotal }) })) }));
90
92
  });
91
93
  Subtotal.displayName = 'Payment.Subtotal';
92
94
  /**
@@ -121,7 +123,7 @@ export const Discount = React.forwardRef((props, ref) => {
121
123
  Discount.displayName = 'Payment.Discount';
122
124
  /**
123
125
  * Displays the payment total.
124
- * Always displays the monetary value but exposes priceText, hasPriceText, and isFree for custom rendering.
126
+ * Always displays the monetary value. Exposes customPrice for custom rendering.
125
127
  *
126
128
  * @component
127
129
  * @example
@@ -131,14 +133,9 @@ Discount.displayName = 'Payment.Discount';
131
133
  * @example
132
134
  * ```tsx
133
135
  * <Payment.Total asChild>
134
- * {({ total, priceText, hasPriceText, isFree }) => (
136
+ * {({ total, customPrice }) => (
135
137
  * <div>
136
- * {isFree ? (
137
- * <span className="text-green-500">Free</span>
138
- * ) : (
139
- * <Money money={total} />
140
- * )}
141
- * {hasPriceText && <span className="text-sm">({priceText})</span>}
138
+ * {customPrice ? <span>{customPrice}</span> : <Money money={total} />}
142
139
  * </div>
143
140
  * )}
144
141
  * </Payment.Total>
@@ -146,7 +143,10 @@ Discount.displayName = 'Payment.Discount';
146
143
  */
147
144
  export const Total = React.forwardRef((props, ref) => {
148
145
  const { asChild, children, className } = props;
149
- return (_jsx(CorePayment.Data, { children: ({ total, priceText, hasPriceText, isFree }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentTotal, "data-has-price-text": hasPriceText, "data-is-free": isFree, customElement: children, customElementProps: { total, priceText, hasPriceText, isFree }, children: total && _jsx(Money, { money: total }) })) }));
146
+ return (_jsx(CorePayment.Data, { children: ({ total, customPrice }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentTotal, "data-has-custom-price": !!customPrice, customElement: children, customElementProps: {
147
+ total,
148
+ customPrice,
149
+ }, children: total && _jsx(Money, { money: total }) })) }));
150
150
  });
151
151
  Total.displayName = 'Payment.Total';
152
152
  /**
@@ -261,7 +261,7 @@ export const LineItemRepeater = React.forwardRef((props, ref) => {
261
261
  LineItemRepeater.displayName = 'Payment.LineItemRepeater';
262
262
  /**
263
263
  * Displays the line item total (total price after tax).
264
- * Shows priceText for CUSTOM rate type services, otherwise shows totalPrice.
264
+ * Shows customPrice for CUSTOM rate type services, otherwise shows totalPrice.
265
265
  *
266
266
  * @component
267
267
  * @example
@@ -271,12 +271,12 @@ LineItemRepeater.displayName = 'Payment.LineItemRepeater';
271
271
  */
272
272
  export const LineItemTotal = React.forwardRef((props, ref) => {
273
273
  const { asChild, children, className } = props;
274
- return (_jsx(CorePayment.LineItemInfo, { children: ({ totalPrice, priceText, hasPriceText, isFree }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemTotal, "data-has-price-text": hasPriceText, "data-is-free": isFree, customElement: children, customElementProps: { totalPrice, priceText, hasPriceText, isFree }, children: priceText ? (_jsx("span", { children: priceText })) : (totalPrice && _jsx(Money, { money: totalPrice })) })) }));
274
+ return (_jsx(CorePayment.LineItemInfo, { children: ({ totalPrice, customPrice }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemTotal, "data-has-custom-price": !!customPrice, customElement: children, customElementProps: { totalPrice, customPrice }, children: customPrice ? (_jsx("span", { children: customPrice })) : (totalPrice && _jsx(Money, { money: totalPrice })) })) }));
275
275
  });
276
276
  LineItemTotal.displayName = 'Payment.LineItemTotal';
277
277
  /**
278
278
  * Displays the line item subtotal (full price before discounts).
279
- * Shows priceText for CUSTOM rate type services, otherwise shows subtotal.
279
+ * Shows customPrice for CUSTOM rate type services, otherwise shows subtotal.
280
280
  *
281
281
  * @component
282
282
  * @example
@@ -286,7 +286,7 @@ LineItemTotal.displayName = 'Payment.LineItemTotal';
286
286
  */
287
287
  export const LineItemSubtotal = React.forwardRef((props, ref) => {
288
288
  const { asChild, children, className } = props;
289
- return (_jsx(CorePayment.LineItemInfo, { children: ({ subtotal, priceText, hasPriceText, isFree }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemSubtotal, "data-has-price-text": hasPriceText, "data-is-free": isFree, customElement: children, customElementProps: { subtotal, priceText, hasPriceText, isFree }, children: priceText ? (_jsx("span", { children: priceText })) : (subtotal && _jsx(Money, { money: subtotal })) })) }));
289
+ return (_jsx(CorePayment.LineItemInfo, { children: ({ subtotal, customPrice }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemSubtotal, "data-has-custom-price": !!customPrice, customElement: children, customElementProps: { subtotal, customPrice }, children: customPrice ? (_jsx("span", { children: customPrice })) : (subtotal && _jsx(Money, { money: subtotal })) })) }));
290
290
  });
291
291
  LineItemSubtotal.displayName = 'Payment.LineItemSubtotal';
292
292
  /**
@@ -44,7 +44,7 @@ export interface BookChildProps {
44
44
  isLoading: boolean;
45
45
  error: BookingError | null;
46
46
  canBook: boolean;
47
- onClick: () => void;
47
+ onClick: () => Promise<void>;
48
48
  disabled: boolean;
49
49
  }
50
50
  /**
@@ -57,9 +57,17 @@ export interface BookProps {
57
57
  label?: string;
58
58
  loadingState?: string;
59
59
  disabled?: boolean;
60
+ /**
61
+ * Called when checkout is required for payment.
62
+ * The navigateToCheckout function has the checkoutId captured - just call it.
63
+ */
60
64
  onCheckout?: (result: {
61
- checkoutId: string;
65
+ navigateToCheckout: () => void;
62
66
  }) => void;
67
+ /**
68
+ * Called when booking completes without requiring checkout (free/offline services).
69
+ * Receives the orderId which can be used to display confirmation or navigate to a thank you page.
70
+ */
63
71
  onComplete?: (result: {
64
72
  orderId: string;
65
73
  }) => void;
@@ -15,10 +15,8 @@ export type PaymentOption = 'FULL_PAYMENT_ONLINE' | 'FULL_PAYMENT_OFFLINE' | 'DE
15
15
  export interface LineItemAdditionalInfo {
16
16
  /** Line item ID to match with SDK's calculatedLineItems */
17
17
  lineItemId: string;
18
- /** Custom price text (for CUSTOM rate type services) */
19
- priceText?: string;
20
- /** Whether this is a free service (NO_FEE rate type) */
21
- isFree?: boolean;
18
+ /** Custom price (for CUSTOM rate type services) */
19
+ customPrice?: string;
22
20
  }
23
21
  /**
24
22
  * Payment calculation result with SDK response and additional computed info
@@ -20,11 +20,10 @@ const BOOKINGS_APP_DEF_ID = '13d21c63-b5ec-5912-8397-c3a5ddb27a97';
20
20
  // ============================================================================
21
21
  function getAdditionalInfo(service, lineItemId) {
22
22
  const payment = service.payment;
23
- const priceText = payment?.rateType === RateType.CUSTOM && payment.custom?.description
23
+ const customPrice = payment?.rateType === RateType.CUSTOM && payment.custom?.description
24
24
  ? payment.custom.description
25
25
  : undefined;
26
- const isFree = payment?.rateType === RateType.NO_FEE;
27
- return { lineItemId, priceText, isFree };
26
+ return { lineItemId, customPrice };
28
27
  }
29
28
  function getPaymentOption(service, hasDeposit) {
30
29
  const options = service.payment?.options;
@@ -5,7 +5,8 @@ export async function navigateToCheckout(checkoutId) {
5
5
  const { redirectSession } = await redirects.createRedirectSession({
6
6
  ecomCheckout: { checkoutId },
7
7
  callbacks: {
8
- postFlowUrl: window.location.href,
8
+ postFlowUrl: `${window.location.host}/booking/services`,
9
+ bookingsServiceListUrl: `${window.location.host}/booking/services`,
9
10
  },
10
11
  });
11
12
  if (redirectSession?.fullUrl) {
@@ -14,13 +14,19 @@ export interface BookProps {
14
14
  label?: string;
15
15
  loadingState?: string;
16
16
  disabled?: boolean;
17
+ /**
18
+ * Called when checkout is required for payment.
19
+ * The navigateToCheckout function already has the checkoutId captured - just call it to navigate.
20
+ */
17
21
  onCheckout?: (result: {
18
- checkoutId: string;
19
- navigateToCheckout: (checkoutId: string) => void;
22
+ navigateToCheckout: () => void;
20
23
  }) => void;
24
+ /**
25
+ * Called when booking completes without requiring checkout (free/offline services).
26
+ * Receives the orderId which can be used to display confirmation or navigate to a thank you page.
27
+ */
21
28
  onComplete?: (result: {
22
29
  orderId: string;
23
- navigateToThankYou: (orderId: string) => void;
24
30
  }) => void;
25
31
  onError?: (error: BookingError) => void;
26
32
  }
@@ -29,20 +35,20 @@ export interface BookProps {
29
35
  *
30
36
  * @example
31
37
  * ```tsx
32
- * // Simple usage
38
+ * // Simple usage - uses default navigation
33
39
  * <Booking.Actions.Book />
34
40
  *
35
- * // With callbacks
41
+ * // With callbacks - navigateToCheckout has checkoutId captured
36
42
  * <Booking.Actions.Book
37
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
38
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
43
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
44
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
39
45
  * onError={(error) => console.error(error.message)}
40
46
  * />
41
47
  *
42
48
  * // With render function
43
49
  * <Booking.Actions.Book
44
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
45
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
50
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
51
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
46
52
  * >
47
53
  * {({ isLoading, error, canBook, onClick, disabled }) => (
48
54
  * <button onClick={onClick} disabled={disabled}>
@@ -54,8 +60,8 @@ export interface BookProps {
54
60
  * // With asChild
55
61
  * <Booking.Actions.Book
56
62
  * asChild
57
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
58
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
63
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
64
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
59
65
  * >
60
66
  * <CustomBookButton />
61
67
  * </Booking.Actions.Book>
@@ -6,7 +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
+ import { navigateToCheckout as navigateToCheckoutUtil } from '../../utils/navigation.js';
10
10
  var TestIds;
11
11
  (function (TestIds) {
12
12
  TestIds["bookingActionBook"] = "booking-action-book";
@@ -16,20 +16,20 @@ var TestIds;
16
16
  *
17
17
  * @example
18
18
  * ```tsx
19
- * // Simple usage
19
+ * // Simple usage - uses default navigation
20
20
  * <Booking.Actions.Book />
21
21
  *
22
- * // With callbacks
22
+ * // With callbacks - navigateToCheckout has checkoutId captured
23
23
  * <Booking.Actions.Book
24
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
25
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
24
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
25
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
26
26
  * onError={(error) => console.error(error.message)}
27
27
  * />
28
28
  *
29
29
  * // With render function
30
30
  * <Booking.Actions.Book
31
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
32
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
31
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
32
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
33
33
  * >
34
34
  * {({ isLoading, error, canBook, onClick, disabled }) => (
35
35
  * <button onClick={onClick} disabled={disabled}>
@@ -41,8 +41,8 @@ var TestIds;
41
41
  * // With asChild
42
42
  * <Booking.Actions.Book
43
43
  * asChild
44
- * onCheckout={({ checkoutId, navigateToCheckout }) => navigateToCheckout(checkoutId)}
45
- * onComplete={({ orderId }) => router.push(`/thank-you?orderId=${orderId}`)}
44
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
45
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
46
46
  * >
47
47
  * <CustomBookButton />
48
48
  * </Booking.Actions.Book>
@@ -51,14 +51,11 @@ var TestIds;
51
51
  export const Book = React.forwardRef((props, ref) => {
52
52
  const { asChild, children, className, label = 'Book Now', loadingState = 'Booking...', disabled, onCheckout, onComplete, onError, } = props;
53
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
- },
54
+ onCheckout?.({
55
+ navigateToCheckout: () => navigateToCheckoutUtil(result.checkoutId),
61
56
  });
57
+ }, onComplete: (result) => {
58
+ onComplete?.({ orderId: result.orderId });
62
59
  }, 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 }) })) }));
63
60
  });
64
61
  Book.displayName = 'Booking.Actions.Book';
@@ -44,10 +44,11 @@ export declare const Root: {
44
44
  * // Simple usage
45
45
  * <Booking.Actions.Book />
46
46
  *
47
- * // With callbacks
47
+ * // With callbacks - navigateToCheckout has checkoutId already captured
48
48
  * <Booking.Actions.Book
49
- * onCheckoutRequired={({ checkoutId }) => router.push(`/checkout?id=${checkoutId}`)}
50
- * onError={(error) => toast.error(error)}
49
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
50
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
51
+ * onError={(error) => toast.error(error.message)}
51
52
  * />
52
53
  * ```
53
54
  */
@@ -60,10 +60,11 @@ Root.displayName = 'Booking.Root';
60
60
  * // Simple usage
61
61
  * <Booking.Actions.Book />
62
62
  *
63
- * // With callbacks
63
+ * // With callbacks - navigateToCheckout has checkoutId already captured
64
64
  * <Booking.Actions.Book
65
- * onCheckoutRequired={({ checkoutId }) => router.push(`/checkout?id=${checkoutId}`)}
66
- * onError={(error) => toast.error(error)}
65
+ * onCheckout={({ navigateToCheckout }) => navigateToCheckout()}
66
+ * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
67
+ * onError={(error) => toast.error(error.message)}
67
68
  * />
68
69
  * ```
69
70
  */
@@ -32,11 +32,21 @@ export interface BookProps {
32
32
  * Core Book component - provides book action data via render props.
33
33
  * Used internally by higher-level Booking.Actions.Book component.
34
34
  *
35
+ * The onCheckout callback receives the checkoutId for internal use.
36
+ * The high-level component wraps this to provide a simpler API with
37
+ * navigateToCheckout() that has the checkoutId already captured.
38
+ *
35
39
  * @example
36
40
  * ```tsx
37
41
  * <Book
38
- * onCheckout={({ checkoutId }) => navigate(`/checkout?id=${checkoutId}`)}
39
- * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
42
+ * onCheckout={({ checkoutId }) => {
43
+ * // Use checkoutId to navigate to checkout
44
+ * navigateToCheckout(checkoutId);
45
+ * }}
46
+ * onComplete={({ orderId }) => {
47
+ * // Use orderId to navigate to thank you page
48
+ * navigate(`/thank-you?orderId=${orderId}`);
49
+ * }}
40
50
  * >
41
51
  * {({ isLoading, error, canBook, onClick, disabled }) => (
42
52
  * <button onClick={onClick} disabled={disabled}>
@@ -11,11 +11,21 @@ import { BookResultType, } from '../../../services/booking/book-action/types.js'
11
11
  * Core Book component - provides book action data via render props.
12
12
  * Used internally by higher-level Booking.Actions.Book component.
13
13
  *
14
+ * The onCheckout callback receives the checkoutId for internal use.
15
+ * The high-level component wraps this to provide a simpler API with
16
+ * navigateToCheckout() that has the checkoutId already captured.
17
+ *
14
18
  * @example
15
19
  * ```tsx
16
20
  * <Book
17
- * onCheckout={({ checkoutId }) => navigate(`/checkout?id=${checkoutId}`)}
18
- * onComplete={({ orderId }) => navigate(`/thank-you?orderId=${orderId}`)}
21
+ * onCheckout={({ checkoutId }) => {
22
+ * // Use checkoutId to navigate to checkout
23
+ * navigateToCheckout(checkoutId);
24
+ * }}
25
+ * onComplete={({ orderId }) => {
26
+ * // Use orderId to navigate to thank you page
27
+ * navigate(`/thank-you?orderId=${orderId}`);
28
+ * }}
19
29
  * >
20
30
  * {({ isLoading, error, canBook, onClick, disabled }) => (
21
31
  * <button onClick={onClick} disabled={disabled}>
@@ -59,9 +59,7 @@ export interface DataProps {
59
59
  discount: MoneyData | null;
60
60
  payNow: MoneyData | null;
61
61
  payLater: MoneyData | null;
62
- priceText: string | null;
63
- hasPriceText: boolean;
64
- isFree: boolean;
62
+ customPrice: string | null;
65
63
  hasTax: boolean;
66
64
  hasDiscount: boolean;
67
65
  isDeposit: boolean;
@@ -93,10 +91,8 @@ export interface LineItem {
93
91
  discount: MoneyData | null;
94
92
  /** Deposit amount */
95
93
  deposit: MoneyData | null;
96
- /** Custom price text (for CUSTOM rate type) */
97
- priceText: string | null;
98
- /** Whether this is a free service (NO_FEE rate type) */
99
- isFree: boolean;
94
+ /** Custom price text (for CUSTOM rate type services) */
95
+ customPrice: string | null;
100
96
  }
101
97
  /**
102
98
  * Props for LineItems render prop component
@@ -180,9 +176,7 @@ export interface LineItemInfoProps {
180
176
  tax: MoneyData | null;
181
177
  discount: MoneyData | null;
182
178
  deposit: MoneyData | null;
183
- priceText: string | null;
184
- hasPriceText: boolean;
185
- isFree: boolean;
179
+ customPrice: string | null;
186
180
  hasDiscount: boolean;
187
181
  hasDeposit: boolean;
188
182
  }) => React.ReactNode;
@@ -195,9 +189,9 @@ export interface LineItemInfoProps {
195
189
  * @example
196
190
  * ```tsx
197
191
  * <LineItemInfo>
198
- * {({ totalPrice, priceText, lineItem }) => (
192
+ * {({ totalPrice, customPrice, lineItem }) => (
199
193
  * <div>
200
- * {priceText ? <span>{priceText}</span> : <Money money={totalPrice} />}
194
+ * {customPrice ? <span>{customPrice}</span> : <Money money={totalPrice} />}
201
195
  * </div>
202
196
  * )}
203
197
  * </LineItemInfo>
@@ -77,13 +77,10 @@ export function Data(props) {
77
77
  const discount = convertMultiCurrencyPriceToMoneyData(priceSummary?.discount, currency);
78
78
  const payNow = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payNow?.total, currency);
79
79
  const payLater = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payLater?.total, currency);
80
- // Get priceText and isFree from first line item (single service mode)
80
+ // Get customPrice from first line item (single service mode)
81
81
  const additionalInfoList = paymentDetails?.additionalInfo?.lineItems || [];
82
82
  const firstLineItemInfo = additionalInfoList[0];
83
- const priceText = firstLineItemInfo?.priceText || null;
84
- const hasPriceText = !!priceText;
85
- // Use isFree from service (NO_FEE rate type) if available, otherwise fallback to amount check
86
- const isFree = firstLineItemInfo?.isFree ?? false;
83
+ const customPrice = firstLineItemInfo?.customPrice || null;
87
84
  // Computed flags
88
85
  const hasTax = tax !== null && tax.amount > 0;
89
86
  const hasDiscount = discount !== null && discount.amount > 0;
@@ -95,9 +92,7 @@ export function Data(props) {
95
92
  discount,
96
93
  payNow,
97
94
  payLater,
98
- priceText,
99
- hasPriceText,
100
- isFree,
95
+ customPrice,
101
96
  hasTax,
102
97
  hasDiscount,
103
98
  isDeposit,
@@ -145,8 +140,7 @@ export function LineItems(props) {
145
140
  tax: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.taxDetails?.totalTax, currency),
146
141
  discount: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.totalDiscount, currency),
147
142
  deposit: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.depositAmount, currency),
148
- priceText: additionalInfo?.priceText || null,
149
- isFree: additionalInfo?.isFree ?? false,
143
+ customPrice: additionalInfo?.customPrice || null,
150
144
  };
151
145
  });
152
146
  const hasLineItems = lineItems.length > 0;
@@ -202,9 +196,9 @@ export function LineItemProvider(props) {
202
196
  * @example
203
197
  * ```tsx
204
198
  * <LineItemInfo>
205
- * {({ totalPrice, priceText, lineItem }) => (
199
+ * {({ totalPrice, customPrice, lineItem }) => (
206
200
  * <div>
207
- * {priceText ? <span>{priceText}</span> : <Money money={totalPrice} />}
201
+ * {customPrice ? <span>{customPrice}</span> : <Money money={totalPrice} />}
208
202
  * </div>
209
203
  * )}
210
204
  * </LineItemInfo>
@@ -213,7 +207,6 @@ export function LineItemProvider(props) {
213
207
  export function LineItemInfo(props) {
214
208
  const { lineItem, currency } = useLineItemContext();
215
209
  // Computed flags
216
- const hasPriceText = !!lineItem.priceText;
217
210
  const hasDiscount = lineItem.discount !== null && lineItem.discount.amount > 0;
218
211
  const hasDeposit = lineItem.deposit !== null && lineItem.deposit.amount > 0;
219
212
  return props.children({
@@ -224,9 +217,7 @@ export function LineItemInfo(props) {
224
217
  tax: lineItem.tax,
225
218
  discount: lineItem.discount,
226
219
  deposit: lineItem.deposit,
227
- priceText: lineItem.priceText,
228
- hasPriceText,
229
- isFree: lineItem.isFree,
220
+ customPrice: lineItem.customPrice,
230
221
  hasDiscount,
231
222
  hasDeposit,
232
223
  });
@@ -63,15 +63,13 @@ export interface SubtotalProps {
63
63
  asChild?: boolean;
64
64
  children?: AsChildChildren<{
65
65
  subtotal: MoneyData | null;
66
- priceText: string | null;
67
- hasPriceText: boolean;
68
- isFree: boolean;
66
+ customPrice: string | null;
69
67
  }>;
70
68
  className?: string;
71
69
  }
72
70
  /**
73
71
  * Displays the payment subtotal.
74
- * Always displays the monetary value but exposes priceText, hasPriceText, and isFree for custom rendering.
72
+ * Always displays the monetary value. Exposes customPrice for custom rendering.
75
73
  *
76
74
  * @component
77
75
  * @example
@@ -81,10 +79,9 @@ export interface SubtotalProps {
81
79
  * @example
82
80
  * ```tsx
83
81
  * <Payment.Subtotal asChild>
84
- * {({ subtotal, priceText, hasPriceText, isFree }) => (
82
+ * {({ subtotal, customPrice }) => (
85
83
  * <div>
86
- * <Money money={subtotal} />
87
- * {hasPriceText && <span className="text-sm">({priceText})</span>}
84
+ * {customPrice ? <span>{customPrice}</span> : <Money money={subtotal} />}
88
85
  * </div>
89
86
  * )}
90
87
  * </Payment.Subtotal>
@@ -140,15 +137,13 @@ export interface TotalProps {
140
137
  asChild?: boolean;
141
138
  children?: AsChildChildren<{
142
139
  total: MoneyData | null;
143
- priceText: string | null;
144
- hasPriceText: boolean;
145
- isFree: boolean;
140
+ customPrice: string | null;
146
141
  }>;
147
142
  className?: string;
148
143
  }
149
144
  /**
150
145
  * Displays the payment total.
151
- * Always displays the monetary value but exposes priceText, hasPriceText, and isFree for custom rendering.
146
+ * Always displays the monetary value. Exposes customPrice for custom rendering.
152
147
  *
153
148
  * @component
154
149
  * @example
@@ -158,14 +153,9 @@ export interface TotalProps {
158
153
  * @example
159
154
  * ```tsx
160
155
  * <Payment.Total asChild>
161
- * {({ total, priceText, hasPriceText, isFree }) => (
156
+ * {({ total, customPrice }) => (
162
157
  * <div>
163
- * {isFree ? (
164
- * <span className="text-green-500">Free</span>
165
- * ) : (
166
- * <Money money={total} />
167
- * )}
168
- * {hasPriceText && <span className="text-sm">({priceText})</span>}
158
+ * {customPrice ? <span>{customPrice}</span> : <Money money={total} />}
169
159
  * </div>
170
160
  * )}
171
161
  * </Payment.Total>
@@ -302,15 +292,13 @@ export interface LineItemTotalProps {
302
292
  asChild?: boolean;
303
293
  children?: AsChildChildren<{
304
294
  totalPrice: MoneyData | null;
305
- priceText: string | null;
306
- hasPriceText: boolean;
307
- isFree: boolean;
295
+ customPrice: string | null;
308
296
  }>;
309
297
  className?: string;
310
298
  }
311
299
  /**
312
300
  * Displays the line item total (total price after tax).
313
- * Shows priceText for CUSTOM rate type services, otherwise shows totalPrice.
301
+ * Shows customPrice for CUSTOM rate type services, otherwise shows totalPrice.
314
302
  *
315
303
  * @component
316
304
  * @example
@@ -326,15 +314,13 @@ export interface LineItemSubtotalProps {
326
314
  asChild?: boolean;
327
315
  children?: AsChildChildren<{
328
316
  subtotal: MoneyData | null;
329
- priceText: string | null;
330
- hasPriceText: boolean;
331
- isFree: boolean;
317
+ customPrice: string | null;
332
318
  }>;
333
319
  className?: string;
334
320
  }
335
321
  /**
336
322
  * Displays the line item subtotal (full price before discounts).
337
- * Shows priceText for CUSTOM rate type services, otherwise shows subtotal.
323
+ * Shows customPrice for CUSTOM rate type services, otherwise shows subtotal.
338
324
  *
339
325
  * @component
340
326
  * @example
@@ -65,7 +65,7 @@ export const Root = React.forwardRef((props, ref) => {
65
65
  Root.displayName = 'Payment.Root';
66
66
  /**
67
67
  * Displays the payment subtotal.
68
- * Always displays the monetary value but exposes priceText, hasPriceText, and isFree for custom rendering.
68
+ * Always displays the monetary value. Exposes customPrice for custom rendering.
69
69
  *
70
70
  * @component
71
71
  * @example
@@ -75,10 +75,9 @@ Root.displayName = 'Payment.Root';
75
75
  * @example
76
76
  * ```tsx
77
77
  * <Payment.Subtotal asChild>
78
- * {({ subtotal, priceText, hasPriceText, isFree }) => (
78
+ * {({ subtotal, customPrice }) => (
79
79
  * <div>
80
- * <Money money={subtotal} />
81
- * {hasPriceText && <span className="text-sm">({priceText})</span>}
80
+ * {customPrice ? <span>{customPrice}</span> : <Money money={subtotal} />}
82
81
  * </div>
83
82
  * )}
84
83
  * </Payment.Subtotal>
@@ -86,7 +85,10 @@ Root.displayName = 'Payment.Root';
86
85
  */
87
86
  export const Subtotal = React.forwardRef((props, ref) => {
88
87
  const { asChild, children, className } = props;
89
- return (_jsx(CorePayment.Data, { children: ({ subtotal, priceText, hasPriceText, isFree }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentSubtotal, "data-has-price-text": hasPriceText, "data-is-free": isFree, customElement: children, customElementProps: { subtotal, priceText, hasPriceText, isFree }, children: subtotal && _jsx(Money, { money: subtotal }) })) }));
88
+ return (_jsx(CorePayment.Data, { children: ({ subtotal, customPrice }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentSubtotal, "data-has-custom-price": !!customPrice, customElement: children, customElementProps: {
89
+ subtotal,
90
+ customPrice,
91
+ }, children: subtotal && _jsx(Money, { money: subtotal }) })) }));
90
92
  });
91
93
  Subtotal.displayName = 'Payment.Subtotal';
92
94
  /**
@@ -121,7 +123,7 @@ export const Discount = React.forwardRef((props, ref) => {
121
123
  Discount.displayName = 'Payment.Discount';
122
124
  /**
123
125
  * Displays the payment total.
124
- * Always displays the monetary value but exposes priceText, hasPriceText, and isFree for custom rendering.
126
+ * Always displays the monetary value. Exposes customPrice for custom rendering.
125
127
  *
126
128
  * @component
127
129
  * @example
@@ -131,14 +133,9 @@ Discount.displayName = 'Payment.Discount';
131
133
  * @example
132
134
  * ```tsx
133
135
  * <Payment.Total asChild>
134
- * {({ total, priceText, hasPriceText, isFree }) => (
136
+ * {({ total, customPrice }) => (
135
137
  * <div>
136
- * {isFree ? (
137
- * <span className="text-green-500">Free</span>
138
- * ) : (
139
- * <Money money={total} />
140
- * )}
141
- * {hasPriceText && <span className="text-sm">({priceText})</span>}
138
+ * {customPrice ? <span>{customPrice}</span> : <Money money={total} />}
142
139
  * </div>
143
140
  * )}
144
141
  * </Payment.Total>
@@ -146,7 +143,10 @@ Discount.displayName = 'Payment.Discount';
146
143
  */
147
144
  export const Total = React.forwardRef((props, ref) => {
148
145
  const { asChild, children, className } = props;
149
- return (_jsx(CorePayment.Data, { children: ({ total, priceText, hasPriceText, isFree }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentTotal, "data-has-price-text": hasPriceText, "data-is-free": isFree, customElement: children, customElementProps: { total, priceText, hasPriceText, isFree }, children: total && _jsx(Money, { money: total }) })) }));
146
+ return (_jsx(CorePayment.Data, { children: ({ total, customPrice }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentTotal, "data-has-custom-price": !!customPrice, customElement: children, customElementProps: {
147
+ total,
148
+ customPrice,
149
+ }, children: total && _jsx(Money, { money: total }) })) }));
150
150
  });
151
151
  Total.displayName = 'Payment.Total';
152
152
  /**
@@ -261,7 +261,7 @@ export const LineItemRepeater = React.forwardRef((props, ref) => {
261
261
  LineItemRepeater.displayName = 'Payment.LineItemRepeater';
262
262
  /**
263
263
  * Displays the line item total (total price after tax).
264
- * Shows priceText for CUSTOM rate type services, otherwise shows totalPrice.
264
+ * Shows customPrice for CUSTOM rate type services, otherwise shows totalPrice.
265
265
  *
266
266
  * @component
267
267
  * @example
@@ -271,12 +271,12 @@ LineItemRepeater.displayName = 'Payment.LineItemRepeater';
271
271
  */
272
272
  export const LineItemTotal = React.forwardRef((props, ref) => {
273
273
  const { asChild, children, className } = props;
274
- return (_jsx(CorePayment.LineItemInfo, { children: ({ totalPrice, priceText, hasPriceText, isFree }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemTotal, "data-has-price-text": hasPriceText, "data-is-free": isFree, customElement: children, customElementProps: { totalPrice, priceText, hasPriceText, isFree }, children: priceText ? (_jsx("span", { children: priceText })) : (totalPrice && _jsx(Money, { money: totalPrice })) })) }));
274
+ return (_jsx(CorePayment.LineItemInfo, { children: ({ totalPrice, customPrice }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemTotal, "data-has-custom-price": !!customPrice, customElement: children, customElementProps: { totalPrice, customPrice }, children: customPrice ? (_jsx("span", { children: customPrice })) : (totalPrice && _jsx(Money, { money: totalPrice })) })) }));
275
275
  });
276
276
  LineItemTotal.displayName = 'Payment.LineItemTotal';
277
277
  /**
278
278
  * Displays the line item subtotal (full price before discounts).
279
- * Shows priceText for CUSTOM rate type services, otherwise shows subtotal.
279
+ * Shows customPrice for CUSTOM rate type services, otherwise shows subtotal.
280
280
  *
281
281
  * @component
282
282
  * @example
@@ -286,7 +286,7 @@ LineItemTotal.displayName = 'Payment.LineItemTotal';
286
286
  */
287
287
  export const LineItemSubtotal = React.forwardRef((props, ref) => {
288
288
  const { asChild, children, className } = props;
289
- return (_jsx(CorePayment.LineItemInfo, { children: ({ subtotal, priceText, hasPriceText, isFree }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemSubtotal, "data-has-price-text": hasPriceText, "data-is-free": isFree, customElement: children, customElementProps: { subtotal, priceText, hasPriceText, isFree }, children: priceText ? (_jsx("span", { children: priceText })) : (subtotal && _jsx(Money, { money: subtotal })) })) }));
289
+ return (_jsx(CorePayment.LineItemInfo, { children: ({ subtotal, customPrice }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemSubtotal, "data-has-custom-price": !!customPrice, customElement: children, customElementProps: { subtotal, customPrice }, children: customPrice ? (_jsx("span", { children: customPrice })) : (subtotal && _jsx(Money, { money: subtotal })) })) }));
290
290
  });
291
291
  LineItemSubtotal.displayName = 'Payment.LineItemSubtotal';
292
292
  /**
@@ -44,7 +44,7 @@ export interface BookChildProps {
44
44
  isLoading: boolean;
45
45
  error: BookingError | null;
46
46
  canBook: boolean;
47
- onClick: () => void;
47
+ onClick: () => Promise<void>;
48
48
  disabled: boolean;
49
49
  }
50
50
  /**
@@ -57,9 +57,17 @@ export interface BookProps {
57
57
  label?: string;
58
58
  loadingState?: string;
59
59
  disabled?: boolean;
60
+ /**
61
+ * Called when checkout is required for payment.
62
+ * The navigateToCheckout function has the checkoutId captured - just call it.
63
+ */
60
64
  onCheckout?: (result: {
61
- checkoutId: string;
65
+ navigateToCheckout: () => void;
62
66
  }) => void;
67
+ /**
68
+ * Called when booking completes without requiring checkout (free/offline services).
69
+ * Receives the orderId which can be used to display confirmation or navigate to a thank you page.
70
+ */
63
71
  onComplete?: (result: {
64
72
  orderId: string;
65
73
  }) => void;
@@ -15,10 +15,8 @@ export type PaymentOption = 'FULL_PAYMENT_ONLINE' | 'FULL_PAYMENT_OFFLINE' | 'DE
15
15
  export interface LineItemAdditionalInfo {
16
16
  /** Line item ID to match with SDK's calculatedLineItems */
17
17
  lineItemId: string;
18
- /** Custom price text (for CUSTOM rate type services) */
19
- priceText?: string;
20
- /** Whether this is a free service (NO_FEE rate type) */
21
- isFree?: boolean;
18
+ /** Custom price (for CUSTOM rate type services) */
19
+ customPrice?: string;
22
20
  }
23
21
  /**
24
22
  * Payment calculation result with SDK response and additional computed info
@@ -20,11 +20,10 @@ const BOOKINGS_APP_DEF_ID = '13d21c63-b5ec-5912-8397-c3a5ddb27a97';
20
20
  // ============================================================================
21
21
  function getAdditionalInfo(service, lineItemId) {
22
22
  const payment = service.payment;
23
- const priceText = payment?.rateType === RateType.CUSTOM && payment.custom?.description
23
+ const customPrice = payment?.rateType === RateType.CUSTOM && payment.custom?.description
24
24
  ? payment.custom.description
25
25
  : undefined;
26
- const isFree = payment?.rateType === RateType.NO_FEE;
27
- return { lineItemId, priceText, isFree };
26
+ return { lineItemId, customPrice };
28
27
  }
29
28
  function getPaymentOption(service, hasDeposit) {
30
29
  const options = service.payment?.options;
@@ -5,7 +5,8 @@ export async function navigateToCheckout(checkoutId) {
5
5
  const { redirectSession } = await redirects.createRedirectSession({
6
6
  ecomCheckout: { checkoutId },
7
7
  callbacks: {
8
- postFlowUrl: window.location.href,
8
+ postFlowUrl: `${window.location.host}/booking/services`,
9
+ bookingsServiceListUrl: `${window.location.host}/booking/services`,
9
10
  },
10
11
  });
11
12
  if (redirectSession?.fullUrl) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-bookings",
3
- "version": "0.0.66",
3
+ "version": "0.0.68",
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": "cdf272bf09b943d1c3a3edba1a82225f2117165605b07f82878cc26c"
75
+ "falconPackageHash": "3b008d24daecb8e73bbbb513fa6c77a3915d21d91e398a5574be74a6"
76
76
  }