@wix/headless-bookings 0.0.59 → 0.0.61

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.
@@ -192,31 +192,32 @@ export declare function TimeSlot(props: BookingTimeSlotProps): React.ReactNode;
192
192
  * Props for Booking.Payment component
193
193
  */
194
194
  export interface BookingPaymentProps {
195
- children: React.ReactNode;
195
+ children?: React.ReactNode | ((props: {
196
+ isLoading: boolean;
197
+ hasError: boolean;
198
+ }) => React.ReactNode);
199
+ asChild?: boolean;
200
+ className?: string;
196
201
  }
197
202
  /**
198
203
  * Wraps Payment.Root with all services from all booking items.
199
204
  * Should be used outside of ItemRepeater - it calculates payment for all items.
200
205
  * Children can use all Payment.* components (Payment.Total, Payment.Subtotal, etc.).
201
- * Always renders with data-has-payment attribute for CSS targeting.
206
+ * Supports asChild to expose { isLoading, hasError } to children.
202
207
  *
203
208
  * @component
204
209
  * @example
205
210
  * ```tsx
206
- * <Booking.Root>
207
- * <Booking.ItemsRoot>
208
- * <Booking.Items>
209
- * <Booking.ItemRepeater>
210
- * <Booking.Service><Service.Name /></Booking.Service>
211
- * </Booking.ItemRepeater>
212
- * </Booking.Items>
213
- * </Booking.ItemsRoot>
211
+ * // Standard usage
212
+ * <Booking.Payment>
213
+ * <Payment.Total className="text-xl font-bold" />
214
+ * <Payment.Subtotal />
215
+ * </Booking.Payment>
214
216
  *
215
- * <Booking.Payment>
216
- * <Payment.Total className="text-xl font-bold" />
217
- * <Payment.Subtotal />
218
- * </Booking.Payment>
219
- * </Booking.Root>
217
+ * // With asChild to access isLoading
218
+ * <Booking.Payment asChild>
219
+ * {({ isLoading }) => isLoading ? <Skeleton /> : <PaymentSection />}
220
+ * </Booking.Payment>
220
221
  * ```
221
222
  */
222
223
  export declare function Payment(props: BookingPaymentProps): React.ReactNode;
@@ -204,30 +204,26 @@ export function TimeSlot(props) {
204
204
  * Wraps Payment.Root with all services from all booking items.
205
205
  * Should be used outside of ItemRepeater - it calculates payment for all items.
206
206
  * Children can use all Payment.* components (Payment.Total, Payment.Subtotal, etc.).
207
- * Always renders with data-has-payment attribute for CSS targeting.
207
+ * Supports asChild to expose { isLoading, hasError } to children.
208
208
  *
209
209
  * @component
210
210
  * @example
211
211
  * ```tsx
212
- * <Booking.Root>
213
- * <Booking.ItemsRoot>
214
- * <Booking.Items>
215
- * <Booking.ItemRepeater>
216
- * <Booking.Service><Service.Name /></Booking.Service>
217
- * </Booking.ItemRepeater>
218
- * </Booking.Items>
219
- * </Booking.ItemsRoot>
212
+ * // Standard usage
213
+ * <Booking.Payment>
214
+ * <Payment.Total className="text-xl font-bold" />
215
+ * <Payment.Subtotal />
216
+ * </Booking.Payment>
220
217
  *
221
- * <Booking.Payment>
222
- * <Payment.Total className="text-xl font-bold" />
223
- * <Payment.Subtotal />
224
- * </Booking.Payment>
225
- * </Booking.Root>
218
+ * // With asChild to access isLoading
219
+ * <Booking.Payment asChild>
220
+ * {({ isLoading }) => isLoading ? <Skeleton /> : <PaymentSection />}
221
+ * </Booking.Payment>
226
222
  * ```
227
223
  */
228
224
  export function Payment(props) {
229
- const { children } = props;
230
- return (_jsx(CoreBooking.PaymentData, { children: ({ pricingServiceSelections }) => (_jsx(PaymentComponent.Root, { pricingServiceSelections: pricingServiceSelections, children: children })) }));
225
+ const { children, asChild, className } = props;
226
+ return (_jsx(CoreBooking.PaymentData, { children: ({ pricingServiceSelections }) => (_jsx(PaymentComponent.Root, { pricingServiceSelections: pricingServiceSelections, asChild: asChild, className: className, children: children })) }));
231
227
  }
232
228
  /**
233
229
  * Wraps Location context with the location from the booking context.
@@ -95,6 +95,8 @@ export interface LineItem {
95
95
  deposit: MoneyData | null;
96
96
  /** Custom price text (for CUSTOM rate type) */
97
97
  priceText: string | null;
98
+ /** Whether this is a free service (NO_FEE rate type) */
99
+ isFree: boolean;
98
100
  }
99
101
  /**
100
102
  * Props for LineItems render prop component
@@ -77,12 +77,13 @@ export function Data(props) {
77
77
  const discount = convertMultiCurrencyPriceToMoneyData(priceSummary?.discount, currency);
78
78
  const payNow = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payNow?.total, currency);
79
79
  const payLater = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payLater?.total, currency);
80
- // Get priceText from first line item (single service mode)
80
+ // Get priceText and isFree from first line item (single service mode)
81
81
  const additionalInfoList = paymentDetails?.additionalInfo?.lineItems || [];
82
82
  const firstLineItemInfo = additionalInfoList[0];
83
83
  const priceText = firstLineItemInfo?.priceText || null;
84
84
  const hasPriceText = !!priceText;
85
- const isFree = total !== null && total.amount === 0 && !hasPriceText;
85
+ // Use isFree from service (NO_FEE rate type) if available, otherwise fallback to amount check
86
+ const isFree = firstLineItemInfo?.isFree ?? false;
86
87
  // Computed flags
87
88
  const hasTax = tax !== null && tax.amount > 0;
88
89
  const hasDiscount = discount !== null && discount.amount > 0;
@@ -145,6 +146,7 @@ export function LineItems(props) {
145
146
  discount: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.totalDiscount, currency),
146
147
  deposit: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.depositAmount, currency),
147
148
  priceText: additionalInfo?.priceText || null,
149
+ isFree: additionalInfo?.isFree ?? false,
148
150
  };
149
151
  });
150
152
  const hasLineItems = lineItems.length > 0;
@@ -212,9 +214,6 @@ export function LineItemInfo(props) {
212
214
  const { lineItem, currency } = useLineItemContext();
213
215
  // Computed flags
214
216
  const hasPriceText = !!lineItem.priceText;
215
- const isFree = lineItem.totalPrice !== null &&
216
- lineItem.totalPrice.amount === 0 &&
217
- !hasPriceText;
218
217
  const hasDiscount = lineItem.discount !== null && lineItem.discount.amount > 0;
219
218
  const hasDeposit = lineItem.deposit !== null && lineItem.deposit.amount > 0;
220
219
  return props.children({
@@ -227,7 +226,7 @@ export function LineItemInfo(props) {
227
226
  deposit: lineItem.deposit,
228
227
  priceText: lineItem.priceText,
229
228
  hasPriceText,
230
- isFree,
229
+ isFree: lineItem.isFree,
231
230
  hasDiscount,
232
231
  hasDeposit,
233
232
  });
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import React from 'react';
6
6
  import { type ServiceServiceConfig } from '../../../services/service/service.js';
7
- import type { Service, Payment, BookingPolicy, Schedule } from '@wix/auto_sdk_bookings_services';
7
+ import type { Service, Payment, BookingPolicy, Schedule, StaffMember } from '@wix/auto_sdk_bookings_services';
8
8
  import type { MoneyData } from '@wix/headless-components/react';
9
9
  /**
10
10
  * Props for Service.Root component
@@ -56,10 +56,7 @@ export interface InfoProps {
56
56
  _id?: string;
57
57
  };
58
58
  locations?: Array<NonNullable<Service['locations']>[number]>;
59
- staff?: Array<{
60
- name?: string;
61
- _id?: string;
62
- }>;
59
+ staff?: StaffMember[];
63
60
  media?: Array<{
64
61
  url?: string;
65
62
  _id?: string;
@@ -92,10 +92,7 @@ export function Info(props) {
92
92
  const duration = service.schedule?.availabilityConstraints?.sessionDurations?.[0];
93
93
  // Get staff members from staffMemberDetails.staffMembers
94
94
  // (available when using conditionalFields: ['STAFF_MEMBER_DETAILS'] in queryServices)
95
- const staff = service.staffMembers?.map((member) => ({
96
- _id: member.staffMemberId,
97
- name: member.name || undefined,
98
- })) || [];
95
+ const staff = service.staffMembers || [];
99
96
  // Convert mainMedia to WixMediaImage format
100
97
  // Handle both snake_case (main_media) and camelCase (mainMedia) from API
101
98
  // Handle both string and object formats for image
@@ -179,12 +179,13 @@ export interface PayNowProps {
179
179
  asChild?: boolean;
180
180
  children?: AsChildChildren<{
181
181
  payNow: MoneyData | null;
182
+ isDeposit: boolean;
182
183
  }>;
183
184
  className?: string;
184
185
  }
185
186
  /**
186
- * Displays the pay now amount (for deposit scenarios).
187
- * Uses data-has-deposit attribute for conditional visibility.
187
+ * Displays the pay now amount (amount to pay upfront in deposit scenarios).
188
+ * Only renders in deposit mode when payNow has a valid amount.
188
189
  *
189
190
  * @component
190
191
  * @example
@@ -200,12 +201,13 @@ export interface PayLaterProps {
200
201
  asChild?: boolean;
201
202
  children?: AsChildChildren<{
202
203
  payLater: MoneyData | null;
204
+ isDeposit: boolean;
203
205
  }>;
204
206
  className?: string;
205
207
  }
206
208
  /**
207
209
  * Displays the pay later amount (for deposit scenarios).
208
- * Uses data-has-deposit attribute for conditional visibility.
210
+ * Only renders when in deposit mode and payLater has a valid amount.
209
211
  *
210
212
  * @component
211
213
  * @example
@@ -150,8 +150,8 @@ export const Total = React.forwardRef((props, ref) => {
150
150
  });
151
151
  Total.displayName = 'Payment.Total';
152
152
  /**
153
- * Displays the pay now amount (for deposit scenarios).
154
- * Uses data-has-deposit attribute for conditional visibility.
153
+ * Displays the pay now amount (amount to pay upfront in deposit scenarios).
154
+ * Only renders in deposit mode when payNow has a valid amount.
155
155
  *
156
156
  * @component
157
157
  * @example
@@ -161,12 +161,12 @@ Total.displayName = 'Payment.Total';
161
161
  */
162
162
  export const PayNow = React.forwardRef((props, ref) => {
163
163
  const { asChild, children, className } = props;
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 }) })) }));
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, isDeposit }, children: isDeposit && payNow && payNow.amount > 0 && (_jsx(Money, { money: payNow })) })) }));
165
165
  });
166
166
  PayNow.displayName = 'Payment.PayNow';
167
167
  /**
168
168
  * Displays the pay later amount (for deposit scenarios).
169
- * Uses data-has-deposit attribute for conditional visibility.
169
+ * Only renders when in deposit mode and payLater has a valid amount.
170
170
  *
171
171
  * @component
172
172
  * @example
@@ -176,7 +176,7 @@ PayNow.displayName = 'Payment.PayNow';
176
176
  */
177
177
  export const PayLater = React.forwardRef((props, ref) => {
178
178
  const { asChild, children, className } = props;
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 }) })) }));
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, isDeposit }, children: isDeposit && payLater && payLater.amount > 0 && (_jsx(Money, { money: payLater })) })) }));
180
180
  });
181
181
  PayLater.displayName = 'Payment.PayLater';
182
182
  /**
@@ -9,13 +9,6 @@ import { GenericListRepeaterProps, type ListVariant } from '@wix/headless-compon
9
9
  import { type AsChildChildren } from '@wix/headless-utils/react';
10
10
  import type { StaffMemberListServiceConfig } from '../../services/staff-member-list/staff-member-list.js';
11
11
  import type { StaffMemberData } from '../../services/staff-member-list/staff-member-list.def.js';
12
- /**
13
- * Staff member with id for list rendering
14
- * Uses staffMemberId as the id (which equals resource id)
15
- */
16
- interface StaffMemberWithId extends StaffMemberData {
17
- id: string;
18
- }
19
12
  /**
20
13
  * Props for the StaffMemberList root component
21
14
  */
@@ -73,7 +66,7 @@ export declare const StaffMembers: React.ForwardRefExoticComponent<StaffMembersP
73
66
  /**
74
67
  * Props for StaffMemberList.StaffMemberRepeater component
75
68
  */
76
- export type StaffMemberRepeaterProps = GenericListRepeaterProps<StaffMemberWithId>;
69
+ export type StaffMemberRepeaterProps = GenericListRepeaterProps<StaffMemberData>;
77
70
  export declare const StaffMemberRepeater: React.ForwardRefExoticComponent<StaffMemberRepeaterProps & React.RefAttributes<HTMLElement>>;
78
71
  /**
79
72
  * Props for StaffMemberList.Totals component
@@ -120,4 +113,3 @@ export interface ClearProps {
120
113
  export declare const Actions: {
121
114
  Clear: React.ForwardRefExoticComponent<ClearProps & React.RefAttributes<HTMLButtonElement>>;
122
115
  };
123
- export {};
@@ -59,10 +59,9 @@ Root.displayName = 'StaffMemberList.Root';
59
59
  const RootContent = React.forwardRef((props, ref) => {
60
60
  const { children, className, variant } = props;
61
61
  return (_jsx(CoreStaffMemberList.StaffMembers, { children: ({ staffMembers, isLoading }) => {
62
- // Add id to each staff member for GenericList (staffMemberId equals resource id)
63
- const itemsWithId = staffMembers.map((staffMember, index) => ({
62
+ const itemsWithId = staffMembers.map((staffMember) => ({
64
63
  ...staffMember,
65
- id: staffMember.staffMemberId || String(index),
64
+ id: staffMember.staffMemberId,
66
65
  }));
67
66
  return (_jsx(GenericList.Root, { items: itemsWithId, hasMore: false, isLoading: isLoading, className: className, ref: ref, "data-testid": TestIds.staffMemberListRoot, variant: variant, children: children }));
68
67
  } }));
@@ -90,8 +89,8 @@ StaffMembers.displayName = 'StaffMemberList.StaffMembers';
90
89
  export const StaffMemberRepeater = React.forwardRef((props, ref) => {
91
90
  const { children } = props;
92
91
  // Custom itemWrapper that wraps each item in StaffMember.Root
93
- const staffMemberItemWrapper = ({ item: staffMemberWithId, children: itemChildren, }) => {
94
- return (_jsx(StaffMember.Root, { staffMember: staffMemberWithId, "data-testid": TestIds.staffMemberListStaffMember, "data-staff-member-id": staffMemberWithId.id, "data-item-id": staffMemberWithId.id, children: itemChildren }, staffMemberWithId.id));
92
+ const staffMemberItemWrapper = ({ item: staffMember, children: itemChildren, }) => {
93
+ return (_jsx(StaffMember.Root, { staffMember: staffMember, "data-testid": TestIds.staffMemberListStaffMember, "data-staff-member-id": staffMember.staffMemberId, "data-item-id": staffMember.staffMemberId, children: itemChildren }, staffMember.staffMemberId));
95
94
  };
96
95
  return (_jsx(GenericList.Repeater, { ref: ref, itemWrapper: staffMemberItemWrapper, children: children }));
97
96
  });
@@ -17,6 +17,8 @@ export interface LineItemAdditionalInfo {
17
17
  lineItemId: string;
18
18
  /** Custom price text (for CUSTOM rate type services) */
19
19
  priceText?: string;
20
+ /** Whether this is a free service (NO_FEE rate type) */
21
+ isFree?: boolean;
20
22
  }
21
23
  /**
22
24
  * Payment calculation result with SDK response and additional computed info
@@ -23,7 +23,8 @@ function getAdditionalInfo(service, lineItemId) {
23
23
  const priceText = payment?.rateType === RateType.CUSTOM && payment.custom?.description
24
24
  ? payment.custom.description
25
25
  : undefined;
26
- return { lineItemId, priceText };
26
+ const isFree = payment?.rateType === RateType.NO_FEE;
27
+ return { lineItemId, priceText, isFree };
27
28
  }
28
29
  function getPaymentOption(service, hasDeposit) {
29
30
  const options = service.payment?.options;
@@ -192,31 +192,32 @@ export declare function TimeSlot(props: BookingTimeSlotProps): React.ReactNode;
192
192
  * Props for Booking.Payment component
193
193
  */
194
194
  export interface BookingPaymentProps {
195
- children: React.ReactNode;
195
+ children?: React.ReactNode | ((props: {
196
+ isLoading: boolean;
197
+ hasError: boolean;
198
+ }) => React.ReactNode);
199
+ asChild?: boolean;
200
+ className?: string;
196
201
  }
197
202
  /**
198
203
  * Wraps Payment.Root with all services from all booking items.
199
204
  * Should be used outside of ItemRepeater - it calculates payment for all items.
200
205
  * Children can use all Payment.* components (Payment.Total, Payment.Subtotal, etc.).
201
- * Always renders with data-has-payment attribute for CSS targeting.
206
+ * Supports asChild to expose { isLoading, hasError } to children.
202
207
  *
203
208
  * @component
204
209
  * @example
205
210
  * ```tsx
206
- * <Booking.Root>
207
- * <Booking.ItemsRoot>
208
- * <Booking.Items>
209
- * <Booking.ItemRepeater>
210
- * <Booking.Service><Service.Name /></Booking.Service>
211
- * </Booking.ItemRepeater>
212
- * </Booking.Items>
213
- * </Booking.ItemsRoot>
211
+ * // Standard usage
212
+ * <Booking.Payment>
213
+ * <Payment.Total className="text-xl font-bold" />
214
+ * <Payment.Subtotal />
215
+ * </Booking.Payment>
214
216
  *
215
- * <Booking.Payment>
216
- * <Payment.Total className="text-xl font-bold" />
217
- * <Payment.Subtotal />
218
- * </Booking.Payment>
219
- * </Booking.Root>
217
+ * // With asChild to access isLoading
218
+ * <Booking.Payment asChild>
219
+ * {({ isLoading }) => isLoading ? <Skeleton /> : <PaymentSection />}
220
+ * </Booking.Payment>
220
221
  * ```
221
222
  */
222
223
  export declare function Payment(props: BookingPaymentProps): React.ReactNode;
@@ -204,30 +204,26 @@ export function TimeSlot(props) {
204
204
  * Wraps Payment.Root with all services from all booking items.
205
205
  * Should be used outside of ItemRepeater - it calculates payment for all items.
206
206
  * Children can use all Payment.* components (Payment.Total, Payment.Subtotal, etc.).
207
- * Always renders with data-has-payment attribute for CSS targeting.
207
+ * Supports asChild to expose { isLoading, hasError } to children.
208
208
  *
209
209
  * @component
210
210
  * @example
211
211
  * ```tsx
212
- * <Booking.Root>
213
- * <Booking.ItemsRoot>
214
- * <Booking.Items>
215
- * <Booking.ItemRepeater>
216
- * <Booking.Service><Service.Name /></Booking.Service>
217
- * </Booking.ItemRepeater>
218
- * </Booking.Items>
219
- * </Booking.ItemsRoot>
212
+ * // Standard usage
213
+ * <Booking.Payment>
214
+ * <Payment.Total className="text-xl font-bold" />
215
+ * <Payment.Subtotal />
216
+ * </Booking.Payment>
220
217
  *
221
- * <Booking.Payment>
222
- * <Payment.Total className="text-xl font-bold" />
223
- * <Payment.Subtotal />
224
- * </Booking.Payment>
225
- * </Booking.Root>
218
+ * // With asChild to access isLoading
219
+ * <Booking.Payment asChild>
220
+ * {({ isLoading }) => isLoading ? <Skeleton /> : <PaymentSection />}
221
+ * </Booking.Payment>
226
222
  * ```
227
223
  */
228
224
  export function Payment(props) {
229
- const { children } = props;
230
- return (_jsx(CoreBooking.PaymentData, { children: ({ pricingServiceSelections }) => (_jsx(PaymentComponent.Root, { pricingServiceSelections: pricingServiceSelections, children: children })) }));
225
+ const { children, asChild, className } = props;
226
+ return (_jsx(CoreBooking.PaymentData, { children: ({ pricingServiceSelections }) => (_jsx(PaymentComponent.Root, { pricingServiceSelections: pricingServiceSelections, asChild: asChild, className: className, children: children })) }));
231
227
  }
232
228
  /**
233
229
  * Wraps Location context with the location from the booking context.
@@ -95,6 +95,8 @@ export interface LineItem {
95
95
  deposit: MoneyData | null;
96
96
  /** Custom price text (for CUSTOM rate type) */
97
97
  priceText: string | null;
98
+ /** Whether this is a free service (NO_FEE rate type) */
99
+ isFree: boolean;
98
100
  }
99
101
  /**
100
102
  * Props for LineItems render prop component
@@ -77,12 +77,13 @@ export function Data(props) {
77
77
  const discount = convertMultiCurrencyPriceToMoneyData(priceSummary?.discount, currency);
78
78
  const payNow = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payNow?.total, currency);
79
79
  const payLater = convertMultiCurrencyPriceToMoneyData(paymentDetails?.response?.payLater?.total, currency);
80
- // Get priceText from first line item (single service mode)
80
+ // Get priceText and isFree from first line item (single service mode)
81
81
  const additionalInfoList = paymentDetails?.additionalInfo?.lineItems || [];
82
82
  const firstLineItemInfo = additionalInfoList[0];
83
83
  const priceText = firstLineItemInfo?.priceText || null;
84
84
  const hasPriceText = !!priceText;
85
- const isFree = total !== null && total.amount === 0 && !hasPriceText;
85
+ // Use isFree from service (NO_FEE rate type) if available, otherwise fallback to amount check
86
+ const isFree = firstLineItemInfo?.isFree ?? false;
86
87
  // Computed flags
87
88
  const hasTax = tax !== null && tax.amount > 0;
88
89
  const hasDiscount = discount !== null && discount.amount > 0;
@@ -145,6 +146,7 @@ export function LineItems(props) {
145
146
  discount: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.totalDiscount, currency),
146
147
  deposit: convertMultiCurrencyPriceToMoneyData(pricesBreakdown?.depositAmount, currency),
147
148
  priceText: additionalInfo?.priceText || null,
149
+ isFree: additionalInfo?.isFree ?? false,
148
150
  };
149
151
  });
150
152
  const hasLineItems = lineItems.length > 0;
@@ -212,9 +214,6 @@ export function LineItemInfo(props) {
212
214
  const { lineItem, currency } = useLineItemContext();
213
215
  // Computed flags
214
216
  const hasPriceText = !!lineItem.priceText;
215
- const isFree = lineItem.totalPrice !== null &&
216
- lineItem.totalPrice.amount === 0 &&
217
- !hasPriceText;
218
217
  const hasDiscount = lineItem.discount !== null && lineItem.discount.amount > 0;
219
218
  const hasDeposit = lineItem.deposit !== null && lineItem.deposit.amount > 0;
220
219
  return props.children({
@@ -227,7 +226,7 @@ export function LineItemInfo(props) {
227
226
  deposit: lineItem.deposit,
228
227
  priceText: lineItem.priceText,
229
228
  hasPriceText,
230
- isFree,
229
+ isFree: lineItem.isFree,
231
230
  hasDiscount,
232
231
  hasDeposit,
233
232
  });
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import React from 'react';
6
6
  import { type ServiceServiceConfig } from '../../../services/service/service.js';
7
- import type { Service, Payment, BookingPolicy, Schedule } from '@wix/auto_sdk_bookings_services';
7
+ import type { Service, Payment, BookingPolicy, Schedule, StaffMember } from '@wix/auto_sdk_bookings_services';
8
8
  import type { MoneyData } from '@wix/headless-components/react';
9
9
  /**
10
10
  * Props for Service.Root component
@@ -56,10 +56,7 @@ export interface InfoProps {
56
56
  _id?: string;
57
57
  };
58
58
  locations?: Array<NonNullable<Service['locations']>[number]>;
59
- staff?: Array<{
60
- name?: string;
61
- _id?: string;
62
- }>;
59
+ staff?: StaffMember[];
63
60
  media?: Array<{
64
61
  url?: string;
65
62
  _id?: string;
@@ -92,10 +92,7 @@ export function Info(props) {
92
92
  const duration = service.schedule?.availabilityConstraints?.sessionDurations?.[0];
93
93
  // Get staff members from staffMemberDetails.staffMembers
94
94
  // (available when using conditionalFields: ['STAFF_MEMBER_DETAILS'] in queryServices)
95
- const staff = service.staffMembers?.map((member) => ({
96
- _id: member.staffMemberId,
97
- name: member.name || undefined,
98
- })) || [];
95
+ const staff = service.staffMembers || [];
99
96
  // Convert mainMedia to WixMediaImage format
100
97
  // Handle both snake_case (main_media) and camelCase (mainMedia) from API
101
98
  // Handle both string and object formats for image
@@ -179,12 +179,13 @@ export interface PayNowProps {
179
179
  asChild?: boolean;
180
180
  children?: AsChildChildren<{
181
181
  payNow: MoneyData | null;
182
+ isDeposit: boolean;
182
183
  }>;
183
184
  className?: string;
184
185
  }
185
186
  /**
186
- * Displays the pay now amount (for deposit scenarios).
187
- * Uses data-has-deposit attribute for conditional visibility.
187
+ * Displays the pay now amount (amount to pay upfront in deposit scenarios).
188
+ * Only renders in deposit mode when payNow has a valid amount.
188
189
  *
189
190
  * @component
190
191
  * @example
@@ -200,12 +201,13 @@ export interface PayLaterProps {
200
201
  asChild?: boolean;
201
202
  children?: AsChildChildren<{
202
203
  payLater: MoneyData | null;
204
+ isDeposit: boolean;
203
205
  }>;
204
206
  className?: string;
205
207
  }
206
208
  /**
207
209
  * Displays the pay later amount (for deposit scenarios).
208
- * Uses data-has-deposit attribute for conditional visibility.
210
+ * Only renders when in deposit mode and payLater has a valid amount.
209
211
  *
210
212
  * @component
211
213
  * @example
@@ -150,8 +150,8 @@ export const Total = React.forwardRef((props, ref) => {
150
150
  });
151
151
  Total.displayName = 'Payment.Total';
152
152
  /**
153
- * Displays the pay now amount (for deposit scenarios).
154
- * Uses data-has-deposit attribute for conditional visibility.
153
+ * Displays the pay now amount (amount to pay upfront in deposit scenarios).
154
+ * Only renders in deposit mode when payNow has a valid amount.
155
155
  *
156
156
  * @component
157
157
  * @example
@@ -161,12 +161,12 @@ Total.displayName = 'Payment.Total';
161
161
  */
162
162
  export const PayNow = React.forwardRef((props, ref) => {
163
163
  const { asChild, children, className } = props;
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 }) })) }));
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, isDeposit }, children: isDeposit && payNow && payNow.amount > 0 && (_jsx(Money, { money: payNow })) })) }));
165
165
  });
166
166
  PayNow.displayName = 'Payment.PayNow';
167
167
  /**
168
168
  * Displays the pay later amount (for deposit scenarios).
169
- * Uses data-has-deposit attribute for conditional visibility.
169
+ * Only renders when in deposit mode and payLater has a valid amount.
170
170
  *
171
171
  * @component
172
172
  * @example
@@ -176,7 +176,7 @@ PayNow.displayName = 'Payment.PayNow';
176
176
  */
177
177
  export const PayLater = React.forwardRef((props, ref) => {
178
178
  const { asChild, children, className } = props;
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 }) })) }));
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, isDeposit }, children: isDeposit && payLater && payLater.amount > 0 && (_jsx(Money, { money: payLater })) })) }));
180
180
  });
181
181
  PayLater.displayName = 'Payment.PayLater';
182
182
  /**
@@ -9,13 +9,6 @@ import { GenericListRepeaterProps, type ListVariant } from '@wix/headless-compon
9
9
  import { type AsChildChildren } from '@wix/headless-utils/react';
10
10
  import type { StaffMemberListServiceConfig } from '../../services/staff-member-list/staff-member-list.js';
11
11
  import type { StaffMemberData } from '../../services/staff-member-list/staff-member-list.def.js';
12
- /**
13
- * Staff member with id for list rendering
14
- * Uses staffMemberId as the id (which equals resource id)
15
- */
16
- interface StaffMemberWithId extends StaffMemberData {
17
- id: string;
18
- }
19
12
  /**
20
13
  * Props for the StaffMemberList root component
21
14
  */
@@ -73,7 +66,7 @@ export declare const StaffMembers: React.ForwardRefExoticComponent<StaffMembersP
73
66
  /**
74
67
  * Props for StaffMemberList.StaffMemberRepeater component
75
68
  */
76
- export type StaffMemberRepeaterProps = GenericListRepeaterProps<StaffMemberWithId>;
69
+ export type StaffMemberRepeaterProps = GenericListRepeaterProps<StaffMemberData>;
77
70
  export declare const StaffMemberRepeater: React.ForwardRefExoticComponent<StaffMemberRepeaterProps & React.RefAttributes<HTMLElement>>;
78
71
  /**
79
72
  * Props for StaffMemberList.Totals component
@@ -120,4 +113,3 @@ export interface ClearProps {
120
113
  export declare const Actions: {
121
114
  Clear: React.ForwardRefExoticComponent<ClearProps & React.RefAttributes<HTMLButtonElement>>;
122
115
  };
123
- export {};
@@ -59,10 +59,9 @@ Root.displayName = 'StaffMemberList.Root';
59
59
  const RootContent = React.forwardRef((props, ref) => {
60
60
  const { children, className, variant } = props;
61
61
  return (_jsx(CoreStaffMemberList.StaffMembers, { children: ({ staffMembers, isLoading }) => {
62
- // Add id to each staff member for GenericList (staffMemberId equals resource id)
63
- const itemsWithId = staffMembers.map((staffMember, index) => ({
62
+ const itemsWithId = staffMembers.map((staffMember) => ({
64
63
  ...staffMember,
65
- id: staffMember.staffMemberId || String(index),
64
+ id: staffMember.staffMemberId,
66
65
  }));
67
66
  return (_jsx(GenericList.Root, { items: itemsWithId, hasMore: false, isLoading: isLoading, className: className, ref: ref, "data-testid": TestIds.staffMemberListRoot, variant: variant, children: children }));
68
67
  } }));
@@ -90,8 +89,8 @@ StaffMembers.displayName = 'StaffMemberList.StaffMembers';
90
89
  export const StaffMemberRepeater = React.forwardRef((props, ref) => {
91
90
  const { children } = props;
92
91
  // Custom itemWrapper that wraps each item in StaffMember.Root
93
- const staffMemberItemWrapper = ({ item: staffMemberWithId, children: itemChildren, }) => {
94
- return (_jsx(StaffMember.Root, { staffMember: staffMemberWithId, "data-testid": TestIds.staffMemberListStaffMember, "data-staff-member-id": staffMemberWithId.id, "data-item-id": staffMemberWithId.id, children: itemChildren }, staffMemberWithId.id));
92
+ const staffMemberItemWrapper = ({ item: staffMember, children: itemChildren, }) => {
93
+ return (_jsx(StaffMember.Root, { staffMember: staffMember, "data-testid": TestIds.staffMemberListStaffMember, "data-staff-member-id": staffMember.staffMemberId, "data-item-id": staffMember.staffMemberId, children: itemChildren }, staffMember.staffMemberId));
95
94
  };
96
95
  return (_jsx(GenericList.Repeater, { ref: ref, itemWrapper: staffMemberItemWrapper, children: children }));
97
96
  });
@@ -17,6 +17,8 @@ export interface LineItemAdditionalInfo {
17
17
  lineItemId: string;
18
18
  /** Custom price text (for CUSTOM rate type services) */
19
19
  priceText?: string;
20
+ /** Whether this is a free service (NO_FEE rate type) */
21
+ isFree?: boolean;
20
22
  }
21
23
  /**
22
24
  * Payment calculation result with SDK response and additional computed info
@@ -23,7 +23,8 @@ function getAdditionalInfo(service, lineItemId) {
23
23
  const priceText = payment?.rateType === RateType.CUSTOM && payment.custom?.description
24
24
  ? payment.custom.description
25
25
  : undefined;
26
- return { lineItemId, priceText };
26
+ const isFree = payment?.rateType === RateType.NO_FEE;
27
+ return { lineItemId, priceText, isFree };
27
28
  }
28
29
  function getPaymentOption(service, hasDeposit) {
29
30
  const options = service.payment?.options;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-bookings",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
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": "61ca8eb4d4959dd03911f50f6d490b55be609f85f03f0aff55af5948"
75
+ "falconPackageHash": "cd07fd84714c0aeec8926487334181ea2b0381b631c601891b559528"
76
76
  }