@sonic-equipment/ui 122.0.0 → 124.0.0

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 (95) hide show
  1. package/dist/buttons/add-to-cart-button/add-to-cart-button.js +2 -2
  2. package/dist/buttons/add-to-cart-button/connected-add-to-cart-button.js +8 -0
  3. package/dist/buttons/button/button.d.ts +2 -1
  4. package/dist/buttons/button/button.js +8 -3
  5. package/dist/buttons/button/button.module.css.js +1 -1
  6. package/dist/checkout/adyen-payment.d.ts +21 -0
  7. package/dist/checkout/adyen-payment.js +165 -0
  8. package/dist/checkout/parse-amount.d.ts +2 -0
  9. package/dist/checkout/parse-amount.js +10 -0
  10. package/dist/checkout/payment-details.d.ts +5 -0
  11. package/dist/checkout/payment-details.js +21 -0
  12. package/dist/checkout/payment.d.ts +6 -0
  13. package/dist/checkout/payment.js +69 -0
  14. package/dist/collapsables/accordion/accordion-item.d.ts +2 -1
  15. package/dist/collapsables/accordion/accordion-item.js +6 -2
  16. package/dist/collapsables/accordion/accordion.module.css.js +1 -1
  17. package/dist/config.js +1 -1
  18. package/dist/exports.d.ts +22 -0
  19. package/dist/forms/field-error/field-error.d.ts +14 -1
  20. package/dist/forms/field-error/field-error.js +15 -1
  21. package/dist/forms/input/input.d.ts +1 -0
  22. package/dist/forms/input/input.js +2 -2
  23. package/dist/forms/number-field/number-field.d.ts +4 -1
  24. package/dist/forms/number-field/number-field.js +3 -3
  25. package/dist/forms/select/select.d.ts +4 -1
  26. package/dist/forms/select/select.js +6 -3
  27. package/dist/forms/text-field/text-field.d.ts +4 -1
  28. package/dist/forms/text-field/text-field.js +3 -3
  29. package/dist/forms/textarea/textarea.d.ts +1 -0
  30. package/dist/forms/textarea/textarea.js +10 -10
  31. package/dist/icons/stroke/stroke-information-icon.js +7 -0
  32. package/dist/index.js +24 -2
  33. package/dist/intl/translation-id.d.ts +1 -1
  34. package/dist/loading/progress-circle.d.ts +3 -1
  35. package/dist/loading/progress-circle.js +3 -3
  36. package/dist/loading/progress-circle.module.css.js +1 -1
  37. package/dist/pages/cart-page/cart-page.js +1 -0
  38. package/dist/pages/product-details-page/product-details.js +7 -0
  39. package/dist/shared/api/bff/model/bff.model.d.ts +1 -0
  40. package/dist/shared/api/storefront/hooks/authentication/use-create-guest-account.d.ts +4 -0
  41. package/dist/shared/api/storefront/hooks/authentication/use-create-guest-account.js +21 -0
  42. package/dist/shared/api/storefront/hooks/authentication/use-patch-session.d.ts +4 -0
  43. package/dist/shared/api/storefront/hooks/authentication/use-patch-session.js +15 -0
  44. package/dist/shared/api/storefront/hooks/authentication/use-sign-in.js +14 -9
  45. package/dist/shared/api/storefront/hooks/authentication/use-sign-out.js +1 -2
  46. package/dist/shared/api/storefront/hooks/cart/use-add-product-to-current-cart.js +1 -7
  47. package/dist/shared/api/storefront/hooks/cart/use-delete-cart-line-by-id.js +1 -4
  48. package/dist/shared/api/storefront/hooks/cart/use-fetch-current-cart.js +3 -1
  49. package/dist/shared/api/storefront/hooks/cart/use-patch-cart.d.ts +3 -4
  50. package/dist/shared/api/storefront/hooks/cart/use-patch-cart.js +6 -8
  51. package/dist/shared/api/storefront/hooks/cart/use-place-order.d.ts +3 -3
  52. package/dist/shared/api/storefront/hooks/cart/use-place-order.js +7 -8
  53. package/dist/shared/api/storefront/hooks/cart/use-update-cart-line-by-id.js +1 -13
  54. package/dist/shared/api/storefront/hooks/customer/use-fetch-bill-to-addresses.d.ts +1 -0
  55. package/dist/shared/api/storefront/hooks/customer/use-fetch-bill-to-addresses.js +14 -0
  56. package/dist/shared/api/storefront/hooks/customer/use-fetch-ship-to-addresses.d.ts +3 -0
  57. package/dist/shared/api/storefront/hooks/customer/use-fetch-ship-to-addresses.js +15 -0
  58. package/dist/shared/api/storefront/hooks/customer/use-update-bill-to-address.d.ts +6 -0
  59. package/dist/shared/api/storefront/hooks/customer/use-update-bill-to-address.js +17 -0
  60. package/dist/shared/api/storefront/hooks/payment/use-create-adyen-session.d.ts +3 -0
  61. package/dist/shared/api/storefront/hooks/payment/use-create-adyen-session.js +10 -0
  62. package/dist/shared/api/storefront/hooks/payment/use-fetch-adyen-config.d.ts +1 -0
  63. package/dist/shared/api/storefront/hooks/payment/use-fetch-adyen-config.js +14 -0
  64. package/dist/shared/api/storefront/hooks/payment/use-invalidate-adyen.d.ts +1 -0
  65. package/dist/shared/api/storefront/hooks/payment/use-invalidate-adyen.js +9 -0
  66. package/dist/shared/api/storefront/model/shop.model.d.ts +17 -0
  67. package/dist/shared/api/storefront/services/cart-service.d.ts +15 -2
  68. package/dist/shared/api/storefront/services/cart-service.js +68 -3
  69. package/dist/shared/api/storefront/services/customer-service.d.ts +8 -0
  70. package/dist/shared/api/storefront/services/customer-service.js +31 -0
  71. package/dist/shared/api/storefront/services/payment-service.d.ts +8 -0
  72. package/dist/shared/api/storefront/services/payment-service.js +29 -0
  73. package/dist/shared/ga/data-layer.d.ts +2 -0
  74. package/dist/shared/ga/data-layer.js +13 -0
  75. package/dist/shared/ga/google-analytics-provider.d.ts +8 -0
  76. package/dist/shared/ga/google-analytics-provider.js +15 -0
  77. package/dist/shared/ga/types.d.ts +34 -0
  78. package/dist/shared/ga/types.js +8 -0
  79. package/dist/shared/ga/use-data-layer.d.ts +50 -0
  80. package/dist/shared/ga/use-data-layer.js +89 -0
  81. package/dist/shared/model/address.d.ts +2 -0
  82. package/dist/shared/model/address.js +7 -0
  83. package/dist/shared/model/countries.d.ts +225 -0
  84. package/dist/shared/model/countries.js +260 -0
  85. package/dist/shared/model/currency.d.ts +1 -0
  86. package/dist/shared/model/currency.js +1 -0
  87. package/dist/shared/utils/price.d.ts +38 -0
  88. package/dist/shared/utils/price.js +39 -1
  89. package/dist/shared/utils/random.d.ts +10 -0
  90. package/dist/shared/utils/random.js +16 -0
  91. package/dist/styles.css +306 -107
  92. package/dist/tooltip/tooltip.d.ts +9 -0
  93. package/dist/tooltip/tooltip.js +21 -0
  94. package/dist/tooltip/tooltip.module.css.js +3 -0
  95. package/package.json +2 -2
@@ -72,7 +72,7 @@ function SpinnerState({ isDisabled, onChange, onManualInput, quantity, }) {
72
72
  useEffect(() => {
73
73
  setInternalQuantity(quantity);
74
74
  }, [quantity]);
75
- return (jsx(NumberField, { withButtons: true, autoGrow: true, formatOptions: {
75
+ return (jsx(NumberField, { withButtons: true, autoGrow: true, "data-test-selector": "quantity", formatOptions: {
76
76
  maximumFractionDigits: 0,
77
77
  style: 'decimal',
78
78
  useGrouping: false,
@@ -95,7 +95,7 @@ function ManualInputState({ isDisabled, onCancel, onConfirm, quantity, }) {
95
95
  if (e.key === 'Escape')
96
96
  onCancel();
97
97
  };
98
- return (jsxs("div", { className: styles['manual-input-container'], children: [jsx("div", { className: styles['left-button-spacer'] }), jsx(NumberField, { autoFocus: true, autoGrow: true, defaultValue: quantity ? ensureNumber(quantity) : undefined, formatOptions: {
98
+ return (jsxs("div", { className: styles['manual-input-container'], children: [jsx("div", { className: styles['left-button-spacer'] }), jsx(NumberField, { autoFocus: true, autoGrow: true, "data-test-selector": "quantity", defaultValue: quantity ? ensureNumber(quantity) : undefined, formatOptions: {
99
99
  maximumFractionDigits: 0,
100
100
  style: 'decimal',
101
101
  useGrouping: false,
@@ -5,11 +5,13 @@ import { useAddProductToCurrentCart } from '../../shared/api/storefront/hooks/ca
5
5
  import { useDeleteCartLineById } from '../../shared/api/storefront/hooks/cart/use-delete-cart-line-by-id.js';
6
6
  import { useFetchCurrentCartLines } from '../../shared/api/storefront/hooks/cart/use-fetch-current-cart-lines.js';
7
7
  import { useUpdateCartLineById } from '../../shared/api/storefront/hooks/cart/use-update-cart-line-by-id.js';
8
+ import { useDataLayer } from '../../shared/ga/use-data-layer.js';
8
9
  import { useCartEvents } from '../../shared/providers/cart-provider.js';
9
10
  import { useToast } from '../../toast/use-toast.js';
10
11
  import { AddToCartButton } from './add-to-cart-button.js';
11
12
 
12
13
  const ConnectedAddToCartButton = ({ onAddToCart, productId }) => {
14
+ const { createEcommerceEvent, dataLayer } = useDataLayer();
13
15
  const { isPending: isPendingAddToCart, mutate: addToCart } = useAddProductToCurrentCart();
14
16
  const { data: cartLines, isLoading: isLoadingCartLines } = useFetchCurrentCartLines();
15
17
  const { isPending: isPendingDeleteCartLine, mutate: deleteCartLine } = useDeleteCartLineById();
@@ -77,6 +79,12 @@ const ConnectedAddToCartButton = ({ onAddToCart, productId }) => {
77
79
  onSuccess: cartLine => {
78
80
  onAddToCart?.({ cartLine });
79
81
  onCartLineAdded?.(cartLine);
82
+ dataLayer.push(createEcommerceEvent({
83
+ cartLine,
84
+ event: {
85
+ event: 'add_to_cart',
86
+ },
87
+ }));
80
88
  addToast({
81
89
  body: (jsx(FormattedMessage, { id: "The product has been added to your cart." })),
82
90
  isUserDismissable: false,
@@ -9,10 +9,11 @@ export interface ButtonProps {
9
9
  icon?: React.ReactNode;
10
10
  iconPosition?: 'left' | 'right';
11
11
  isDisabled?: boolean;
12
+ isLoading?: string | ReactNode | boolean;
12
13
  onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
13
14
  size?: 'sm' | 'md' | 'lg';
14
15
  type?: 'button' | 'submit' | 'reset';
15
16
  variant?: 'solid' | 'outline' | 'ghost';
16
17
  withArrow?: boolean;
17
18
  }
18
- export declare function Button({ _pseudo, children, className, color, condensed, 'data-test-selector': dataTestSelector, icon, iconPosition, isDisabled, onClick: _onClick, size, type, variant, withArrow, }: ButtonProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare function Button({ _pseudo, children, className, color, condensed, 'data-test-selector': dataTestSelector, icon, iconPosition, isDisabled, isLoading, onClick: _onClick, size, type, variant, withArrow, }: ButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -1,10 +1,12 @@
1
1
  "use client";
2
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { isValidElement } from 'react';
3
4
  import clsx from 'clsx';
5
+ import { ProgressCircle } from '../../loading/progress-circle.js';
4
6
  import { GlyphsArrowBoldCapsRightIcon } from '../../icons/glyph/glyphs-arrow-boldcaps-right-icon.js';
5
7
  import buttonStyles from './button.module.css.js';
6
8
 
7
- function Button({ _pseudo = 'none', children, className, color = 'primary', condensed, 'data-test-selector': dataTestSelector, icon, iconPosition = 'left', isDisabled, onClick: _onClick, size = 'lg', type = 'button', variant = 'solid', withArrow = false, }) {
9
+ function Button({ _pseudo = 'none', children, className, color = 'primary', condensed, 'data-test-selector': dataTestSelector, icon, iconPosition = 'left', isDisabled, isLoading = false, onClick: _onClick, size = 'lg', type = 'button', variant = 'solid', withArrow = false, }) {
8
10
  const showIconOnLeft = icon && iconPosition === 'left';
9
11
  const showIconOnRight = icon && iconPosition === 'right';
10
12
  const onClick = (e) => {
@@ -15,7 +17,10 @@ function Button({ _pseudo = 'none', children, className, color = 'primary', cond
15
17
  return;
16
18
  e.preventDefault();
17
19
  };
18
- return (jsxs("button", { className: clsx({ [buttonStyles.condensed]: condensed }, { [buttonStyles.icon]: icon }, buttonStyles.button, buttonStyles[variant], buttonStyles[size], buttonStyles[color], buttonStyles[_pseudo], className), "data-disabled": isDisabled ? true : undefined, "data-test-selector": dataTestSelector, disabled: isDisabled, onClick: onClick, type: type, children: [jsx(Fragment, { children: showIconOnLeft && jsx("span", { className: buttonStyles.icon, children: icon }) }), children, withArrow && (jsx(GlyphsArrowBoldCapsRightIcon, { className: buttonStyles['right-arrow-icon'] })), showIconOnRight && jsx("span", { className: buttonStyles.icon, children: icon })] }));
20
+ return (jsx("button", { className: clsx({ [buttonStyles.condensed]: condensed }, { [buttonStyles.icon]: icon }, { [buttonStyles['loading-uninformative']]: isLoading === true }, {
21
+ [buttonStyles['loading-informative']]: isLoading &&
22
+ (typeof isLoading === 'string' || isValidElement(isLoading)),
23
+ }, buttonStyles.button, buttonStyles[variant], buttonStyles[size], buttonStyles[color], buttonStyles[_pseudo], className), "data-disabled": isDisabled ? true : undefined, "data-test-selector": dataTestSelector, disabled: isDisabled, onClick: onClick, type: type, children: jsxs(Fragment, { children: [showIconOnLeft && jsx("span", { className: buttonStyles.icon, children: icon }), jsx("span", { className: buttonStyles.children, children: isLoading ? (isLoading === true ? children : isLoading) : children }), withArrow && (jsx(GlyphsArrowBoldCapsRightIcon, { className: buttonStyles['right-arrow-icon'] })), showIconOnRight && jsx("span", { className: buttonStyles.icon, children: icon }), isLoading && (jsx(ProgressCircle, { className: buttonStyles.spinner, size: "sm", variant: color === 'primary' ? 'white' : 'gray' }))] }) }));
19
24
  }
20
25
 
21
26
  export { Button };
@@ -1,3 +1,3 @@
1
- var buttonStyles = {"button":"button-module-V4meK","icon":"button-module-XaNWz","sm":"button-module-Pbwz7","md":"button-module-GVTEW","condensed":"button-module-GKHQc","lg":"button-module-nyNY8","primary":"button-module-tmyk8","outline":"button-module-vq9GI","solid":"button-module-AjvlY","hover":"button-module-YzPAr","focus":"button-module--xzsY","active":"button-module-XMFzj","ghost":"button-module-f4UVe","right-arrow-icon":"button-module-ydQAo","secondary":"button-module--1bCH"};
1
+ var buttonStyles = {"button":"button-module-V4meK","icon":"button-module-XaNWz","loading-uninformative":"button-module-LwuW2","loading-informative":"button-module-U5IxM","spinner":"button-module-13ndF","children":"button-module-vqRq-","primary":"button-module-tmyk8","secondary":"button-module--1bCH","sm":"button-module-Pbwz7","md":"button-module-GVTEW","condensed":"button-module-GKHQc","lg":"button-module-nyNY8","outline":"button-module-vq9GI","solid":"button-module-AjvlY","hover":"button-module-YzPAr","focus":"button-module--xzsY","active":"button-module-XMFzj","ghost":"button-module-f4UVe","right-arrow-icon":"button-module-ydQAo"};
2
2
 
3
3
  export { buttonStyles as default };
@@ -0,0 +1,21 @@
1
+ import { MutableRefObject } from 'react';
2
+ import Dropin from '@adyen/adyen-web/dist/types/components/Dropin';
3
+ import { AdyenPaymentModel } from 'shared/api/storefront/model/shop.model';
4
+ import '@adyen/adyen-web/dist/adyen.css';
5
+ interface AdyenPaymentProps {
6
+ amount: number;
7
+ billToId: string;
8
+ cartId: string;
9
+ countryCode: string;
10
+ currencyCode: string;
11
+ dropinRef: MutableRefObject<Dropin | null>;
12
+ environment: 'test' | 'live' | 'live-us' | 'live-au' | 'live-apse' | 'live-in';
13
+ locale: string;
14
+ onComplete: (result: AdyenPaymentModel) => Promise<void>;
15
+ onError: (error: unknown, result: AdyenPaymentModel) => void;
16
+ orderAmount: number;
17
+ redirectResult: string | undefined;
18
+ returnUrl: string;
19
+ }
20
+ export declare function AdyenPayment({ amount, billToId, cartId, countryCode, currencyCode, dropinRef, environment, locale, onComplete, onError, orderAmount, redirectResult, returnUrl, }: AdyenPaymentProps): import("react/jsx-runtime").JSX.Element;
21
+ export {};
@@ -0,0 +1,165 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useRef, useEffect } from 'react';
3
+ import AdyenCheckout from '@adyen/adyen-web';
4
+ import qs from 'query-string';
5
+ import { useCreateAdyenSession } from '../shared/api/storefront/hooks/payment/use-create-adyen-session.js';
6
+ import { useFetchAdyenConfig } from '../shared/api/storefront/hooks/payment/use-fetch-adyen-config.js';
7
+ import { getAdyenPaymentDetails, postAdyenPayment } from '../shared/api/storefront/services/cart-service.js';
8
+ import { parseAmount } from './parse-amount.js';
9
+
10
+ function AdyenPayment({ amount, billToId, cartId, countryCode, currencyCode, dropinRef, environment, locale, onComplete, onError, orderAmount, redirectResult, returnUrl, }) {
11
+ const dropinDivRef = useRef(null);
12
+ const { data: adyenSettings } = useFetchAdyenConfig();
13
+ const { data: adyenSession, error, mutate: createAdyenSession, } = useCreateAdyenSession();
14
+ useEffect(() => {
15
+ createAdyenSession({
16
+ cartId,
17
+ orderAmount,
18
+ returnUrl,
19
+ });
20
+ }, [cartId, countryCode, orderAmount, returnUrl, createAdyenSession]);
21
+ useEffect(() => {
22
+ if (!adyenSettings || !adyenSession || !dropinDivRef.current)
23
+ return;
24
+ const options = {
25
+ amount: parseAmount(amount, countryCode),
26
+ analytics: {
27
+ enabled: false,
28
+ },
29
+ clientKey: adyenSettings.clientKey,
30
+ countryCode,
31
+ environment,
32
+ locale,
33
+ onAdditionalDetails: (async (state, _component) => {
34
+ if (!state.details.redirectResult) {
35
+ throw new Error('No redirectResult');
36
+ }
37
+ const result = await getAdyenPaymentDetails({
38
+ redirectResult: state.details.redirectResult,
39
+ });
40
+ const { amount: adyenAmount, customerId: adyenCustomerId } = getAndRemoveAdyenQueryParams();
41
+ if (amount.toFixed(2).replaceAll(/[,.]/gi, '') !== adyenAmount)
42
+ return onError(new Error('Invalid amount'), result);
43
+ if (billToId !== adyenCustomerId)
44
+ return onError(new Error('Invalid customer'), result);
45
+ return handlePaymentResponse(result, onComplete, onError);
46
+ }),
47
+ onSubmit: (async (state, _component) => {
48
+ if (state.data.paymentMethod.type === 'paybybank') {
49
+ state.data.countryCode = countryCode;
50
+ }
51
+ const result = await postAdyenPayment({
52
+ currencyCode,
53
+ data: state.data,
54
+ orderAmount,
55
+ returnUrl,
56
+ webOrderNumber: adyenSession.webOrderNumber,
57
+ });
58
+ if (result.action) {
59
+ if (result.action.type === 'redirect') {
60
+ return handleRedirectPaymentAction(result);
61
+ }
62
+ throw new Error('Invalid payment response');
63
+ }
64
+ return handlePaymentResponse(result, onComplete, onError);
65
+ }),
66
+ session: {
67
+ ...adyenSession,
68
+ id: adyenSession.transactionId,
69
+ },
70
+ showPayButton: false,
71
+ };
72
+ (async function loadAdyen() {
73
+ if (!dropinDivRef.current)
74
+ return;
75
+ const checkout = await AdyenCheckout(options);
76
+ const dropIn = checkout.create('dropin');
77
+ if (redirectResult)
78
+ checkout.submitDetails({ details: { redirectResult } });
79
+ if (dropinRef.current)
80
+ dropinRef.current.unmount();
81
+ dropinRef.current = dropIn.mount(dropinDivRef.current);
82
+ })();
83
+ }, [
84
+ billToId,
85
+ adyenSettings,
86
+ adyenSession,
87
+ amount,
88
+ countryCode,
89
+ currencyCode,
90
+ environment,
91
+ locale,
92
+ orderAmount,
93
+ redirectResult,
94
+ returnUrl,
95
+ dropinRef,
96
+ onComplete,
97
+ onError,
98
+ ]);
99
+ if (error)
100
+ return jsx("div", { children: String(error) });
101
+ return jsx("div", { ref: dropinDivRef, id: "dropin" });
102
+ }
103
+ function getAndRemoveAdyenQueryParams() {
104
+ if (typeof window === 'undefined')
105
+ return {};
106
+ const params = qs.parse(window.location.search || '');
107
+ const { amount, customerId, redirectResult } = params;
108
+ delete params['redirectResult'];
109
+ delete params['amount'];
110
+ delete params['customerId'];
111
+ history?.pushState({}, '', `${window.location.pathname}${qs.stringify(params) ? `?${qs.stringify(params)}` : ''}`);
112
+ return { amount, customerId, redirectResult };
113
+ }
114
+ async function handlePaymentResponse(result, onSubmit, onError) {
115
+ try {
116
+ if (result.action) {
117
+ if (result.action.type === 'redirect') {
118
+ handleRedirectPaymentAction(result);
119
+ }
120
+ else {
121
+ throw new Error('Adyen_Error');
122
+ }
123
+ }
124
+ else {
125
+ switch (result.resultCode) {
126
+ case 'Pending':
127
+ case 'Received':
128
+ case 'Authorised':
129
+ return await onSubmit(result);
130
+ case 'Refused':
131
+ throw new Error('Adyen_Refused');
132
+ case 'Cancelled':
133
+ throw new Error('Adyen_Cancelled');
134
+ default:
135
+ throw new Error('Adyen_Error');
136
+ }
137
+ }
138
+ }
139
+ catch (error) {
140
+ onError(error, result);
141
+ }
142
+ }
143
+ function handleRedirectPaymentAction(adyenPaymentResult) {
144
+ if (typeof window === 'undefined' || typeof document === 'undefined')
145
+ return;
146
+ if (adyenPaymentResult.action.method === 'GET') {
147
+ window.location.href = adyenPaymentResult.action.url;
148
+ }
149
+ else if (adyenPaymentResult.action.method === 'POST') {
150
+ const form = document.createElement('form');
151
+ form.method = 'POST';
152
+ form.action = adyenPaymentResult.action.url;
153
+ for (const key in adyenPaymentResult.action.data) {
154
+ const input = document.createElement('input');
155
+ input.name = key;
156
+ input.value = adyenPaymentResult.action.data[key];
157
+ form.append(input);
158
+ }
159
+ document.body.append(form);
160
+ form.submit();
161
+ form.remove();
162
+ }
163
+ }
164
+
165
+ export { AdyenPayment };
@@ -0,0 +1,2 @@
1
+ import { PaymentAmountExtended } from '@adyen/adyen-web/dist/types/types';
2
+ export declare function parseAmount(amount: string | number, countryCode: string): PaymentAmountExtended;
@@ -0,0 +1,10 @@
1
+ import { getCurrencyByCountryCode } from '../shared/utils/price.js';
2
+
3
+ function parseAmount(amount, countryCode) {
4
+ return {
5
+ currency: getCurrencyByCountryCode(countryCode),
6
+ value: Number(amount),
7
+ };
8
+ }
9
+
10
+ export { parseAmount };
@@ -0,0 +1,5 @@
1
+ import { CartModel } from '../shared/api/storefront/model/shop.model';
2
+ export declare function PaymentDetails({ cart, onCartChanged, }: {
3
+ cart: CartModel;
4
+ onCartChanged: (cart: CartModel) => void;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { useState, useEffect } from 'react';
3
+ import { TextField } from '../forms/text-field/text-field.js';
4
+ import { useFormattedMessage } from '../intl/use-formatted-message.js';
5
+ import { useDebouncedCallback } from '../shared/hooks/use-debounced-callback.js';
6
+
7
+ function PaymentDetails({ cart, onCartChanged, }) {
8
+ const [vatNumber, setVatNumber] = useState(cart.customerVatNumber);
9
+ const [poNumber, setPONumber] = useState(cart.poNumber);
10
+ const t = useFormattedMessage();
11
+ const onCartChangedDebounced = useDebouncedCallback(onCartChanged, 500);
12
+ useEffect(() => {
13
+ if (!vatNumber || !poNumber)
14
+ return;
15
+ onCartChangedDebounced({ ...cart, customerVatNumber: vatNumber, poNumber });
16
+ // eslint-disable-next-line react-hooks/exhaustive-deps
17
+ }, [vatNumber, poNumber]);
18
+ return (jsxs(Fragment, { children: [jsx(TextField, { isRequired: true, showLabel: true, label: t('VAT Number'), minLength: 8, onChange: setVatNumber, value: vatNumber }), jsx(TextField, { isRequired: true, showLabel: true, label: t('PO Number'), onChange: setPONumber, value: poNumber })] }));
19
+ }
20
+
21
+ export { PaymentDetails };
@@ -0,0 +1,6 @@
1
+ import { AdyenPaymentModel, CartModel } from '../shared/api/storefront/model/shop.model';
2
+ export declare function Payment({ cart: _cart, onError, onPaymentComplete, }: {
3
+ cart: CartModel;
4
+ onError: (error: unknown, result: AdyenPaymentModel) => void;
5
+ onPaymentComplete: () => void;
6
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,69 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { useRef, useEffect, useCallback } from 'react';
3
+ import { Form } from 'react-aria-components';
4
+ import qs from 'query-string';
5
+ import { Button } from '../buttons/button/button.js';
6
+ import { usePatchCart } from '../shared/api/storefront/hooks/cart/use-patch-cart.js';
7
+ import { usePlaceOrder } from '../shared/api/storefront/hooks/cart/use-place-order.js';
8
+ import { currencySymbolToISO } from '../shared/model/currency.js';
9
+ import { AdyenPayment } from './adyen-payment.js';
10
+ import { PaymentDetails } from './payment-details.js';
11
+
12
+ function Payment({ cart: _cart, onError, onPaymentComplete, }) {
13
+ const patchCart = usePatchCart();
14
+ const placeOrder = usePlaceOrder();
15
+ const dropinRef = useRef(null);
16
+ const cartRef = useRef(_cart);
17
+ const cart = cartRef.current;
18
+ const { redirectResult } =
19
+ // eslint-disable-next-line ssr-friendly/no-dom-globals-in-react-fc
20
+ typeof window === 'undefined' ? {} : qs.parse(window.location.search || '');
21
+ const allowSubmit = !redirectResult;
22
+ const countryCode = _cart.billTo?.country?.abbreviation;
23
+ useEffect(() => {
24
+ cartRef.current = _cart;
25
+ }, [_cart]);
26
+ async function onSubmit() {
27
+ const cart = cartRef.current;
28
+ dropinRef.current?.showValidation();
29
+ if (!cart.customerVatNumber)
30
+ return;
31
+ if (dropinRef.current) {
32
+ if (!dropinRef.current.isValid)
33
+ return;
34
+ await patchCart({ cart });
35
+ dropinRef.current.submit();
36
+ }
37
+ }
38
+ const onComplete = useCallback(async (result) => {
39
+ const cart = cartRef.current;
40
+ await placeOrder({
41
+ cart: {
42
+ ...cart,
43
+ paymentMethod: null,
44
+ paymentOptions: {
45
+ ...cart.paymentOptions,
46
+ adyenPspReference: result.pspReference,
47
+ isAdyenDropIn: true,
48
+ },
49
+ },
50
+ });
51
+ return onPaymentComplete();
52
+ }, [onPaymentComplete, placeOrder]);
53
+ return (jsxs(Fragment, { children: [jsx("h2", { children: "Payment" }), jsxs(Form, { onSubmit: e => {
54
+ e.preventDefault();
55
+ onSubmit();
56
+ }, children: [!redirectResult && (jsx(PaymentDetails, { cart: cart, onCartChanged: cart => {
57
+ cartRef.current = cart;
58
+ } })), cart.paymentMethod?.name === 'PBI' && 'Pay by invoice', cart.paymentMethod?.name !== 'PBI' &&
59
+ cart.paymentOptions &&
60
+ cart.billTo?.id &&
61
+ countryCode && (jsx(AdyenPayment, { amount: cart.orderGrandTotal, billToId: cart.billTo.id, cartId: cart.trackId, countryCode: countryCode, currencyCode: currencySymbolToISO[cart.currencySymbol], dropinRef: dropinRef, environment: "test", locale: "nl-NL", onComplete: onComplete, onError: onError, orderAmount: cart.orderGrandTotal, redirectResult: !redirectResult || Array.isArray(redirectResult)
62
+ ? undefined
63
+ : redirectResult, returnUrl: typeof window === 'undefined'
64
+ ? ''
65
+ : // eslint-disable-next-line ssr-friendly/no-dom-globals-in-react-fc
66
+ `${window.location.pathname}${window.location.search ? `?${window.location.search}` : ''}` })), jsx(Button, { isDisabled: !allowSubmit, type: "submit", children: "Pay" })] })] }));
67
+ }
68
+
69
+ export { Payment };
@@ -2,6 +2,7 @@ import { ReactNode } from 'react';
2
2
  export type BorderType = 'top' | 'middle' | 'middle-accentuated' | 'bottom';
3
3
  export interface AccordionItemProps {
4
4
  _pseudo?: 'none' | 'focus' | 'hover' | 'active';
5
+ allowToggle?: boolean;
5
6
  borderType?: BorderType | BorderType[];
6
7
  children: ReactNode;
7
8
  className?: string;
@@ -11,4 +12,4 @@ export interface AccordionItemProps {
11
12
  size?: 'md' | 'lg';
12
13
  title: ReactNode;
13
14
  }
14
- export declare function AccordionItem({ _pseudo, borderType, children, className, id, initialIsOpen, isDisabled, size, title, }: AccordionItemProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function AccordionItem({ _pseudo, allowToggle, borderType, children, className, id, initialIsOpen, isDisabled, size, title, }: AccordionItemProps): import("react/jsx-runtime").JSX.Element;
@@ -7,12 +7,16 @@ import { useDisclosure } from '../../shared/hooks/use-disclosure.js';
7
7
  import { ensureArray } from '../../shared/utils/array.js';
8
8
  import styles from './accordion.module.css.js';
9
9
 
10
- function AccordionItem({ _pseudo = 'none', borderType = 'bottom', children, className, id, initialIsOpen = false, isDisabled = false, size, title, }) {
10
+ function AccordionItem({ _pseudo = 'none', allowToggle = true, borderType = 'bottom', children, className, id, initialIsOpen = false, isDisabled = false, size, title, }) {
11
11
  const { isOpen, toggle } = useDisclosure(initialIsOpen);
12
12
  const panelId = `panel-${id}`;
13
13
  return (jsxs("div", { className: clsx(className, ...ensureArray(borderType).map(type => styles[`border-type-${type}`]), styles['accordion-item'], {
14
14
  [styles['is-open']]: isOpen,
15
- }), children: [jsx("h3", { children: jsxs("button", { "aria-controls": panelId, "aria-expanded": isOpen, className: clsx(styles.button, styles[_pseudo]), disabled: isDisabled, id: id, onClick: toggle, type: "button", children: [title, jsx("span", { className: styles.icon, children: size === 'lg' ? (jsx(GlyphsChevronsBoldDownIcon, {})) : (jsx(GlyphsChevronsSlimDownIcon, {})) })] }) }), jsx("div", { "aria-labelledby": id, className: styles.panel, id: panelId, role: "region", children: jsx("div", { className: styles.content, children: children }) })] }));
15
+ [styles['allow-toggle']]: allowToggle,
16
+ }), children: [jsx("h3", { children: jsxs("button", { "aria-controls": panelId, "aria-expanded": isOpen, className: clsx(styles.button, styles[_pseudo]), disabled: isDisabled, id: id, onClick: () => {
17
+ if (allowToggle)
18
+ toggle();
19
+ }, type: "button", children: [title, jsx("span", { className: styles.icon, children: size === 'lg' ? (jsx(GlyphsChevronsBoldDownIcon, {})) : (jsx(GlyphsChevronsSlimDownIcon, {})) })] }) }), jsx("div", { "aria-labelledby": id, className: styles.panel, id: panelId, role: "region", children: jsx("div", { className: styles.content, children: children }) })] }));
16
20
  }
17
21
 
18
22
  export { AccordionItem };
@@ -1,3 +1,3 @@
1
- var styles = {"accordion":"accordion-module-9WvAH","indented":"accordion-module-6CcEH","white":"accordion-module-CaVdG","accordion-item":"accordion-module-lf9d-","lg":"accordion-module-0qnae","with-seperators":"accordion-module-yOLrW","button":"accordion-module--Rwpb","icon":"accordion-module-Y50uq","focus":"accordion-module-M4BZs","panel":"accordion-module-KZjMo","content":"accordion-module-ejMH3","border-type-bottom":"accordion-module-oTdZK","border-type-top":"accordion-module-0mrLq","border-type-middle":"accordion-module-aAr-R","is-open":"accordion-module-W0F1z","border-type-middle-accentuated":"accordion-module-OB98a"};
1
+ var styles = {"accordion":"accordion-module-9WvAH","indented":"accordion-module-6CcEH","white":"accordion-module-CaVdG","accordion-item":"accordion-module-lf9d-","lg":"accordion-module-0qnae","with-seperators":"accordion-module-yOLrW","button":"accordion-module--Rwpb","icon":"accordion-module-Y50uq","focus":"accordion-module-M4BZs","allow-toggle":"accordion-module-QEO2d","panel":"accordion-module-KZjMo","content":"accordion-module-ejMH3","border-type-bottom":"accordion-module-oTdZK","border-type-top":"accordion-module-0mrLq","border-type-middle":"accordion-module-aAr-R","is-open":"accordion-module-W0F1z","border-type-middle-accentuated":"accordion-module-OB98a"};
2
2
 
3
3
  export { styles as default };
package/dist/config.js CHANGED
@@ -3,7 +3,7 @@ import main from './shared/utils/merge.js';
3
3
 
4
4
  const env = (typeof process === 'undefined'
5
5
  ? { VITE_SHOP_API_URL: ""}
6
- : main(process.env, { VITE_SHOP_API_URL: ""}));
6
+ : main({ ...process.env }, { VITE_SHOP_API_URL: ""}));
7
7
  const validateEnvVariable = (name, value) => {
8
8
  if (value)
9
9
  return value;
package/dist/exports.d.ts CHANGED
@@ -55,6 +55,10 @@ export * from './carousel/usp-carousel/product-usp-carousel-slide';
55
55
  export * from './carousel/usp-carousel/usp-carousel';
56
56
  export * from './cart-totals/cart-totals';
57
57
  export * from './cart-totals/cart-totals-summary';
58
+ export * from './checkout/adyen-payment';
59
+ export * from './checkout/parse-amount';
60
+ export * from './checkout/payment';
61
+ export * from './checkout/payment-details';
58
62
  export * from './collapsables/accordion/accordion';
59
63
  export * from './collapsables/accordion/accordion-item';
60
64
  export * from './collapsables/show-all/show-all';
@@ -163,9 +167,11 @@ export * from './shared/api/bff/hooks/use-fetch-announcements';
163
167
  export * from './shared/api/bff/hooks/use-fetch-product-details-page-data';
164
168
  export * from './shared/api/bff/hooks/use-fetch-product-listing-page-data';
165
169
  export * from './shared/api/bff/services/bff-service';
170
+ export * from './shared/api/storefront/hooks/authentication/use-create-guest-account';
166
171
  export * from './shared/api/storefront/hooks/authentication/use-fetch-session';
167
172
  export * from './shared/api/storefront/hooks/authentication/use-invalidate-session';
168
173
  export * from './shared/api/storefront/hooks/authentication/use-is-authenticated';
174
+ export * from './shared/api/storefront/hooks/authentication/use-patch-session';
169
175
  export * from './shared/api/storefront/hooks/authentication/use-sign-in';
170
176
  export * from './shared/api/storefront/hooks/authentication/use-sign-out';
171
177
  export * from './shared/api/storefront/hooks/cart/use-add-product-to-current-cart';
@@ -184,6 +190,12 @@ export * from './shared/api/storefront/hooks/cart/use-patch-cart';
184
190
  export * from './shared/api/storefront/hooks/cart/use-place-order';
185
191
  export * from './shared/api/storefront/hooks/cart/use-save-cart-for-later';
186
192
  export * from './shared/api/storefront/hooks/cart/use-update-cart-line-by-id';
193
+ export * from './shared/api/storefront/hooks/customer/use-fetch-bill-to-addresses';
194
+ export * from './shared/api/storefront/hooks/customer/use-fetch-ship-to-addresses';
195
+ export * from './shared/api/storefront/hooks/customer/use-update-bill-to-address';
196
+ export * from './shared/api/storefront/hooks/payment/use-create-adyen-session';
197
+ export * from './shared/api/storefront/hooks/payment/use-fetch-adyen-config';
198
+ export * from './shared/api/storefront/hooks/payment/use-invalidate-adyen';
187
199
  export * from './shared/api/storefront/hooks/translation/use-fetch-translations';
188
200
  export * from './shared/api/storefront/hooks/website/use-fetch-countries';
189
201
  export * from './shared/api/storefront/hooks/website/use-fetch-countries-languages';
@@ -198,11 +210,17 @@ export * from './shared/api/storefront/hooks/wishlist/use-fetch-all-wishlists-it
198
210
  export * from './shared/api/storefront/hooks/wishlist/use-fetch-wishlists';
199
211
  export * from './shared/api/storefront/services/authentication-service';
200
212
  export * from './shared/api/storefront/services/cart-service';
213
+ export * from './shared/api/storefront/services/customer-service';
214
+ export * from './shared/api/storefront/services/payment-service';
201
215
  export * from './shared/api/storefront/services/translation-service';
202
216
  export * from './shared/api/storefront/services/website-service';
203
217
  export * from './shared/api/storefront/services/wishlist-service';
204
218
  export * from './shared/feature-flags/use-feature-flags';
205
219
  export * from './shared/fetch/request';
220
+ export * from './shared/ga/data-layer';
221
+ export * from './shared/ga/google-analytics-provider';
222
+ export * from './shared/ga/types';
223
+ export * from './shared/ga/use-data-layer';
206
224
  export * from './shared/hooks/use-breakpoint';
207
225
  export * from './shared/hooks/use-cookie';
208
226
  export * from './shared/hooks/use-debounced-callback';
@@ -215,8 +233,10 @@ export * from './shared/hooks/use-scroll-lock';
215
233
  export * from './shared/hooks/use-scroll-to';
216
234
  export * from './shared/hooks/use-session-storage';
217
235
  export * from './shared/hooks/use-watch-css-property';
236
+ export * from './shared/model/address';
218
237
  export * from './shared/model/announcement';
219
238
  export * from './shared/model/category';
239
+ export * from './shared/model/countries';
220
240
  export * from './shared/model/countries-languages';
221
241
  export * from './shared/model/currency';
222
242
  export * from './shared/model/hit';
@@ -238,6 +258,7 @@ export * from './shared/utils/environment';
238
258
  export * from './shared/utils/event-emitter';
239
259
  export * from './shared/utils/merge';
240
260
  export * from './shared/utils/price';
261
+ export * from './shared/utils/random';
241
262
  export * from './shared/utils/time';
242
263
  export * from './shared/utils/uuid';
243
264
  export * from './shared/utils/wait';
@@ -251,4 +272,5 @@ export * from './toast/toast-provider';
251
272
  export * from './toast/types';
252
273
  export * from './toast/use-toast';
253
274
  export * from './tokens/tokens';
275
+ export * from './tooltip/tooltip';
254
276
  export * from './typography/heading/heading';
@@ -1,5 +1,18 @@
1
+ import { ReactNode } from 'react';
2
+ export type ValidationError = string | string[];
3
+ export type ValidateFunction<T> = (value: T) => ValidationError | true | null | undefined;
4
+ export interface ValidationResult {
5
+ /** Whether the input value is invalid. */
6
+ isInvalid: boolean;
7
+ /** The native validation details for the input. */
8
+ validationDetails: ValidityState;
9
+ /** The current error messages for the input if it is invalid, otherwise an empty array. */
10
+ validationErrors: string[];
11
+ }
1
12
  interface FieldErrorProps {
2
- children?: string;
13
+ children?: ReactNode | ((values: ValidationResult & {
14
+ defaultChildren: ReactNode;
15
+ }) => ReactNode);
3
16
  }
4
17
  export declare function FieldError({ children }: FieldErrorProps): import("react/jsx-runtime").JSX.Element;
5
18
  export {};
@@ -1,10 +1,24 @@
1
1
  "use client";
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { FieldError as FieldError$1 } from 'react-aria-components';
4
+ import { useFormattedMessage } from '../../intl/use-formatted-message.js';
4
5
  import styles from './field-error.module.css.js';
5
6
 
6
7
  function FieldError({ children }) {
7
- return (jsx(FieldError$1, { className: styles['field-error'], children: children }));
8
+ const t = useFormattedMessage();
9
+ return (jsx(FieldError$1, { className: styles['field-error'], children: children ||
10
+ (result => {
11
+ if (!result.isInvalid)
12
+ return;
13
+ if (result.validationDetails.customError)
14
+ return result.validationErrors[0];
15
+ const validationErrorType = Object.entries(result.validationDetails)
16
+ .filter(([, value]) => value)
17
+ .map(([key]) => key)[0];
18
+ if (validationErrorType)
19
+ return t(`validation.${validationErrorType}`);
20
+ return t('validation.invalid');
21
+ }) }));
8
22
  }
9
23
 
10
24
  export { FieldError };
@@ -3,6 +3,7 @@ import { Input as AriaInput } from 'react-aria-components';
3
3
  export interface InputProps extends Omit<ComponentProps<typeof AriaInput>, 'size'> {
4
4
  _pseudo?: 'focus' | 'none';
5
5
  autoGrow?: boolean;
6
+ label: string;
6
7
  size?: 'md' | 'lg';
7
8
  value?: string | number;
8
9
  }
@@ -10,7 +10,7 @@ import styles from './input.module.css.js';
10
10
  * This component is used to create an input that grows as the user types.
11
11
  * It uses a shadow input to calculate the width of the input.
12
12
  */
13
- const Input = forwardRef(({ _pseudo = 'none', autoGrow, size = 'lg', ...inputProps }, inputRef) => {
13
+ const Input = forwardRef(({ _pseudo = 'none', autoGrow, label, size = 'lg', ...inputProps }, inputRef) => {
14
14
  const [props, ref] = useContextProps(inputProps, inputRef, InputContext);
15
15
  const { defaultValue, onChange, value: controlledValue } = props;
16
16
  const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
@@ -20,7 +20,7 @@ const Input = forwardRef(({ _pseudo = 'none', autoGrow, size = 'lg', ...inputPro
20
20
  ? onChange?.(event)
21
21
  : setUncontrolledValue(event.target.value);
22
22
  const { pressProps } = usePress({});
23
- return (jsx("div", { className: clsx(styles['input-container'], styles[size], styles[_pseudo]), children: jsxs("div", { className: clsx({ [styles['growing-input']]: autoGrow }), children: [jsx(Input$1, { size: autoGrow ? 1 : undefined, ...props, ...pressProps, ref: ref, onChange: handleChange, onClick: e => {
23
+ return (jsx("div", { className: clsx(styles['input-container'], styles[size], styles[_pseudo]), children: jsxs("div", { className: clsx({ [styles['growing-input']]: autoGrow }), children: [jsx(Input$1, { "aria-label": label, size: autoGrow ? 1 : undefined, ...props, ...pressProps, ref: ref, onChange: handleChange, onClick: e => {
24
24
  e.preventDefault();
25
25
  e.stopPropagation();
26
26
  e.target.focus();