@wix/headless-bookings 0.0.57 → 0.0.58

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.
@@ -19,8 +19,8 @@ var TestIds;
19
19
  TestIds["paymentLineItems"] = "payment-line-items";
20
20
  TestIds["paymentLineItemsList"] = "payment-line-items-list";
21
21
  TestIds["paymentLineItemRepeater"] = "payment-line-item-repeater";
22
- TestIds["paymentLineItemRoot"] = "payment-line-item-root";
23
- TestIds["paymentLineItemPrice"] = "payment-line-item-price";
22
+ TestIds["paymentLineItemTotal"] = "payment-line-item-total";
23
+ TestIds["paymentLineItemSubtotal"] = "payment-line-item-subtotal";
24
24
  TestIds["paymentLineItemDiscount"] = "payment-line-item-discount";
25
25
  TestIds["paymentLineItemDeposit"] = "payment-line-item-deposit";
26
26
  })(TestIds || (TestIds = {}));
@@ -28,7 +28,7 @@ var TestIds;
28
28
  * Root component that provides payment context to the entire app.
29
29
  * Supports 3 modes:
30
30
  * 1. SSR: paymentServiceConfig.paymentDetails - pre-fetched data
31
- * 2. Lazy loading: slotServices + numberOfParticipants props - calls API via useEffect
31
+ * 2. Lazy loading: pricingServiceSelections + numberOfParticipants props - calls API via useEffect
32
32
  * 3. Reactive: no config/props - reads from BookingService signals
33
33
  *
34
34
  * @order 1
@@ -57,24 +57,36 @@ var TestIds;
57
57
  * ```
58
58
  */
59
59
  export const Root = React.forwardRef((props, ref) => {
60
- const { children, paymentServiceConfig, slotServices, numberOfParticipants, asChild, className, ...attrs } = props;
61
- return (_jsx(CorePayment.Root, { paymentServiceConfig: paymentServiceConfig, slotServices: slotServices, numberOfParticipants: numberOfParticipants, children: ({ isLoading, hasError }) => {
60
+ const { children, paymentServiceConfig, pricingServiceSelections, numberOfParticipants, asChild, className, ...attrs } = props;
61
+ return (_jsx(CorePayment.Root, { paymentServiceConfig: paymentServiceConfig, pricingServiceSelections: pricingServiceSelections, numberOfParticipants: numberOfParticipants, children: ({ isLoading, hasError }) => {
62
62
  return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentRoot, "data-loading": isLoading, "data-has-error": hasError, customElement: children, customElementProps: { isLoading, hasError }, ...attrs, children: children }));
63
63
  } }));
64
64
  });
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
69
  *
69
70
  * @component
70
71
  * @example
71
72
  * ```tsx
72
73
  * <Payment.Subtotal className="text-lg" />
73
74
  * ```
75
+ * @example
76
+ * ```tsx
77
+ * <Payment.Subtotal asChild>
78
+ * {({ subtotal, priceText, hasPriceText, isFree }) => (
79
+ * <div>
80
+ * <Money money={subtotal} />
81
+ * {hasPriceText && <span className="text-sm">({priceText})</span>}
82
+ * </div>
83
+ * )}
84
+ * </Payment.Subtotal>
85
+ * ```
74
86
  */
75
87
  export const Subtotal = React.forwardRef((props, ref) => {
76
88
  const { asChild, children, className } = props;
77
- return (_jsx(CorePayment.Data, { children: ({ subtotal }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentSubtotal, customElement: children, customElementProps: { subtotal }, children: subtotal ? _jsx(Money, { money: subtotal }) : null })) }));
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 }) })) }));
78
90
  });
79
91
  Subtotal.displayName = 'Payment.Subtotal';
80
92
  /**
@@ -89,10 +101,7 @@ Subtotal.displayName = 'Payment.Subtotal';
89
101
  */
90
102
  export const Tax = React.forwardRef((props, ref) => {
91
103
  const { asChild, children, className } = props;
92
- return (_jsx(CorePayment.Data, { children: ({ tax }) => {
93
- const hasTax = tax !== null && tax.amount > 0;
94
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentTax, "data-has-tax": hasTax, customElement: children, customElementProps: { tax }, children: tax ? _jsx(Money, { money: tax }) : null }));
95
- } }));
104
+ return (_jsx(CorePayment.Data, { children: ({ tax, hasTax }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentTax, "data-has-tax": hasTax, customElement: children, customElementProps: { tax }, children: hasTax && _jsx(Money, { money: tax }) })) }));
96
105
  });
97
106
  Tax.displayName = 'Payment.Tax';
98
107
  /**
@@ -107,24 +116,37 @@ Tax.displayName = 'Payment.Tax';
107
116
  */
108
117
  export const Discount = React.forwardRef((props, ref) => {
109
118
  const { asChild, children, className } = props;
110
- return (_jsx(CorePayment.Data, { children: ({ discount }) => {
111
- const hasDiscount = discount !== null && discount.amount > 0;
112
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentDiscount, "data-has-discount": hasDiscount, customElement: children, customElementProps: { discount }, children: discount ? _jsx(Money, { money: discount }) : null }));
113
- } }));
119
+ return (_jsx(CorePayment.Data, { children: ({ discount, hasDiscount }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentDiscount, "data-has-discount": hasDiscount, customElement: children, customElementProps: { discount }, children: hasDiscount && _jsx(Money, { money: discount }) })) }));
114
120
  });
115
121
  Discount.displayName = 'Payment.Discount';
116
122
  /**
117
123
  * Displays the payment total.
124
+ * Always displays the monetary value but exposes priceText, hasPriceText, and isFree for custom rendering.
118
125
  *
119
126
  * @component
120
127
  * @example
121
128
  * ```tsx
122
129
  * <Payment.Total className="text-2xl font-bold" />
123
130
  * ```
131
+ * @example
132
+ * ```tsx
133
+ * <Payment.Total asChild>
134
+ * {({ total, priceText, hasPriceText, isFree }) => (
135
+ * <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>}
142
+ * </div>
143
+ * )}
144
+ * </Payment.Total>
145
+ * ```
124
146
  */
125
147
  export const Total = React.forwardRef((props, ref) => {
126
148
  const { asChild, children, className } = props;
127
- return (_jsx(CorePayment.Data, { children: ({ total }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentTotal, customElement: children, customElementProps: { total }, children: total ? _jsx(Money, { money: total }) : null })) }));
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 }) })) }));
128
150
  });
129
151
  Total.displayName = 'Payment.Total';
130
152
  /**
@@ -139,10 +161,7 @@ Total.displayName = 'Payment.Total';
139
161
  */
140
162
  export const PayNow = React.forwardRef((props, ref) => {
141
163
  const { asChild, children, className } = props;
142
- return (_jsx(CorePayment.Data, { children: ({ payNow, total }) => {
143
- const isDeposit = payNow !== null && total !== null && payNow.amount < total.amount;
144
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentPayNow, "data-has-deposit": isDeposit, customElement: children, customElementProps: { payNow }, children: payNow ? _jsx(Money, { money: payNow }) : null }));
145
- } }));
164
+ return (_jsx(CorePayment.Data, { children: ({ payNow, isDeposit }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentPayNow, "data-has-deposit": isDeposit, customElement: children, customElementProps: { payNow }, children: payNow && payNow.amount > 0 && _jsx(Money, { money: payNow }) })) }));
146
165
  });
147
166
  PayNow.displayName = 'Payment.PayNow';
148
167
  /**
@@ -157,10 +176,7 @@ PayNow.displayName = 'Payment.PayNow';
157
176
  */
158
177
  export const PayLater = React.forwardRef((props, ref) => {
159
178
  const { asChild, children, className } = props;
160
- return (_jsx(CorePayment.Data, { children: ({ payLater, payNow, total }) => {
161
- const isDeposit = payNow !== null && total !== null && payNow.amount < total.amount;
162
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentPayLater, "data-has-deposit": isDeposit, customElement: children, customElementProps: { payLater }, children: payLater ? _jsx(Money, { money: payLater }) : null }));
163
- } }));
179
+ return (_jsx(CorePayment.Data, { children: ({ payLater, isDeposit }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentPayLater, "data-has-deposit": isDeposit, customElement: children, customElementProps: { payLater }, children: payLater && payLater.amount > 0 && _jsx(Money, { money: payLater }) })) }));
164
180
  });
165
181
  PayLater.displayName = 'Payment.PayLater';
166
182
  /**
@@ -219,84 +235,60 @@ export const LineItemsList = React.forwardRef((props, ref) => {
219
235
  });
220
236
  LineItemsList.displayName = 'Payment.LineItemsList';
221
237
  /**
222
- * Repeater component that renders LineItemRoot for each line item.
223
- * Supports asChild pattern for custom rendering.
238
+ * Internal component that wraps each line item with context provider.
239
+ */
240
+ const LineItemWrapper = ({ item, currency, children, }) => {
241
+ return (_jsx(CorePayment.LineItemProvider, { lineItem: item, currency: currency, children: children }));
242
+ };
243
+ /**
244
+ * Repeater component that maps over line items and provides context per item.
245
+ * Uses GenericList.Repeater internally with itemWrapper that provides LineItemContext.
224
246
  *
225
247
  * @component
226
248
  * @example
227
249
  * ```tsx
228
- * // Standard usage
229
250
  * <Payment.LineItemRepeater>
230
- * <Payment.LineItemPrice />
231
- * </Payment.LineItemRepeater>
232
- *
233
- * // AsChild usage with custom list
234
- * <Payment.LineItemRepeater asChild>
235
- * {({ items, itemWrapper }) => (
236
- * <ol>
237
- * {items.map((item, index) => (
238
- * <li key={item.id}>
239
- * {itemWrapper({ item, index, children: <Payment.LineItemPrice /> })}
240
- * </li>
241
- * ))}
242
- * </ol>
243
- * )}
251
+ * <Payment.LineItemTotal />
252
+ * <Payment.LineItemDiscount />
244
253
  * </Payment.LineItemRepeater>
245
254
  * ```
246
255
  */
247
256
  export const LineItemRepeater = React.forwardRef((props, ref) => {
248
- const { children, asChild } = props;
257
+ const { children } = props;
249
258
  const { currency } = useLineItemsContext();
250
- // Custom itemWrapper that renders LineItemRoot for each line item
251
- const lineItemWrapper = ({ item, children: itemChildren, }) => {
252
- return (_jsx(LineItemRoot, { lineItem: item, currency: currency, "data-testid": TestIds.paymentLineItemRepeater, children: itemChildren || undefined }, item.id));
253
- };
254
- return (_jsx(GenericList.Repeater, { ref: ref, itemWrapper: lineItemWrapper, asChild: asChild, children: children }));
259
+ return (_jsx(GenericList.Repeater, { ref: ref, itemWrapper: ({ item }) => (_jsx(LineItemWrapper, { item: item, currency: currency, children: children }, item.id)), children: undefined }));
255
260
  });
256
261
  LineItemRepeater.displayName = 'Payment.LineItemRepeater';
257
- const LineItemContext = React.createContext(null);
258
- function useLineItemContext() {
259
- const context = React.useContext(LineItemContext);
260
- if (!context) {
261
- throw new Error('useLineItemContext must be used within a Payment.LineItemRoot component');
262
- }
263
- return context;
264
- }
265
262
  /**
266
- * Root component for a single line item.
267
- * Provides context for child components.
263
+ * Displays the line item total (total price after tax).
264
+ * Shows priceText for CUSTOM rate type services, otherwise shows totalPrice.
268
265
  *
269
266
  * @component
270
267
  * @example
271
268
  * ```tsx
272
- * <Payment.LineItemRoot lineItem={item} currency="USD">
273
- * <Payment.LineItemPrice />
274
- * <Payment.LineItemDiscount />
275
- * </Payment.LineItemRoot>
269
+ * <Payment.LineItemTotal className="text-2xl font-bold" />
276
270
  * ```
277
271
  */
278
- export const LineItemRoot = React.forwardRef((props, ref) => {
279
- const { lineItem, currency, children, asChild, className, ...attrs } = props;
280
- return (_jsx(LineItemContext.Provider, { value: { lineItem, currency }, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": attrs['data-testid'] || TestIds.paymentLineItemRoot, customElement: children, customElementProps: { lineItem, currency }, ...attrs, children: _jsx("div", { children: React.isValidElement(children) ? children : null }) }) }));
272
+ export const LineItemTotal = React.forwardRef((props, ref) => {
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 })) })) }));
281
275
  });
282
- LineItemRoot.displayName = 'Payment.LineItemRoot';
276
+ LineItemTotal.displayName = 'Payment.LineItemTotal';
283
277
  /**
284
- * Displays the line item price.
285
- * Shows priceText for CUSTOM rate type services, otherwise shows totalPrice.
278
+ * Displays the line item subtotal (full price before discounts).
279
+ * Shows priceText for CUSTOM rate type services, otherwise shows subtotal.
286
280
  *
287
281
  * @component
288
282
  * @example
289
283
  * ```tsx
290
- * <Payment.LineItemPrice className="text-lg font-bold" />
284
+ * <Payment.LineItemSubtotal className="text-lg" />
291
285
  * ```
292
286
  */
293
- export const LineItemPrice = React.forwardRef((props, ref) => {
287
+ export const LineItemSubtotal = React.forwardRef((props, ref) => {
294
288
  const { asChild, children, className } = props;
295
- const { lineItem } = useLineItemContext();
296
- const { totalPrice, priceText } = lineItem;
297
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemPrice, "data-has-price-text": !!priceText, customElement: children, customElementProps: { totalPrice, priceText }, children: priceText ? (_jsx("span", { children: priceText })) : totalPrice ? (_jsx(Money, { money: totalPrice })) : null }));
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 })) })) }));
298
290
  });
299
- LineItemPrice.displayName = 'Payment.LineItemPrice';
291
+ LineItemSubtotal.displayName = 'Payment.LineItemSubtotal';
300
292
  /**
301
293
  * Displays the line item discount.
302
294
  * Uses data-has-discount attribute for conditional visibility.
@@ -309,10 +301,7 @@ LineItemPrice.displayName = 'Payment.LineItemPrice';
309
301
  */
310
302
  export const LineItemDiscount = React.forwardRef((props, ref) => {
311
303
  const { asChild, children, className } = props;
312
- const { lineItem } = useLineItemContext();
313
- const { discount } = lineItem;
314
- const hasDiscount = discount !== null && discount.amount > 0;
315
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemDiscount, "data-has-discount": hasDiscount, customElement: children, customElementProps: { discount }, children: discount ? _jsx(Money, { money: discount }) : null }));
304
+ return (_jsx(CorePayment.LineItemInfo, { children: ({ discount, hasDiscount }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemDiscount, "data-has-discount": hasDiscount, customElement: children, customElementProps: { discount }, children: hasDiscount && _jsx(Money, { money: discount }) })) }));
316
305
  });
317
306
  LineItemDiscount.displayName = 'Payment.LineItemDiscount';
318
307
  /**
@@ -327,9 +316,6 @@ LineItemDiscount.displayName = 'Payment.LineItemDiscount';
327
316
  */
328
317
  export const LineItemDeposit = React.forwardRef((props, ref) => {
329
318
  const { asChild, children, className } = props;
330
- const { lineItem } = useLineItemContext();
331
- const { deposit } = lineItem;
332
- const hasDeposit = deposit !== null && deposit.amount > 0;
333
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemDeposit, "data-has-deposit": hasDeposit, customElement: children, customElementProps: { deposit }, children: deposit ? _jsx(Money, { money: deposit }) : null }));
319
+ return (_jsx(CorePayment.LineItemInfo, { children: ({ deposit, hasDeposit }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItemDeposit, "data-has-deposit": hasDeposit, customElement: children, customElementProps: { deposit }, children: hasDeposit && _jsx(Money, { money: deposit }) })) }));
334
320
  });
335
321
  LineItemDeposit.displayName = 'Payment.LineItemDeposit';
@@ -8,6 +8,6 @@ export { TimeSlotListService, TimeSlotListServiceDefinition, TimeSlotService, Ti
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
9
  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
10
  export { BookingFormService, BookingFormServiceDefinition, type BookingFormServiceAPI, type BookingFormServiceConfig, type BookingFormServiceInternalAPI, type ValidationResult, } from './booking-form/booking-form.js';
11
- export { PaymentService, PaymentServiceDefinition, type PaymentServiceAPI, type PaymentServiceConfig, type PaymentDetails, type SlotService, type LineItemAdditionalInfo, loadPaymentConfig, type SuccessPaymentConfigResult, type ErrorPaymentConfigResult, } from './payment/payment.js';
11
+ export { PaymentService, PaymentServiceDefinition, type PaymentServiceAPI, type PaymentServiceConfig, type PaymentDetails, type PricingServiceSelection, type LineItemAdditionalInfo, loadPaymentConfig, type SuccessPaymentConfigResult, type ErrorPaymentConfigResult, } from './payment/payment.js';
12
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
13
  export { StaffMemberListService, StaffMemberListServiceDefinition, loadStaffMemberListServiceInitialData, staffMemberListServiceBinding, type StaffMemberListServiceAPI, type StaffMemberListServiceConfig, type StaffMemberListActions, type StaffMemberData, type LoadStaffMemberListServiceResult, } from './staff-member-list/staff-member-list.js';
@@ -3,8 +3,8 @@
3
3
  * Handles calculating total prices, taxes, discounts, and deposit information
4
4
  * Supports two modes: SSR (config with pre-fetched data) and lazy loading (reactive)
5
5
  */
6
- import { type PaymentServiceConfig, type PaymentServiceInternalConfig, type SlotService } from './payment.def.js';
7
- export { PaymentServiceDefinition, type PaymentServiceConfig, type PaymentDetails, type LineItemAdditionalInfo, type SlotService, } from './payment.def.js';
6
+ import { type PaymentServiceConfig, type PaymentServiceInternalConfig, type PricingServiceSelection } from './payment.def.js';
7
+ export { PaymentServiceDefinition, type PaymentServiceConfig, type PaymentDetails, type LineItemAdditionalInfo, type PricingServiceSelection, } from './payment.def.js';
8
8
  export type { PaymentServiceAPI, PaymentServiceInternalConfig, } from './payment.def.js';
9
9
  export declare const PaymentService: import("@wix/services-definitions").ServiceFactory<string & {
10
10
  __api: import("./payment.def.js").PaymentServiceAPI;
@@ -23,10 +23,10 @@ export type ErrorPaymentConfigResult = {
23
23
  * Load payment configuration for Server-Side Rendering (SSR).
24
24
  * Calls the pricing APIs and returns pre-calculated payment details.
25
25
  *
26
- * @param params.slotServices - Slot/service inputs for calculation
26
+ * @param params.pricingServiceSelections - Pricing service selections for calculation
27
27
  * @param params.numberOfParticipants - Number of participants (default: 1)
28
28
  */
29
29
  export declare function loadPaymentConfig(params: {
30
- slotServices: SlotService[];
30
+ pricingServiceSelections: PricingServiceSelection[];
31
31
  numberOfParticipants?: number;
32
32
  }): Promise<SuccessPaymentConfigResult | ErrorPaymentConfigResult>;
@@ -30,9 +30,9 @@ export interface PaymentDetails {
30
30
  };
31
31
  }
32
32
  /**
33
- * Input for calculating payment for a slot/service
33
+ * Input for calculating payment for a booking service
34
34
  */
35
- export interface SlotService {
35
+ export interface PricingServiceSelection {
36
36
  service: Service;
37
37
  /** Optional line item ID. If not provided, one will be generated. */
38
38
  lineItemId?: string;
@@ -50,8 +50,8 @@ export interface PaymentServiceConfig {
50
50
  * Not exposed publicly - Core merges this with PaymentServiceConfig
51
51
  */
52
52
  export interface PaymentServiceInternalConfig extends PaymentServiceConfig {
53
- /** Slot services for lazy loading (internal use) */
54
- slotServices?: SlotService[];
53
+ /** Booking services for lazy loading (internal use) */
54
+ pricingServiceSelections?: PricingServiceSelection[];
55
55
  /** Number of participants for lazy loading (internal use) */
56
56
  numberOfParticipants?: number;
57
57
  }
@@ -60,11 +60,11 @@ function buildLineItem(service, numberOfParticipants, lineItemId) {
60
60
  paymentOption,
61
61
  };
62
62
  }
63
- async function calculatePaymentDetails(slotServices, numberOfParticipants) {
64
- // Ensure all slot services have a lineItemId
65
- const data = slotServices.map((slot) => ({
66
- ...slot,
67
- lineItemId: slot.lineItemId || generateLineItemId(),
63
+ async function calculatePaymentDetails(pricingServiceSelections, numberOfParticipants) {
64
+ // Ensure all booking services have a lineItemId
65
+ const data = pricingServiceSelections.map((selection) => ({
66
+ ...selection,
67
+ lineItemId: selection.lineItemId || generateLineItemId(),
68
68
  }));
69
69
  const lineItems = data.map((item) => buildLineItem(item.service, numberOfParticipants, item.lineItemId));
70
70
  const response = await calculateTotals({
@@ -96,15 +96,15 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
96
96
  const isLoadingSignal = signalsService.signal(false);
97
97
  const errorSignal = signalsService.signal(null);
98
98
  // Internal calculate function
99
- const calculate = async (slotServices, numberOfParticipants) => {
100
- if (!slotServices.length) {
99
+ const calculate = async (pricingServiceSelections, numberOfParticipants) => {
100
+ if (!pricingServiceSelections.length) {
101
101
  paymentDetailsSignal.set(null);
102
102
  return;
103
103
  }
104
104
  isLoadingSignal.set(true);
105
105
  errorSignal.set(null);
106
106
  try {
107
- const paymentDetails = await calculatePaymentDetails(slotServices, numberOfParticipants);
107
+ const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, numberOfParticipants);
108
108
  paymentDetailsSignal.set(paymentDetails);
109
109
  }
110
110
  catch (error) {
@@ -122,22 +122,30 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
122
122
  if (config.paymentDetails) {
123
123
  // No-op - data is pre-fetched
124
124
  }
125
- // Mode 2: Lazy loading - slotServices provided via internal config
126
- else if (config.slotServices?.length) {
127
- void calculate(config.slotServices, config.numberOfParticipants ?? 1);
125
+ // Mode 2: Lazy loading - pricingServiceSelections provided via internal config
126
+ else if (config.pricingServiceSelections?.length) {
127
+ void calculate(config.pricingServiceSelections, config.numberOfParticipants ?? 1);
128
128
  }
129
129
  // Mode 3: Reactive - observe BookingService signals (client-side only)
130
130
  else if (typeof window !== 'undefined' && bookingService) {
131
- signalsService.effect(() => {
131
+ // Computed signal that only changes when services change (not participants)
132
+ const servicesKey = signalsService.computed(() => {
132
133
  const selections = bookingService?.serviceSelections.get() || [];
134
+ return selections.map((s) => s.service._id).join(',');
135
+ });
136
+ signalsService.effect(() => {
137
+ // Subscribe to servicesKey to trigger effect only when services change
138
+ servicesKey.get();
139
+ // Read current selections without subscribing (peek)
140
+ const selections = bookingService?.serviceSelections.peek() || [];
133
141
  // For single service mode, take the first selection
134
142
  const firstSelection = selections[0];
135
143
  const numParticipants = firstSelection?.totalParticipants || 1;
136
- const services = selections.map((selection) => ({
144
+ const pricingServiceSelections = selections.map((selection) => ({
137
145
  service: selection.service,
138
146
  lineItemId: selection.instanceId,
139
147
  }));
140
- void calculate(services, numParticipants);
148
+ void calculate(pricingServiceSelections, numParticipants);
141
149
  });
142
150
  }
143
151
  return {
@@ -150,19 +158,19 @@ export const PaymentService = implementService.withConfig()(PaymentServiceDefini
150
158
  * Load payment configuration for Server-Side Rendering (SSR).
151
159
  * Calls the pricing APIs and returns pre-calculated payment details.
152
160
  *
153
- * @param params.slotServices - Slot/service inputs for calculation
161
+ * @param params.pricingServiceSelections - Pricing service selections for calculation
154
162
  * @param params.numberOfParticipants - Number of participants (default: 1)
155
163
  */
156
164
  export async function loadPaymentConfig(params) {
157
165
  try {
158
- const { slotServices, numberOfParticipants = 1 } = params;
159
- if (!slotServices.length) {
166
+ const { pricingServiceSelections, numberOfParticipants = 1 } = params;
167
+ if (!pricingServiceSelections.length) {
160
168
  return {
161
169
  type: 'success',
162
170
  config: { paymentDetails: undefined },
163
171
  };
164
172
  }
165
- const paymentDetails = await calculatePaymentDetails(slotServices, numberOfParticipants);
173
+ const paymentDetails = await calculatePaymentDetails(pricingServiceSelections, numberOfParticipants);
166
174
  return {
167
175
  type: 'success',
168
176
  config: { paymentDetails },
@@ -227,7 +227,7 @@ export function TimeSlot(props) {
227
227
  */
228
228
  export function Payment(props) {
229
229
  const { children } = props;
230
- return (_jsx(CoreBooking.PaymentData, { children: ({ slotServices }) => (_jsx(PaymentComponent.Root, { slotServices: slotServices, children: children })) }));
230
+ return (_jsx(CoreBooking.PaymentData, { children: ({ pricingServiceSelections }) => (_jsx(PaymentComponent.Root, { pricingServiceSelections: pricingServiceSelections, children: children })) }));
231
231
  }
232
232
  /**
233
233
  * Wraps Location context with the location from the booking context.
@@ -7,7 +7,7 @@ import { type BookingServiceConfig, type ServiceSelection, type BookingServiceAc
7
7
  import type { FormValues } from '@wix/form-public';
8
8
  import type { TimeSlot, Resource } from '@wix/auto_sdk_bookings_availability-time-slots';
9
9
  import type { Service, Location as ServiceLocationType } from '@wix/auto_sdk_bookings_services';
10
- import type { SlotService } from '../../../services/payment/payment.js';
10
+ import type { PricingServiceSelection } from '../../../services/payment/payment.js';
11
11
  /**
12
12
  * Props for Booking.Root component
13
13
  */
@@ -167,8 +167,8 @@ export declare function ItemsData(props: ItemsDataProps): React.ReactNode;
167
167
  * Data exposed by PaymentData render prop
168
168
  */
169
169
  export interface PaymentDataRenderProps {
170
- /** SlotServices array for Payment.Root */
171
- slotServices: SlotService[];
170
+ /** Pricing service selections array for Payment.Root */
171
+ pricingServiceSelections: PricingServiceSelection[];
172
172
  }
173
173
  /**
174
174
  * Props for PaymentData render prop component
@@ -267,9 +267,9 @@ export function PaymentData(props) {
267
267
  const { children } = props;
268
268
  const bookingService = useService(BookingServiceDefinition);
269
269
  const serviceSelections = bookingService.serviceSelections.get();
270
- const slotServices = serviceSelections.map((selection) => ({
270
+ const pricingServiceSelections = serviceSelections.map((selection) => ({
271
271
  service: selection.service,
272
272
  lineItemId: selection.instanceId,
273
273
  }));
274
- return children({ slotServices });
274
+ return children({ pricingServiceSelections });
275
275
  }
@@ -4,7 +4,7 @@
4
4
  * Note: Currently supports single service only
5
5
  */
6
6
  import React from 'react';
7
- import { type PaymentServiceConfig, type SlotService } from '../../../services/payment/payment.js';
7
+ import { type PaymentServiceConfig, type PricingServiceSelection } from '../../../services/payment/payment.js';
8
8
  import type { MoneyData } from '@wix/headless-components/react';
9
9
  /**
10
10
  * Props for Payment.Root component
@@ -16,8 +16,8 @@ export interface RootProps {
16
16
  }) => React.ReactNode);
17
17
  /** Service config - SSR only (paymentDetails) */
18
18
  paymentServiceConfig?: PaymentServiceConfig;
19
- /** Slot services for lazy loading (triggers API call) */
20
- slotServices?: SlotService[];
19
+ /** Pricing service selections for lazy loading (triggers API call) */
20
+ pricingServiceSelections?: PricingServiceSelection[];
21
21
  /** Number of participants for lazy loading */
22
22
  numberOfParticipants?: number;
23
23
  }
@@ -25,7 +25,7 @@ export interface RootProps {
25
25
  * Root component that provides payment context.
26
26
  * Supports 3 modes:
27
27
  * 1. SSR: paymentServiceConfig.paymentDetails - pre-fetched data
28
- * 2. Lazy loading: slotServices + numberOfParticipants props - calls API on init
28
+ * 2. Lazy loading: pricingServiceSelections + numberOfParticipants props - calls API on init
29
29
  * 3. Reactive: no config/props - reads from BookingService signals
30
30
  *
31
31
  * @component
@@ -37,7 +37,7 @@ export interface RootProps {
37
37
  * </Payment.Root>
38
38
  *
39
39
  * // Lazy loading mode
40
- * <Payment.Root slotServices={[...]} numberOfParticipants={2}>
40
+ * <Payment.Root pricingServiceSelections={[...]} numberOfParticipants={2}>
41
41
  * {({ isLoading }) => isLoading ? <div>Loading...</div> : <Payment.Total />}
42
42
  * </Payment.Root>
43
43
  *
@@ -59,6 +59,12 @@ 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;
65
+ hasTax: boolean;
66
+ hasDiscount: boolean;
67
+ isDeposit: boolean;
62
68
  currency: string;
63
69
  isLoading: boolean;
64
70
  error: string | null;
@@ -123,3 +129,76 @@ export interface LineItemsProps {
123
129
  * ```
124
130
  */
125
131
  export declare function LineItems(props: LineItemsProps): React.ReactNode;
132
+ /**
133
+ * Context value for LineItem components
134
+ */
135
+ export interface LineItemContextValue {
136
+ lineItem: LineItem;
137
+ currency: string;
138
+ }
139
+ /**
140
+ * Hook to access the current LineItem from context.
141
+ * Must be used within LineItemProvider.
142
+ *
143
+ * @throws Error if used outside of LineItemProvider context
144
+ */
145
+ export declare function useLineItemContext(): LineItemContextValue;
146
+ /**
147
+ * Props for LineItemProvider
148
+ */
149
+ export interface LineItemProviderProps {
150
+ children: React.ReactNode;
151
+ lineItem: LineItem;
152
+ currency: string;
153
+ }
154
+ /**
155
+ * Provider component used by repeaters to provide LineItem context.
156
+ * Can be used by core consumers to create their own repeater.
157
+ *
158
+ * @component
159
+ * @example
160
+ * ```tsx
161
+ * {lineItems.map((item) => (
162
+ * <LineItemProvider key={item.id} lineItem={item} currency={currency}>
163
+ * {children}
164
+ * </LineItemProvider>
165
+ * ))}
166
+ * ```
167
+ */
168
+ export declare function LineItemProvider(props: LineItemProviderProps): React.ReactNode;
169
+ /**
170
+ * Props for LineItemInfo render prop component
171
+ */
172
+ export interface LineItemInfoProps {
173
+ children: (data: {
174
+ lineItem: LineItem;
175
+ currency: string;
176
+ totalPrice: MoneyData | null;
177
+ subtotal: MoneyData | null;
178
+ tax: MoneyData | null;
179
+ discount: MoneyData | null;
180
+ deposit: MoneyData | null;
181
+ priceText: string | null;
182
+ hasPriceText: boolean;
183
+ isFree: boolean;
184
+ hasDiscount: boolean;
185
+ hasDeposit: boolean;
186
+ }) => React.ReactNode;
187
+ }
188
+ /**
189
+ * Core component that provides access to line item info via render props.
190
+ * Must be used within LineItemProvider.
191
+ *
192
+ * @component
193
+ * @example
194
+ * ```tsx
195
+ * <LineItemInfo>
196
+ * {({ totalPrice, priceText, lineItem }) => (
197
+ * <div>
198
+ * {priceText ? <span>{priceText}</span> : <Money money={totalPrice} />}
199
+ * </div>
200
+ * )}
201
+ * </LineItemInfo>
202
+ * ```
203
+ */
204
+ export declare function LineItemInfo(props: LineItemInfoProps): React.ReactNode;