@wix/headless-bookings 1.0.16 → 1.0.18

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 (45) hide show
  1. package/cjs/dist/__mocks__/booking/booking-client-service.js +4 -1
  2. package/cjs/dist/__mocks__/services/services-mocks.d.ts +13 -0
  3. package/cjs/dist/__mocks__/services/services-mocks.js +92 -1
  4. package/cjs/dist/react/core/booking/Booking.d.ts +6 -0
  5. package/cjs/dist/react/core/booking/Booking.js +2 -0
  6. package/cjs/dist/react/core/payment/Payment.d.ts +2 -1
  7. package/cjs/dist/react/core/payment/Payment.js +11 -8
  8. package/cjs/dist/react/core/service/Service.js +4 -2
  9. package/cjs/dist/react/payment/Payment.d.ts +3 -2
  10. package/cjs/dist/react/payment/Payment.js +11 -5
  11. package/cjs/dist/services/booking/book-action/buildBookingRequest.js +13 -4
  12. package/cjs/dist/services/booking/book-action/types.d.ts +6 -0
  13. package/cjs/dist/services/booking/booking.d.ts +17 -0
  14. package/cjs/dist/services/booking/booking.js +8 -0
  15. package/cjs/dist/services/payment/payment.d.ts +3 -0
  16. package/cjs/dist/services/payment/payment.def.d.ts +15 -1
  17. package/cjs/dist/services/payment/payment.js +89 -58
  18. package/cjs/dist/services/service/service.js +1 -1
  19. package/cjs/dist/services/servicePayment.d.ts +26 -0
  20. package/cjs/dist/services/servicePayment.js +46 -0
  21. package/cjs/dist/utils/money.d.ts +12 -2
  22. package/cjs/dist/utils/money.js +30 -18
  23. package/dist/__mocks__/booking/booking-client-service.js +4 -1
  24. package/dist/__mocks__/services/services-mocks.d.ts +13 -0
  25. package/dist/__mocks__/services/services-mocks.js +91 -0
  26. package/dist/react/core/booking/Booking.d.ts +6 -0
  27. package/dist/react/core/booking/Booking.js +2 -0
  28. package/dist/react/core/payment/Payment.d.ts +2 -1
  29. package/dist/react/core/payment/Payment.js +11 -8
  30. package/dist/react/core/service/Service.js +4 -2
  31. package/dist/react/payment/Payment.d.ts +3 -2
  32. package/dist/react/payment/Payment.js +11 -5
  33. package/dist/services/booking/book-action/buildBookingRequest.js +13 -4
  34. package/dist/services/booking/book-action/types.d.ts +6 -0
  35. package/dist/services/booking/booking.d.ts +17 -0
  36. package/dist/services/booking/booking.js +8 -0
  37. package/dist/services/payment/payment.d.ts +3 -0
  38. package/dist/services/payment/payment.def.d.ts +15 -1
  39. package/dist/services/payment/payment.js +89 -58
  40. package/dist/services/service/service.js +1 -1
  41. package/dist/services/servicePayment.d.ts +26 -0
  42. package/dist/services/servicePayment.js +40 -0
  43. package/dist/utils/money.d.ts +12 -2
  44. package/dist/utils/money.js +30 -18
  45. package/package.json +3 -3
@@ -86,7 +86,7 @@ export const ServiceService = implementService.withConfig()(ServiceServiceDefini
86
86
  const initialService = config.service || {};
87
87
  serviceSignal = signalsService.signal(initialService);
88
88
  }
89
- const isLoadingSignal = signalsService.signal(!!config.serviceId);
89
+ const isLoadingSignal = signalsService.signal(!config.service && (!!config.serviceId || !!config.serviceSlug));
90
90
  const errorSignal = signalsService.signal(null);
91
91
  // Default appId to BOOKING_APP_ID if not provided
92
92
  const appId = config.appId ?? BOOKING_APP_ID;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Shared helpers for reading payment configuration off a Service entity.
3
+ *
4
+ * `payment.fixed` / `varied` / `custom` are a `@oneof` in the services SDK, so
5
+ * only one is ever populated — reading `fixed` then `varied` covers both FIXED
6
+ * and VARIED rate types.
7
+ *
8
+ * A percentage deposit stores no amount anywhere on the entity; the server
9
+ * resolves it when the customer books. Hence the split between
10
+ * {@link requiresDeposit} ("takes a deposit?") and {@link hasDeposit} ("has an
11
+ * amount?") — a percentage deposit answers `true` and `false` respectively.
12
+ *
13
+ * @module Services/ServicePayment
14
+ */
15
+ import type { Service } from '@wix/auto_sdk_bookings_services';
16
+ /**
17
+ * Whether the service takes a deposit. An explicit `options.deposit` wins; when
18
+ * the flag is absent a configured amount still counts, so a service that sets one
19
+ * without the flag keeps reporting a deposit.
20
+ */
21
+ export declare function requiresDeposit(service?: Service): boolean;
22
+ /** The deposit amount, if one is configured as a fixed amount. */
23
+ export declare function getDepositValue(service?: Service): string | undefined;
24
+ export declare function hasDeposit(service?: Service): boolean;
25
+ /** Meaningful only alongside `hasDeposit`. */
26
+ export declare function isFullUpfrontPaymentAllowed(service?: Service): boolean;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Shared helpers for reading payment configuration off a Service entity.
3
+ *
4
+ * `payment.fixed` / `varied` / `custom` are a `@oneof` in the services SDK, so
5
+ * only one is ever populated — reading `fixed` then `varied` covers both FIXED
6
+ * and VARIED rate types.
7
+ *
8
+ * A percentage deposit stores no amount anywhere on the entity; the server
9
+ * resolves it when the customer books. Hence the split between
10
+ * {@link requiresDeposit} ("takes a deposit?") and {@link hasDeposit} ("has an
11
+ * amount?") — a percentage deposit answers `true` and `false` respectively.
12
+ *
13
+ * @module Services/ServicePayment
14
+ */
15
+ /**
16
+ * Whether the service takes a deposit. An explicit `options.deposit` wins; when
17
+ * the flag is absent a configured amount still counts, so a service that sets one
18
+ * without the flag keeps reporting a deposit.
19
+ */
20
+ export function requiresDeposit(service) {
21
+ return service?.payment?.options?.deposit ?? hasDeposit(service);
22
+ }
23
+ /** The deposit amount, if one is configured as a fixed amount. */
24
+ export function getDepositValue(service) {
25
+ const payment = service?.payment;
26
+ return (payment?.fixed?.deposit?.value ??
27
+ payment?.varied?.deposit?.value ??
28
+ payment?.fixed?.depositDetails?.amount?.value ??
29
+ payment?.varied?.depositDetails?.amount?.value);
30
+ }
31
+ export function hasDeposit(service) {
32
+ const value = getDepositValue(service);
33
+ return !!value && Number(value) > 0;
34
+ }
35
+ /** Meaningful only alongside `hasDeposit`. */
36
+ export function isFullUpfrontPaymentAllowed(service) {
37
+ const payment = service?.payment;
38
+ return !!(payment?.fixed?.fullUpfrontPaymentAllowed ??
39
+ payment?.varied?.fullUpfrontPaymentAllowed);
40
+ }
@@ -5,13 +5,19 @@ import type { MoneyData } from '@wix/headless-components/react';
5
5
  /**
6
6
  * Converts SDK Money format to MoneyData format
7
7
  *
8
+ * The original SDK value is validated as a whole (values that are not fully
9
+ * numeric and finite, like `"12.34xyz"`, return `null`) but kept as-is, so
10
+ * string amounts reach `Money` with full decimal precision instead of being
11
+ * routed through floating point. Compare amounts numerically with
12
+ * `Number(money.amount)`.
13
+ *
8
14
  * @param sdkMoney - The SDK money object with value, currency, and optional formattedValue
9
15
  * @returns MoneyData object with amount and currency, or null if conversion fails
10
16
  *
11
17
  * @example
12
18
  * ```ts
13
19
  * const money = convertToMoneyData({ value: "25.50", currency: "USD" });
14
- * // Returns: { amount: 25.50, currency: "USD" }
20
+ * // Returns: { amount: "25.50", currency: "USD" }
15
21
  * ```
16
22
  */
17
23
  export declare function convertToMoneyData(sdkMoney: {
@@ -21,7 +27,11 @@ export declare function convertToMoneyData(sdkMoney: {
21
27
  } | undefined): MoneyData | null;
22
28
  /**
23
29
  * Converts ecom MultiCurrencyPrice format to MoneyData format
24
- * MultiCurrencyPrice uses 'amount' as string, while MoneyData uses 'amount' as number
30
+ *
31
+ * The string amount is validated as a whole (values that are not fully
32
+ * numeric and finite, like `"12.34xyz"`, return `null`) but kept as-is, so
33
+ * it reaches `Money` with full decimal precision. Compare amounts
34
+ * numerically with `Number(money.amount)`.
25
35
  *
26
36
  * @param price - The MultiCurrencyPrice object with amount as string
27
37
  * @param currency - The currency code (from response level)
@@ -1,16 +1,31 @@
1
1
  /**
2
2
  * Money utility functions for converting between SDK Money types and MoneyData
3
3
  */
4
+ // Number() (unlike parseFloat) rejects strings with trailing garbage like
5
+ // "12.34xyz", but treats empty/whitespace-only strings as 0 — reject those
6
+ // separately so invalid values return null instead of reaching the formatter.
7
+ function toNumericAmount(value) {
8
+ if (typeof value === 'string') {
9
+ return value.trim() === '' ? NaN : Number(value);
10
+ }
11
+ return value;
12
+ }
4
13
  /**
5
14
  * Converts SDK Money format to MoneyData format
6
15
  *
16
+ * The original SDK value is validated as a whole (values that are not fully
17
+ * numeric and finite, like `"12.34xyz"`, return `null`) but kept as-is, so
18
+ * string amounts reach `Money` with full decimal precision instead of being
19
+ * routed through floating point. Compare amounts numerically with
20
+ * `Number(money.amount)`.
21
+ *
7
22
  * @param sdkMoney - The SDK money object with value, currency, and optional formattedValue
8
23
  * @returns MoneyData object with amount and currency, or null if conversion fails
9
24
  *
10
25
  * @example
11
26
  * ```ts
12
27
  * const money = convertToMoneyData({ value: "25.50", currency: "USD" });
13
- * // Returns: { amount: 25.50, currency: "USD" }
28
+ * // Returns: { amount: "25.50", currency: "USD" }
14
29
  * ```
15
30
  */
16
31
  export function convertToMoneyData(sdkMoney) {
@@ -18,23 +33,20 @@ export function convertToMoneyData(sdkMoney) {
18
33
  return null;
19
34
  const priceValue = sdkMoney.value;
20
35
  const currency = sdkMoney.currency || 'USD';
21
- // Convert price value to number
22
- let amount = undefined;
23
- if (typeof priceValue === 'string') {
24
- const parsed = parseFloat(priceValue);
25
- amount = !isNaN(parsed) ? parsed : undefined;
26
- }
27
- else if (typeof priceValue === 'number') {
28
- amount = priceValue;
29
- }
30
- if (amount !== undefined && !isNaN(amount) && amount >= 0) {
31
- return { amount, currency };
32
- }
33
- return null;
36
+ if (priceValue === undefined)
37
+ return null;
38
+ const parsed = toNumericAmount(priceValue);
39
+ if (!Number.isFinite(parsed) || parsed < 0)
40
+ return null;
41
+ return { amount: priceValue, currency };
34
42
  }
35
43
  /**
36
44
  * Converts ecom MultiCurrencyPrice format to MoneyData format
37
- * MultiCurrencyPrice uses 'amount' as string, while MoneyData uses 'amount' as number
45
+ *
46
+ * The string amount is validated as a whole (values that are not fully
47
+ * numeric and finite, like `"12.34xyz"`, return `null`) but kept as-is, so
48
+ * it reaches `Money` with full decimal precision. Compare amounts
49
+ * numerically with `Number(money.amount)`.
38
50
  *
39
51
  * @param price - The MultiCurrencyPrice object with amount as string
40
52
  * @param currency - The currency code (from response level)
@@ -43,8 +55,8 @@ export function convertToMoneyData(sdkMoney) {
43
55
  export function convertMultiCurrencyPriceToMoneyData(price, currency) {
44
56
  if (!price?.amount)
45
57
  return null;
46
- const parsed = parseFloat(price.amount);
47
- if (isNaN(parsed) || parsed < 0)
58
+ const parsed = toNumericAmount(price.amount);
59
+ if (!Number.isFinite(parsed) || parsed < 0)
48
60
  return null;
49
- return { amount: parsed, currency };
61
+ return { amount: price.amount, currency };
50
62
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-bookings",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "scripts": {
@@ -41,7 +41,7 @@
41
41
  "@wix/auto_sdk_bookings_catalog-search": "^1.0.2",
42
42
  "@wix/auto_sdk_bookings_event-time-slots": "^1.0.112",
43
43
  "@wix/auto_sdk_bookings_pricing": "^1.0.87",
44
- "@wix/auto_sdk_bookings_services": "^1.0.244",
44
+ "@wix/auto_sdk_bookings_services": "^1.0.265",
45
45
  "@wix/auto_sdk_ecom_cart-v-2": "^1.0.86",
46
46
  "@wix/forms": "^1.0.384",
47
47
  "@wix/headless-media": "0.0.25",
@@ -91,5 +91,5 @@
91
91
  "groupId": "com.wixpress.headless-components"
92
92
  }
93
93
  },
94
- "falconPackageHash": "a44adc789e3e848632876173dae3dbb6d3491eb296e0f6250c1d8091"
94
+ "falconPackageHash": "d2d5fc131a53bbc4b56519d28eb9b64e4b51ba51424316274177040e"
95
95
  }