@wix/headless-bookings 0.0.38 → 0.0.40

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.
Files changed (47) hide show
  1. package/cjs/dist/react/core/payment/Payment.d.ts +36 -104
  2. package/cjs/dist/react/core/payment/Payment.js +29 -111
  3. package/cjs/dist/react/core/time-slot-list/TimeSlot.d.ts +14 -22
  4. package/cjs/dist/react/core/time-slot-list/TimeSlot.js +30 -34
  5. package/cjs/dist/react/index.d.ts +0 -1
  6. package/cjs/dist/react/index.js +0 -1
  7. package/cjs/dist/react/payment/Payment.d.ts +119 -82
  8. package/cjs/dist/react/payment/Payment.js +126 -81
  9. package/cjs/dist/react/time-slot-list/TimeSlot.d.ts +24 -2
  10. package/cjs/dist/react/time-slot-list/TimeSlot.js +18 -2
  11. package/cjs/dist/react/time-slot-list/TimeSlotList.js +3 -5
  12. package/cjs/dist/services/index.d.ts +1 -1
  13. package/cjs/dist/services/index.js +1 -1
  14. package/cjs/dist/services/payment/payment.def.d.ts +2 -1
  15. package/cjs/dist/services/payment/payment.js +8 -2
  16. package/cjs/dist/services/time-slot-list/index.d.ts +1 -0
  17. package/cjs/dist/services/time-slot-list/index.js +1 -0
  18. package/cjs/dist/services/time-slot-list/time-slot.d.ts +35 -0
  19. package/cjs/dist/services/time-slot-list/time-slot.js +20 -0
  20. package/cjs/dist/utils/guid.d.ts +7 -0
  21. package/cjs/dist/utils/guid.js +12 -0
  22. package/dist/react/core/payment/Payment.d.ts +36 -104
  23. package/dist/react/core/payment/Payment.js +29 -111
  24. package/dist/react/core/time-slot-list/TimeSlot.d.ts +14 -22
  25. package/dist/react/core/time-slot-list/TimeSlot.js +30 -34
  26. package/dist/react/index.d.ts +0 -1
  27. package/dist/react/index.js +0 -1
  28. package/dist/react/payment/Payment.d.ts +119 -82
  29. package/dist/react/payment/Payment.js +126 -81
  30. package/dist/react/time-slot-list/TimeSlot.d.ts +24 -2
  31. package/dist/react/time-slot-list/TimeSlot.js +18 -2
  32. package/dist/react/time-slot-list/TimeSlotList.js +3 -5
  33. package/dist/services/index.d.ts +1 -1
  34. package/dist/services/index.js +1 -1
  35. package/dist/services/payment/payment.def.d.ts +2 -1
  36. package/dist/services/payment/payment.js +8 -2
  37. package/dist/services/time-slot-list/index.d.ts +1 -0
  38. package/dist/services/time-slot-list/index.js +1 -0
  39. package/dist/services/time-slot-list/time-slot.d.ts +35 -0
  40. package/dist/services/time-slot-list/time-slot.js +20 -0
  41. package/dist/utils/guid.d.ts +7 -0
  42. package/dist/utils/guid.js +12 -0
  43. package/package.json +2 -2
  44. package/cjs/dist/react/payment/PaymentLineItem.d.ts +0 -144
  45. package/cjs/dist/react/payment/PaymentLineItem.js +0 -141
  46. package/dist/react/payment/PaymentLineItem.d.ts +0 -144
  47. package/dist/react/payment/PaymentLineItem.js +0 -141
@@ -1,17 +1,15 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  /**
3
3
  * Payment - High-level component for displaying payment information
4
4
  * Provides components for displaying payment summary, totals, and line items
5
5
  */
6
6
  import React from 'react';
7
7
  import * as CorePayment from '../core/payment/Payment.js';
8
- import * as PaymentLineItem from './PaymentLineItem.js';
9
8
  import { AsChildSlot } from '@wix/headless-utils/react';
10
9
  import { Money, GenericList } from '@wix/headless-components/react';
11
10
  var TestIds;
12
11
  (function (TestIds) {
13
12
  TestIds["paymentRoot"] = "payment-root";
14
- TestIds["paymentSummary"] = "payment-summary";
15
13
  TestIds["paymentSubtotal"] = "payment-subtotal";
16
14
  TestIds["paymentTax"] = "payment-tax";
17
15
  TestIds["paymentDiscount"] = "payment-discount";
@@ -21,6 +19,10 @@ var TestIds;
21
19
  TestIds["paymentLineItems"] = "payment-line-items";
22
20
  TestIds["paymentLineItemsList"] = "payment-line-items-list";
23
21
  TestIds["paymentLineItemRepeater"] = "payment-line-item-repeater";
22
+ TestIds["paymentLineItemRoot"] = "payment-line-item-root";
23
+ TestIds["paymentLineItemPrice"] = "payment-line-item-price";
24
+ TestIds["paymentLineItemDiscount"] = "payment-line-item-discount";
25
+ TestIds["paymentLineItemDeposit"] = "payment-line-item-deposit";
24
26
  })(TestIds || (TestIds = {}));
25
27
  /**
26
28
  * Root component that provides payment context to the entire app.
@@ -33,13 +35,13 @@ var TestIds;
33
35
  * @component
34
36
  * @example
35
37
  * ```tsx
36
- * import { Payment, PaymentLineItem } from '@wix/bookings/components';
38
+ * import { Payment } from '@wix/bookings/components';
37
39
  *
38
40
  * // SSR mode
39
41
  * function PaymentPage({ paymentDetails }) {
40
42
  * return (
41
43
  * <Payment.Root paymentServiceConfig={{ paymentDetails }}>
42
- * <Payment.Summary />
44
+ * <Payment.Total />
43
45
  * </Payment.Root>
44
46
  * );
45
47
  * }
@@ -48,7 +50,7 @@ var TestIds;
48
50
  * function LazyPayment({ slotServices }) {
49
51
  * return (
50
52
  * <Payment.Root slotServices={slotServices} numberOfParticipants={2}>
51
- * {({ isLoading }) => isLoading ? <div>Loading...</div> : <Payment.Summary />}
53
+ * {({ isLoading }) => isLoading ? <div>Loading...</div> : <Payment.Total />}
52
54
  * </Payment.Root>
53
55
  * );
54
56
  * }
@@ -61,40 +63,6 @@ export const Root = React.forwardRef((props, ref) => {
61
63
  } }));
62
64
  });
63
65
  Root.displayName = 'Payment.Root';
64
- /**
65
- * Displays the payment summary with totals.
66
- * Uses data-attributes for conditional visibility via CSS.
67
- *
68
- * @component
69
- * @example
70
- * ```tsx
71
- * // Default usage - renders subtotal, tax, discount, total
72
- * <Payment.Summary className="space-y-2" />
73
- *
74
- * // Custom rendering with full summary data
75
- * <Payment.Summary asChild>
76
- * {({ subtotal, tax, hasTax, discount, hasDiscount, total, payNow, isDeposit }) => (
77
- * <div>
78
- * <div>Subtotal: <Money money={subtotal} /></div>
79
- * <div data-has-tax={hasTax}>Tax: <Money money={tax} /></div>
80
- * <div data-has-discount={hasDiscount}>Discount: <Money money={discount} /></div>
81
- * <div>Total: <Money money={total} /></div>
82
- * <div data-has-deposit={isDeposit}>Pay Now: <Money money={payNow} /></div>
83
- * </div>
84
- * )}
85
- * </Payment.Summary>
86
- * ```
87
- */
88
- export const Summary = React.forwardRef((props, ref) => {
89
- const { asChild, children, className } = props;
90
- return (_jsx(CorePayment.Summary, { children: (summaryData) => {
91
- const { subtotal, total, tax, discount, payNow, payLater, isDeposit, } = summaryData;
92
- // Default rendering
93
- const renderDefaultSummary = () => (_jsxs("div", { className: className, "data-testid": TestIds.paymentSummary, children: [_jsx("div", { "data-testid": TestIds.paymentSubtotal, children: subtotal && _jsx(Money, { money: subtotal }) }), _jsx("div", { "data-testid": TestIds.paymentTax, "data-has-tax": tax !== null && tax.amount > 0, children: tax && _jsx(Money, { money: tax }) }), _jsx("div", { "data-testid": TestIds.paymentDiscount, "data-has-discount": discount !== null && discount.amount > 0, children: discount && _jsx(Money, { money: discount }) }), _jsx("div", { "data-testid": TestIds.paymentTotal, children: total && _jsx(Money, { money: total }) }), _jsx("div", { "data-testid": TestIds.paymentPayNow, "data-has-deposit": isDeposit, children: payNow && _jsx(Money, { money: payNow }) }), _jsx("div", { "data-testid": TestIds.paymentPayLater, "data-has-deposit": isDeposit, children: payLater && _jsx(Money, { money: payLater }) })] }));
94
- return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentSummary, "data-has-tax": tax !== null && tax.amount > 0, "data-has-discount": discount !== null && discount.amount > 0, "data-has-deposit": isDeposit, customElement: children, customElementProps: summaryData, children: renderDefaultSummary() }));
95
- } }));
96
- });
97
- Summary.displayName = 'Payment.Summary';
98
66
  /**
99
67
  * Displays the payment subtotal.
100
68
  *
@@ -106,7 +74,7 @@ Summary.displayName = 'Payment.Summary';
106
74
  */
107
75
  export const Subtotal = React.forwardRef((props, ref) => {
108
76
  const { asChild, children, className } = props;
109
- return (_jsx(CorePayment.Summary, { 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 })) }));
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 })) }));
110
78
  });
111
79
  Subtotal.displayName = 'Payment.Subtotal';
112
80
  /**
@@ -121,7 +89,10 @@ Subtotal.displayName = 'Payment.Subtotal';
121
89
  */
122
90
  export const Tax = React.forwardRef((props, ref) => {
123
91
  const { asChild, children, className } = props;
124
- return (_jsx(CorePayment.Summary, { children: ({ tax, hasTax }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentTax, "data-has-tax": hasTax, customElement: children, customElementProps: { tax, hasTax }, children: tax ? _jsx(Money, { money: tax }) : null })) }));
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
+ } }));
125
96
  });
126
97
  Tax.displayName = 'Payment.Tax';
127
98
  /**
@@ -136,7 +107,10 @@ Tax.displayName = 'Payment.Tax';
136
107
  */
137
108
  export const Discount = React.forwardRef((props, ref) => {
138
109
  const { asChild, children, className } = props;
139
- return (_jsx(CorePayment.Summary, { children: ({ discount, hasDiscount }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentDiscount, "data-has-discount": hasDiscount, customElement: children, customElementProps: { discount, hasDiscount }, children: discount ? _jsx(Money, { money: discount }) : null })) }));
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
+ } }));
140
114
  });
141
115
  Discount.displayName = 'Payment.Discount';
142
116
  /**
@@ -150,7 +124,7 @@ Discount.displayName = 'Payment.Discount';
150
124
  */
151
125
  export const Total = React.forwardRef((props, ref) => {
152
126
  const { asChild, children, className } = props;
153
- return (_jsx(CorePayment.Summary, { 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 })) }));
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 })) }));
154
128
  });
155
129
  Total.displayName = 'Payment.Total';
156
130
  /**
@@ -165,7 +139,10 @@ Total.displayName = 'Payment.Total';
165
139
  */
166
140
  export const PayNow = React.forwardRef((props, ref) => {
167
141
  const { asChild, children, className } = props;
168
- return (_jsx(CorePayment.Summary, { children: ({ payNow, isDeposit }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentPayNow, "data-has-deposit": isDeposit, customElement: children, customElementProps: { payNow, isDeposit }, children: payNow ? _jsx(Money, { money: payNow }) : null })) }));
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
+ } }));
169
146
  });
170
147
  PayNow.displayName = 'Payment.PayNow';
171
148
  /**
@@ -180,51 +157,36 @@ PayNow.displayName = 'Payment.PayNow';
180
157
  */
181
158
  export const PayLater = React.forwardRef((props, ref) => {
182
159
  const { asChild, children, className } = props;
183
- return (_jsx(CorePayment.Summary, { children: ({ payLater, isDeposit }) => (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentPayLater, "data-has-deposit": isDeposit, customElement: children, customElementProps: { payLater, isDeposit }, children: payLater ? _jsx(Money, { money: payLater }) : null })) }));
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
+ } }));
184
164
  });
185
165
  PayLater.displayName = 'Payment.PayLater';
186
166
  /**
187
167
  * Container component for line items.
188
168
  * Follows the 3-level List/Options/Repeater pattern.
189
- * Provides context and doesn't render when there are no line items.
169
+ * Provides context and handles loading/error states via GenericList.
190
170
  *
191
171
  * @component
192
172
  * @example
193
173
  * ```tsx
194
174
  * <Payment.LineItems>
195
- * <Payment.LineItemsList>
175
+ * <Payment.LineItemsList
176
+ * emptyState={<div>No items</div>}
177
+ * loadingState={<div>Loading...</div>}
178
+ * errorState={<div>Failed to load</div>}
179
+ * >
196
180
  * <Payment.LineItemRepeater>
197
- * <LineItem.Price />
181
+ * <Payment.LineItemPrice />
198
182
  * </Payment.LineItemRepeater>
199
183
  * </Payment.LineItemsList>
200
184
  * </Payment.LineItems>
201
- *
202
- * // With asChild pattern for custom container
203
- * <Payment.LineItems asChild>
204
- * {({ lineItems, currency }) => (
205
- * <div className="border rounded p-4">
206
- * <Payment.LineItemsList>
207
- * <Payment.LineItemRepeater>
208
- * <LineItem.Price />
209
- * </Payment.LineItemRepeater>
210
- * </Payment.LineItemsList>
211
- * </div>
212
- * )}
213
- * </Payment.LineItems>
214
185
  * ```
215
186
  */
216
187
  export const LineItems = React.forwardRef((props, ref) => {
217
188
  const { asChild, children, className, ...attributes } = props;
218
- return (_jsx(CorePayment.LineItems, { children: ({ lineItems, currency }) => {
219
- if (!lineItems.length)
220
- return null;
221
- // Map line items to include required id field for GenericList
222
- const items = lineItems.map((item, index) => ({
223
- ...item,
224
- id: item.sdkLineItem.lineItemId || String(index),
225
- }));
226
- return (_jsx(GenericList.Root, { items: items, children: _jsx(LineItemsContext.Provider, { value: { currency }, children: _jsx(AsChildSlot, { ...attributes, ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItems, customElement: children, customElementProps: { lineItems, currency }, children: _jsx("div", { children: React.isValidElement(children) ? children : null }) }) }) }));
227
- } }));
189
+ return (_jsx(CorePayment.LineItems, { children: ({ lineItems, currency, isLoading, error }) => (_jsx(GenericList.Root, { items: lineItems, isLoading: isLoading, error: error, children: _jsx(LineItemsContext.Provider, { value: { currency }, children: _jsx(AsChildSlot, { ...attributes, ref: ref, asChild: asChild, className: className, "data-testid": TestIds.paymentLineItems, "data-loading": isLoading, "data-error": !!error, customElement: children, customElementProps: { lineItems, currency, isLoading, error }, children: _jsx("div", { children: React.isValidElement(children) ? children : null }) }) }) })) }));
228
190
  });
229
191
  LineItems.displayName = 'Payment.LineItems';
230
192
  // Context for line items currency
@@ -237,23 +199,27 @@ function useLineItemsContext() {
237
199
  return context;
238
200
  }
239
201
  /**
240
- * List container for line items with empty state support.
202
+ * List container for line items with empty/loading/error state support.
241
203
  *
242
204
  * @component
243
205
  * @example
244
206
  * ```tsx
245
- * <Payment.LineItemsList emptyState={<div>No items</div>}>
207
+ * <Payment.LineItemsList
208
+ * emptyState={<div>No items</div>}
209
+ * loadingState={<div>Loading...</div>}
210
+ * errorState={<div>Failed to load payment details</div>}
211
+ * >
246
212
  * <Payment.LineItemRepeater />
247
213
  * </Payment.LineItemsList>
248
214
  * ```
249
215
  */
250
216
  export const LineItemsList = React.forwardRef((props, ref) => {
251
- const { children, emptyState, className, ...otherProps } = props;
252
- return (_jsx(GenericList.Items, { ref: ref, emptyState: emptyState, className: className, "data-testid": TestIds.paymentLineItemsList, ...otherProps, children: children }));
217
+ const { children, emptyState, loadingState, errorState, className, ...otherProps } = props;
218
+ return (_jsx(GenericList.Items, { ref: ref, emptyState: emptyState, loadingState: loadingState, errorState: errorState, className: className, "data-testid": TestIds.paymentLineItemsList, ...otherProps, children: children }));
253
219
  });
254
220
  LineItemsList.displayName = 'Payment.LineItemsList';
255
221
  /**
256
- * Repeater component that renders LineItem.Root for each line item.
222
+ * Repeater component that renders LineItemRoot for each line item.
257
223
  * Supports asChild pattern for custom rendering.
258
224
  *
259
225
  * @component
@@ -261,7 +227,7 @@ LineItemsList.displayName = 'Payment.LineItemsList';
261
227
  * ```tsx
262
228
  * // Standard usage
263
229
  * <Payment.LineItemRepeater>
264
- * <LineItem.Price />
230
+ * <Payment.LineItemPrice />
265
231
  * </Payment.LineItemRepeater>
266
232
  *
267
233
  * // AsChild usage with custom list
@@ -270,7 +236,7 @@ LineItemsList.displayName = 'Payment.LineItemsList';
270
236
  * <ol>
271
237
  * {items.map((item, index) => (
272
238
  * <li key={item.id}>
273
- * {itemWrapper({ item, index, children: <LineItem.Price /> })}
239
+ * {itemWrapper({ item, index, children: <Payment.LineItemPrice /> })}
274
240
  * </li>
275
241
  * ))}
276
242
  * </ol>
@@ -281,10 +247,89 @@ LineItemsList.displayName = 'Payment.LineItemsList';
281
247
  export const LineItemRepeater = React.forwardRef((props, ref) => {
282
248
  const { children, asChild } = props;
283
249
  const { currency } = useLineItemsContext();
284
- // Custom itemWrapper that renders LineItem.Root for each line item
250
+ // Custom itemWrapper that renders LineItemRoot for each line item
285
251
  const lineItemWrapper = ({ item, children: itemChildren, }) => {
286
- return (_jsx(PaymentLineItem.Root, { lineItem: item, currency: currency, "data-testid": TestIds.paymentLineItemRepeater, children: itemChildren || undefined }, item.id));
252
+ return (_jsx(LineItemRoot, { lineItem: item, currency: currency, "data-testid": TestIds.paymentLineItemRepeater, children: itemChildren || undefined }, item.id));
287
253
  };
288
254
  return (_jsx(GenericList.Repeater, { ref: ref, itemWrapper: lineItemWrapper, asChild: asChild, children: children }));
289
255
  });
290
256
  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
+ /**
266
+ * Root component for a single line item.
267
+ * Provides context for child components.
268
+ *
269
+ * @component
270
+ * @example
271
+ * ```tsx
272
+ * <Payment.LineItemRoot lineItem={item} currency="USD">
273
+ * <Payment.LineItemPrice />
274
+ * <Payment.LineItemDiscount />
275
+ * </Payment.LineItemRoot>
276
+ * ```
277
+ */
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 }) }) }));
281
+ });
282
+ LineItemRoot.displayName = 'Payment.LineItemRoot';
283
+ /**
284
+ * Displays the line item price.
285
+ * Shows priceText for CUSTOM rate type services, otherwise shows totalPrice.
286
+ *
287
+ * @component
288
+ * @example
289
+ * ```tsx
290
+ * <Payment.LineItemPrice className="text-lg font-bold" />
291
+ * ```
292
+ */
293
+ export const LineItemPrice = React.forwardRef((props, ref) => {
294
+ 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 }));
298
+ });
299
+ LineItemPrice.displayName = 'Payment.LineItemPrice';
300
+ /**
301
+ * Displays the line item discount.
302
+ * Uses data-has-discount attribute for conditional visibility.
303
+ *
304
+ * @component
305
+ * @example
306
+ * ```tsx
307
+ * <Payment.LineItemDiscount className="text-green-500" />
308
+ * ```
309
+ */
310
+ export const LineItemDiscount = React.forwardRef((props, ref) => {
311
+ 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 }));
316
+ });
317
+ LineItemDiscount.displayName = 'Payment.LineItemDiscount';
318
+ /**
319
+ * Displays the line item deposit amount.
320
+ * Uses data-has-deposit attribute for conditional visibility.
321
+ *
322
+ * @component
323
+ * @example
324
+ * ```tsx
325
+ * <Payment.LineItemDeposit className="font-semibold" />
326
+ * ```
327
+ */
328
+ export const LineItemDeposit = React.forwardRef((props, ref) => {
329
+ 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 }));
334
+ });
335
+ LineItemDeposit.displayName = 'Payment.LineItemDeposit';
@@ -7,9 +7,31 @@
7
7
  */
8
8
  import React from 'react';
9
9
  import { AsChildChildren } from '@wix/headless-utils/react';
10
- export { TimeSlotProvider } from '../core/time-slot-list/TimeSlot.js';
11
- export type { TimeSlotProviderProps } from '../core/time-slot-list/TimeSlot.js';
10
+ import type { TimeSlot as TimeSlotType } from '@wix/auto_sdk_bookings_availability-time-slots';
12
11
  export type { StaffMemberData } from '../core/time-slot-list/TimeSlot.js';
12
+ /**
13
+ * Props for TimeSlot.Root component
14
+ */
15
+ export interface RootProps {
16
+ children?: React.ReactNode;
17
+ timeSlot: TimeSlotType;
18
+ asChild?: boolean;
19
+ className?: string;
20
+ }
21
+ /**
22
+ * Root component that provides time slot context.
23
+ *
24
+ * @order 1
25
+ * @component
26
+ * @example
27
+ * ```tsx
28
+ * <TimeSlot.Root timeSlot={timeSlot}>
29
+ * <TimeSlot.StartDate />
30
+ * <TimeSlot.Duration />
31
+ * </TimeSlot.Root>
32
+ * ```
33
+ */
34
+ export declare const Root: React.ForwardRefExoticComponent<RootProps & React.RefAttributes<HTMLElement>>;
13
35
  /**
14
36
  * Props for TimeSlot.StartDate component
15
37
  */
@@ -10,8 +10,24 @@ import React from 'react';
10
10
  import { GenericList } from '@wix/headless-components/react';
11
11
  import { AsChildSlot } from '@wix/headless-utils/react';
12
12
  import * as CoreTimeSlot from '../core/time-slot-list/TimeSlot.js';
13
- // Re-export TimeSlotProvider from core for use by TimeSlotRepeater
14
- export { TimeSlotProvider } from '../core/time-slot-list/TimeSlot.js';
13
+ /**
14
+ * Root component that provides time slot context.
15
+ *
16
+ * @order 1
17
+ * @component
18
+ * @example
19
+ * ```tsx
20
+ * <TimeSlot.Root timeSlot={timeSlot}>
21
+ * <TimeSlot.StartDate />
22
+ * <TimeSlot.Duration />
23
+ * </TimeSlot.Root>
24
+ * ```
25
+ */
26
+ export const Root = React.forwardRef((props, ref) => {
27
+ const { children, timeSlot, asChild, className, ...attrs } = props;
28
+ return (_jsx(CoreTimeSlot.Root, { config: { timeSlot }, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, customElement: children, ...attrs, children: children }) }));
29
+ });
30
+ Root.displayName = 'TimeSlot.Root';
15
31
  // ============================================================================
16
32
  // TestIds
17
33
  // ============================================================================
@@ -7,8 +7,8 @@ import React from 'react';
7
7
  import { GenericList } from '@wix/headless-components/react';
8
8
  import { AsChildSlot } from '@wix/headless-utils/react';
9
9
  import * as CoreTimeSlotList from '../core/time-slot-list/TimeSlotList.js';
10
- import { TimeSlotProvider, Info as CoreTimeSlotInfo, } from '../core/time-slot-list/TimeSlot.js';
11
- import { StartDate as TimeSlotStartDate, EndDate as TimeSlotEndDate, Duration, Actions as TimeSlotActions, Location, StaffMembers, StaffMemberRepeater, StaffMember, } from './TimeSlot.js';
10
+ import * as CoreTimeSlot from '../core/time-slot-list/TimeSlot.js';
11
+ import { StartDate as TimeSlotStartDate, EndDate as TimeSlotEndDate, Duration, Actions as TimeSlotActions, Location, StaffMembers, StaffMemberRepeater, StaffMember, Root as TimeSlotRoot, } from './TimeSlot.js';
12
12
  // ============================================================================
13
13
  // Utilities
14
14
  // ============================================================================
@@ -122,9 +122,7 @@ TimeSlots.displayName = 'TimeSlotList.TimeSlots';
122
122
  * Supports both ReactNode and function children patterns.
123
123
  */
124
124
  const TimeSlotItemWrapper = ({ timeSlot, children, }) => {
125
- return (_jsx(TimeSlotProvider, { timeSlot: timeSlot, children: _jsx(CoreTimeSlotInfo, { children: (props) => {
126
- return (_jsx("div", { "data-testid": TestIds.timeSlotListTimeSlot, "data-bookable": props.bookable, "data-selected": props.isSelected, children: typeof children === 'function' ? children(props) : children }));
127
- } }) }));
125
+ return (_jsx(TimeSlotRoot, { timeSlot: timeSlot, children: _jsx(CoreTimeSlot.Info, { children: (props) => (_jsx("div", { "data-testid": TestIds.timeSlotListTimeSlot, "data-bookable": props.bookable, "data-selected": props.isSelected, children: typeof children === 'function' ? children(props) : children })) }) }));
128
126
  };
129
127
  /**
130
128
  * Repeater component that renders each time slot.
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export { ServiceService, ServiceServiceDefinition, type ServiceServiceAPI, type ServiceServiceConfig, type ServiceActions, type ServicePaymentData, loadServiceServiceInitialData, type SuccessServiceServiceConfigResult, type NotFoundServiceServiceConfigResult, } from './service/service.js';
6
6
  export { BookingService, BookingServiceDefinition, type BookingServiceConfig, type BookingServiceActions, type ServiceSelection, type ServiceSelectionTimeSlot, } from './booking/booking.js';
7
- export { TimeSlotListService, TimeSlotListServiceDefinition, ViewMode, loadTimeSlotListConfig, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list/index.js';
7
+ export { TimeSlotListService, TimeSlotListServiceDefinition, TimeSlotService, TimeSlotServiceDefinition, ViewMode, loadTimeSlotListConfig, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type TimeSlotServiceAPI, type TimeSlotServiceConfig, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list/index.js';
8
8
  export { ServiceListService, ServiceListServiceDefinition, type ServiceListServiceAPI, type ServiceListServiceConfig, type ServiceListFilters, type ServiceListActions, type QueryOptions, loadServiceListServiceInitialData, type SuccessServiceListServiceConfigResult, type ErrorServiceListServiceConfigResult, } from './service-list/service-list.js';
9
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';
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export { ServiceService, ServiceServiceDefinition, loadServiceServiceInitialData, } from './service/service.js';
6
6
  export { BookingService, BookingServiceDefinition, } from './booking/booking.js';
7
- export { TimeSlotListService, TimeSlotListServiceDefinition, ViewMode, loadTimeSlotListConfig, } from './time-slot-list/index.js';
7
+ export { TimeSlotListService, TimeSlotListServiceDefinition, TimeSlotService, TimeSlotServiceDefinition, ViewMode, loadTimeSlotListConfig, } from './time-slot-list/index.js';
8
8
  export { ServiceListService, ServiceListServiceDefinition, loadServiceListServiceInitialData, } from './service-list/service-list.js';
9
9
  export { executeBookAction, canBook, buildBookingRequest, buildCheckoutRequest, } from './booking/book-action/index.js';
10
10
  export { BookingFormService, BookingFormServiceDefinition, } from './booking-form/booking-form.js';
@@ -34,7 +34,8 @@ export interface PaymentDetails {
34
34
  */
35
35
  export interface SlotService {
36
36
  service: Service;
37
- lineItemId: string;
37
+ /** Optional line item ID. If not provided, one will be generated. */
38
+ lineItemId?: string;
38
39
  }
39
40
  /**
40
41
  * Public config for PaymentService (SSR mode only):
@@ -9,6 +9,7 @@ import { calculateTotals, } from '@wix/auto_sdk_ecom_totals-calculator';
9
9
  import { BookingServiceDefinition } from '../booking/booking.js';
10
10
  import { RateType } from '@wix/auto_sdk_bookings_services';
11
11
  import { PaymentServiceDefinition, } from './payment.def.js';
12
+ import { generateLineItemId } from '../../utils/guid.js';
12
13
  export { PaymentServiceDefinition, } from './payment.def.js';
13
14
  // ============================================================================
14
15
  // Constants
@@ -54,11 +55,16 @@ function buildLineItem(service, numberOfParticipants, lineItemId) {
54
55
  };
55
56
  }
56
57
  async function calculatePaymentDetails(slotServices, numberOfParticipants) {
57
- const lineItems = slotServices.map((input) => buildLineItem(input.service, numberOfParticipants, input.lineItemId));
58
+ // Ensure all slot services have a lineItemId
59
+ const data = slotServices.map((slot) => ({
60
+ ...slot,
61
+ lineItemId: slot.lineItemId || generateLineItemId(),
62
+ }));
63
+ const lineItems = data.map((item) => buildLineItem(item.service, numberOfParticipants, item.lineItemId));
58
64
  const response = await calculateTotals({
59
65
  lineItems,
60
66
  });
61
- const additionalInfoList = slotServices.map((input) => getAdditionalInfo(input.service, input.lineItemId));
67
+ const additionalInfoList = data.map((item) => getAdditionalInfo(item.service, item.lineItemId));
62
68
  return {
63
69
  response,
64
70
  additionalInfo: {
@@ -4,3 +4,4 @@
4
4
  */
5
5
  export { TimeSlotListServiceDefinition, ViewMode, type DateRange, type TimeSlotListServiceAPI, type TimeSlotListServiceActions, type TimeSlotListServiceConfig, type TimeSlotsFilters, } from './time-slot-list.def.js';
6
6
  export { TimeSlotListService, loadTimeSlotListConfig, type LoadTimeSlotListConfigOptions, type LoadTimeSlotListConfigResult, } from './time-slot-list.js';
7
+ export { TimeSlotService, TimeSlotServiceDefinition, type TimeSlotServiceAPI, type TimeSlotServiceConfig, } from './time-slot.js';
@@ -4,3 +4,4 @@
4
4
  */
5
5
  export { TimeSlotListServiceDefinition, ViewMode, } from './time-slot-list.def.js';
6
6
  export { TimeSlotListService, loadTimeSlotListConfig, } from './time-slot-list.js';
7
+ export { TimeSlotService, TimeSlotServiceDefinition, } from './time-slot.js';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * TimeSlot Service
3
+ * Manages individual time slot data
4
+ */
5
+ import { type Signal } from '@wix/services-definitions/core-services/signals';
6
+ import type { TimeSlot } from '@wix/auto_sdk_bookings_availability-time-slots';
7
+ /**
8
+ * API interface for the TimeSlot service
9
+ */
10
+ export interface TimeSlotServiceAPI {
11
+ /** Reactive signal containing the time slot data */
12
+ timeSlotSignal: Signal<TimeSlot>;
13
+ }
14
+ /**
15
+ * Service definition for TimeSlot
16
+ */
17
+ export declare const TimeSlotServiceDefinition: string & {
18
+ __api: TimeSlotServiceAPI;
19
+ __config: {};
20
+ isServiceDefinition?: boolean;
21
+ } & TimeSlotServiceAPI;
22
+ /**
23
+ * Configuration for TimeSlotService
24
+ */
25
+ export interface TimeSlotServiceConfig {
26
+ timeSlot: TimeSlot;
27
+ }
28
+ /**
29
+ * Implementation of TimeSlotService
30
+ */
31
+ export declare const TimeSlotService: import("@wix/services-definitions").ServiceFactory<string & {
32
+ __api: TimeSlotServiceAPI;
33
+ __config: {};
34
+ isServiceDefinition?: boolean;
35
+ } & TimeSlotServiceAPI, TimeSlotServiceConfig>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * TimeSlot Service
3
+ * Manages individual time slot data
4
+ */
5
+ import { defineService, implementService } from '@wix/services-definitions';
6
+ import { SignalsServiceDefinition, } from '@wix/services-definitions/core-services/signals';
7
+ /**
8
+ * Service definition for TimeSlot
9
+ */
10
+ export const TimeSlotServiceDefinition = defineService('time-slot');
11
+ /**
12
+ * Implementation of TimeSlotService
13
+ */
14
+ export const TimeSlotService = implementService.withConfig()(TimeSlotServiceDefinition, ({ getService, config }) => {
15
+ const signalsService = getService(SignalsServiceDefinition);
16
+ const timeSlotSignal = signalsService.signal(config.timeSlot);
17
+ return {
18
+ timeSlotSignal,
19
+ };
20
+ });
@@ -0,0 +1,7 @@
1
+ /**
2
+ * ID generation utilities
3
+ */
4
+ /**
5
+ * Generates a unique line item ID in Ecom's GUID format
6
+ */
7
+ export declare function generateLineItemId(): string;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * ID generation utilities
3
+ */
4
+ /**
5
+ * Generates a unique line item ID in Ecom's GUID format
6
+ */
7
+ export function generateLineItemId() {
8
+ const s4 = () => Math.floor((1 + Math.random()) * 0x10000)
9
+ .toString(16)
10
+ .substring(1);
11
+ return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-bookings",
3
- "version": "0.0.38",
3
+ "version": "0.0.40",
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": "9f684dcab2e31f9a11e4fa26eb6c63aef1f039fd24342eb4e056f05d"
75
+ "falconPackageHash": "74b83da9361fcd8b13b0615fe645d53672b0f2db9714094ec5026205"
76
76
  }