@wix/headless-bookings 0.0.39 → 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.
@@ -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: {
@@ -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
+ }
@@ -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 PaymentDetails, type SlotService } from '../../../services/payment/payment.js';
7
+ import { type PaymentServiceConfig, type SlotService } from '../../../services/payment/payment.js';
8
8
  import type { MoneyData } from '@wix/headless-components/react';
9
9
  /**
10
10
  * Props for Payment.Root component
@@ -33,122 +33,89 @@ export interface RootProps {
33
33
  * ```tsx
34
34
  * // SSR mode
35
35
  * <Payment.Root paymentServiceConfig={{ paymentDetails }}>
36
- * <Payment.Summary />
36
+ * <Payment.Total />
37
37
  * </Payment.Root>
38
38
  *
39
39
  * // Lazy loading mode
40
40
  * <Payment.Root slotServices={[...]} numberOfParticipants={2}>
41
- * {({ isLoading }) => isLoading ? <div>Loading...</div> : <Payment.Summary />}
41
+ * {({ isLoading }) => isLoading ? <div>Loading...</div> : <Payment.Total />}
42
42
  * </Payment.Root>
43
43
  *
44
44
  * // Reactive mode
45
45
  * <Payment.Root>
46
- * <Payment.Summary />
46
+ * <Payment.Total />
47
47
  * </Payment.Root>
48
48
  * ```
49
49
  */
50
50
  export declare function Root(props: RootProps): React.ReactNode;
51
51
  /**
52
- * Props for Payment render prop component
52
+ * Props for Data render prop component
53
53
  */
54
- export interface PaymentProps {
55
- children: (data: {
56
- paymentDetails: PaymentDetails | null;
57
- isLoading: boolean;
58
- error: string | null;
59
- }) => React.ReactNode;
60
- }
61
- /**
62
- * Core component that provides access to full payment details via render props.
63
- *
64
- * @component
65
- * @example
66
- * ```tsx
67
- * <Payment.Payment>
68
- * {({ paymentDetails, isLoading, error }) => (
69
- * isLoading ? <div>Loading...</div> : <div>{JSON.stringify(paymentDetails)}</div>
70
- * )}
71
- * </Payment.Payment>
72
- * ```
73
- */
74
- export declare function Payment(props: PaymentProps): React.ReactNode;
75
- /**
76
- * Props for Summary render prop component
77
- */
78
- export interface SummaryProps {
54
+ export interface DataProps {
79
55
  children: (data: {
80
56
  subtotal: MoneyData | null;
81
57
  total: MoneyData | null;
82
- currency: string;
83
58
  tax: MoneyData | null;
84
- hasTax: boolean;
85
59
  discount: MoneyData | null;
86
- hasDiscount: boolean;
87
60
  payNow: MoneyData | null;
88
61
  payLater: MoneyData | null;
89
- isDeposit: boolean;
62
+ currency: string;
63
+ isLoading: boolean;
64
+ error: string | null;
90
65
  }) => React.ReactNode;
91
66
  }
92
67
  /**
93
- * Core component that provides access to payment summary totals via render props.
94
- * Returns all pricing totals with MoneyData format for the Money component.
95
- * Use data-attributes for conditional visibility via CSS.
68
+ * Core component that provides access to converted payment data.
69
+ * Returns MoneyData for each price field, ready for rendering.
96
70
  *
97
71
  * @component
98
- * @example
99
- * ```tsx
100
- * <Payment.Summary>
101
- * {({ subtotal, tax, total, hasTax, hasDiscount, discount, isDeposit, payNow }) => (
102
- * <div>
103
- * <div>Subtotal: <Money money={subtotal} /></div>
104
- * <div data-has-tax={hasTax}>Tax: <Money money={tax} /></div>
105
- * <div data-has-discount={hasDiscount}>Discount: <Money money={discount} /></div>
106
- * <div>Total: <Money money={total} /></div>
107
- * <div data-has-deposit={isDeposit}>Pay Now: <Money money={payNow} /></div>
108
- * </div>
109
- * )}
110
- * </Payment.Summary>
111
- * ```
112
72
  */
113
- export declare function Summary(props: SummaryProps): React.ReactNode;
114
- import type { CalculatedLineItem as ApiCalculatedLineItem } from '@wix/auto_sdk_ecom_totals-calculator';
115
- import type { LineItemAdditionalInfo } from '../../../services/payment/payment.js';
73
+ export declare function Data(props: DataProps): React.ReactNode;
116
74
  /**
117
- * Combined line item with SDK data and additional computed info
75
+ * Line item with pre-converted MoneyData prices
118
76
  */
119
- export interface CombinedLineItem {
120
- /** SDK line item data */
121
- sdkLineItem: ApiCalculatedLineItem;
122
- /** Additional computed info */
123
- additionalInfo: LineItemAdditionalInfo | undefined;
77
+ export interface LineItem {
78
+ /** Unique identifier */
79
+ id: string;
80
+ /** Total price after tax */
81
+ totalPrice: MoneyData | null;
82
+ /** Subtotal (full price before discounts) */
83
+ subtotal: MoneyData | null;
84
+ /** Tax amount */
85
+ tax: MoneyData | null;
86
+ /** Discount amount */
87
+ discount: MoneyData | null;
88
+ /** Deposit amount */
89
+ deposit: MoneyData | null;
90
+ /** Custom price text (for CUSTOM rate type) */
91
+ priceText: string | null;
124
92
  }
125
93
  /**
126
94
  * Props for LineItems render prop component
127
95
  */
128
96
  export interface LineItemsProps {
129
97
  children: (data: {
130
- lineItems: CombinedLineItem[];
98
+ lineItems: LineItem[];
131
99
  hasLineItems: boolean;
132
100
  currency: string;
101
+ isLoading: boolean;
102
+ error: string | null;
133
103
  }) => React.ReactNode;
134
104
  }
135
105
  /**
136
106
  * Core component that provides access to calculated line items via render props.
137
- * Combines SDK line items with additional computed info.
138
- * Note: Currently supports single service only (always 1 line item).
107
+ * Returns line items with pre-converted MoneyData prices.
139
108
  *
140
109
  * @component
141
110
  * @example
142
111
  * ```tsx
143
112
  * <Payment.LineItems>
144
- * {({ lineItems, hasLineItems, currency }) => (
113
+ * {({ lineItems, hasLineItems, currency, isLoading, error }) => (
145
114
  * <ul data-has-items={hasLineItems}>
146
115
  * {lineItems.map((item) => (
147
- * <Payment.LineItem key={item.sdkLineItem.lineItemId} lineItem={item} currency={currency}>
148
- * {({ totalPriceAfterTaxAndDiscount }) => (
149
- * <li><Money money={totalPriceAfterTaxAndDiscount} /></li>
150
- * )}
151
- * </Payment.LineItem>
116
+ * <li key={item.id}>
117
+ * <Money money={item.totalPrice} />
118
+ * </li>
152
119
  * ))}
153
120
  * </ul>
154
121
  * )}
@@ -156,38 +123,3 @@ export interface LineItemsProps {
156
123
  * ```
157
124
  */
158
125
  export declare function LineItems(props: LineItemsProps): React.ReactNode;
159
- /**
160
- * Props for LineItem render prop component
161
- */
162
- export interface LineItemProps {
163
- lineItem: CombinedLineItem;
164
- currency: string;
165
- children: (data: {
166
- totalPrice: MoneyData | null;
167
- subtotal: MoneyData | null;
168
- tax: MoneyData | null;
169
- discount: MoneyData | null;
170
- deposit: MoneyData | null;
171
- priceText: string | null;
172
- }) => React.ReactNode;
173
- }
174
- /**
175
- * Core component that provides access to a single line item via render props.
176
- * Use data-attributes for conditional visibility via CSS.
177
- *
178
- * @component
179
- * @example
180
- * ```tsx
181
- * <Payment.LineItem lineItem={item} currency="USD">
182
- * {({ totalPrice, subtotal, tax, discount, deposit, priceText }) => (
183
- * <div>
184
- * <span><Money money={totalPrice} /></span>
185
- * <span data-has-discount={!!discount?.amount}><Money money={discount} /></span>
186
- * <span data-has-deposit={!!deposit?.amount}><Money money={deposit} /></span>
187
- * <span data-has-price-text={!!priceText}>{priceText}</span>
188
- * </div>
189
- * )}
190
- * </Payment.LineItem>
191
- * ```
192
- */
193
- export declare function LineItem(props: LineItemProps): React.ReactNode;
@@ -28,17 +28,17 @@ function RootContent({ children, }) {
28
28
  * ```tsx
29
29
  * // SSR mode
30
30
  * <Payment.Root paymentServiceConfig={{ paymentDetails }}>
31
- * <Payment.Summary />
31
+ * <Payment.Total />
32
32
  * </Payment.Root>
33
33
  *
34
34
  * // Lazy loading mode
35
35
  * <Payment.Root slotServices={[...]} numberOfParticipants={2}>
36
- * {({ isLoading }) => isLoading ? <div>Loading...</div> : <Payment.Summary />}
36
+ * {({ isLoading }) => isLoading ? <div>Loading...</div> : <Payment.Total />}
37
37
  * </Payment.Root>
38
38
  *
39
39
  * // Reactive mode
40
40
  * <Payment.Root>
41
- * <Payment.Summary />
41
+ * <Payment.Total />
42
42
  * </Payment.Root>
43
43
  * ```
44
44
  */
@@ -53,93 +53,40 @@ export function Root(props) {
53
53
  return (_jsx(WixServices, { servicesMap: createServicesMap().addService(PaymentServiceDefinition, PaymentService, internalConfig), children: _jsx(RootContent, { children: children }) }));
54
54
  }
55
55
  /**
56
- * Core component that provides access to full payment details via render props.
56
+ * Core component that provides access to converted payment data.
57
+ * Returns MoneyData for each price field, ready for rendering.
57
58
  *
58
59
  * @component
59
- * @example
60
- * ```tsx
61
- * <Payment.Payment>
62
- * {({ paymentDetails, isLoading, error }) => (
63
- * isLoading ? <div>Loading...</div> : <div>{JSON.stringify(paymentDetails)}</div>
64
- * )}
65
- * </Payment.Payment>
66
- * ```
67
60
  */
68
- export function Payment(props) {
61
+ export function Data(props) {
69
62
  const paymentService = useService(PaymentServiceDefinition);
70
63
  const paymentDetails = paymentService.paymentDetailsSignal.get();
71
64
  const isLoading = paymentService.isLoadingSignal.get();
72
65
  const error = paymentService.errorSignal.get();
73
- return props.children({ paymentDetails, isLoading, error });
74
- }
75
- /**
76
- * Core component that provides access to payment summary totals via render props.
77
- * Returns all pricing totals with MoneyData format for the Money component.
78
- * Use data-attributes for conditional visibility via CSS.
79
- *
80
- * @component
81
- * @example
82
- * ```tsx
83
- * <Payment.Summary>
84
- * {({ subtotal, tax, total, hasTax, hasDiscount, discount, isDeposit, payNow }) => (
85
- * <div>
86
- * <div>Subtotal: <Money money={subtotal} /></div>
87
- * <div data-has-tax={hasTax}>Tax: <Money money={tax} /></div>
88
- * <div data-has-discount={hasDiscount}>Discount: <Money money={discount} /></div>
89
- * <div>Total: <Money money={total} /></div>
90
- * <div data-has-deposit={isDeposit}>Pay Now: <Money money={payNow} /></div>
91
- * </div>
92
- * )}
93
- * </Payment.Summary>
94
- * ```
95
- */
96
- export function Summary(props) {
97
- const paymentService = useService(PaymentServiceDefinition);
98
- const paymentDetails = paymentService.paymentDetailsSignal.get();
99
66
  const currency = paymentDetails?.response?.currency || 'USD';
100
67
  const priceSummary = paymentDetails?.response?.priceSummary;
101
- // Always present
102
68
  const subtotal = convertMultiCurrencyPriceToMoneyData(priceSummary?.subtotal, currency);
103
69
  const total = convertMultiCurrencyPriceToMoneyData(priceSummary?.total, currency);
104
- // May or may not exist
105
70
  const tax = convertMultiCurrencyPriceToMoneyData(priceSummary?.tax, currency);
106
- const hasTax = tax !== null && tax.amount > 0;
107
71
  const discount = convertMultiCurrencyPriceToMoneyData(priceSummary?.discount, currency);
108
- const hasDiscount = discount !== null && discount.amount > 0;
109
- // Deposit scenarios only
110
72
  const payNow = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payNow?.total, currency);
111
73
  const payLater = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payLater?.total, currency);
112
- const isDeposit = payNow !== null && total !== null && payNow.amount < total.amount;
113
- return props.children({
114
- subtotal,
115
- total,
116
- currency,
117
- tax,
118
- hasTax,
119
- discount,
120
- hasDiscount,
121
- payNow,
122
- payLater,
123
- isDeposit,
124
- });
74
+ return props.children({ subtotal, total, tax, discount, payNow, payLater, currency, isLoading, error });
125
75
  }
126
76
  /**
127
77
  * Core component that provides access to calculated line items via render props.
128
- * Combines SDK line items with additional computed info.
129
- * Note: Currently supports single service only (always 1 line item).
78
+ * Returns line items with pre-converted MoneyData prices.
130
79
  *
131
80
  * @component
132
81
  * @example
133
82
  * ```tsx
134
83
  * <Payment.LineItems>
135
- * {({ lineItems, hasLineItems, currency }) => (
84
+ * {({ lineItems, hasLineItems, currency, isLoading, error }) => (
136
85
  * <ul data-has-items={hasLineItems}>
137
86
  * {lineItems.map((item) => (
138
- * <Payment.LineItem key={item.sdkLineItem.lineItemId} lineItem={item} currency={currency}>
139
- * {({ totalPriceAfterTaxAndDiscount }) => (
140
- * <li><Money money={totalPriceAfterTaxAndDiscount} /></li>
141
- * )}
142
- * </Payment.LineItem>
87
+ * <li key={item.id}>
88
+ * <Money money={item.totalPrice} />
89
+ * </li>
143
90
  * ))}
144
91
  * </ul>
145
92
  * )}
@@ -149,54 +96,25 @@ export function Summary(props) {
149
96
  export function LineItems(props) {
150
97
  const paymentService = useService(PaymentServiceDefinition);
151
98
  const paymentDetails = paymentService.paymentDetailsSignal.get();
99
+ const isLoading = paymentService.isLoadingSignal.get();
100
+ const error = paymentService.errorSignal.get();
152
101
  const sdkLineItems = paymentDetails?.response?.calculatedLineItems || [];
153
102
  const additionalInfoList = paymentDetails?.additionalInfo?.lineItems || [];
154
103
  const currency = paymentDetails?.response?.currency || 'USD';
155
- // Combine SDK line items with additional info
156
- const lineItems = sdkLineItems.map((sdkLineItem) => ({
157
- sdkLineItem,
158
- additionalInfo: additionalInfoList.find((info) => info.lineItemId === sdkLineItem.lineItemId),
159
- }));
160
- const hasLineItems = lineItems.length > 0;
161
- return props.children({ lineItems, hasLineItems, currency });
162
- }
163
- /**
164
- * Core component that provides access to a single line item via render props.
165
- * Use data-attributes for conditional visibility via CSS.
166
- *
167
- * @component
168
- * @example
169
- * ```tsx
170
- * <Payment.LineItem lineItem={item} currency="USD">
171
- * {({ totalPrice, subtotal, tax, discount, deposit, priceText }) => (
172
- * <div>
173
- * <span><Money money={totalPrice} /></span>
174
- * <span data-has-discount={!!discount?.amount}><Money money={discount} /></span>
175
- * <span data-has-deposit={!!deposit?.amount}><Money money={deposit} /></span>
176
- * <span data-has-price-text={!!priceText}>{priceText}</span>
177
- * </div>
178
- * )}
179
- * </Payment.LineItem>
180
- * ```
181
- */
182
- export function LineItem(props) {
183
- const { lineItem, currency, children } = props;
184
- const { sdkLineItem, additionalInfo } = lineItem;
185
- const pricesBreakdown = sdkLineItem?.pricesBreakdown;
186
- // Prices (from SDK)
187
- const totalPrice = convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.totalPriceAfterTax, currency);
188
- const subtotal = convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.fullPrice, currency);
189
- const tax = convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.taxDetails?.totalTax, currency);
190
- const discount = convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.totalDiscount, currency);
191
- const deposit = convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.depositAmount, currency);
192
- // Custom price text (for CUSTOM rate type)
193
- const priceText = additionalInfo?.priceText || null;
194
- return children({
195
- totalPrice,
196
- subtotal,
197
- tax,
198
- discount,
199
- deposit,
200
- priceText,
104
+ // Convert SDK line items to LineItem with MoneyData prices
105
+ const lineItems = sdkLineItems.map((sdkLineItem) => {
106
+ const pricesBreakdown = sdkLineItem?.pricesBreakdown;
107
+ const additionalInfo = additionalInfoList.find((info) => info.lineItemId === sdkLineItem.lineItemId);
108
+ return {
109
+ id: sdkLineItem.lineItemId || '',
110
+ totalPrice: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.totalPriceAfterTax, currency),
111
+ subtotal: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.fullPrice, currency),
112
+ tax: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.taxDetails?.totalTax, currency),
113
+ discount: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.totalDiscount, currency),
114
+ deposit: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.depositAmount, currency),
115
+ priceText: additionalInfo?.priceText || null,
116
+ };
201
117
  });
118
+ const hasLineItems = lineItems.length > 0;
119
+ return props.children({ lineItems, hasLineItems, currency, isLoading, error });
202
120
  }
@@ -6,4 +6,3 @@ export * as TimeSlotList from './time-slot-list/TimeSlotList.js';
6
6
  export * as ServiceList from './service-list/ServiceList.js';
7
7
  export * as Location from './location/Location.js';
8
8
  export * as Payment from './payment/Payment.js';
9
- export * as PaymentLineItem from './payment/PaymentLineItem.js';
@@ -6,4 +6,3 @@ export * as TimeSlotList from './time-slot-list/TimeSlotList.js';
6
6
  export * as ServiceList from './service-list/ServiceList.js';
7
7
  export * as Location from './location/Location.js';
8
8
  export * as Payment from './payment/Payment.js';
9
- export * as PaymentLineItem from './payment/PaymentLineItem.js';