@sonic-equipment/ui 132.0.0 → 133.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.
- package/dist/address/address.d.ts +2 -13
- package/dist/buttons/add-to-cart-button/connected-add-to-cart-button.js +1 -1
- package/dist/buttons/button/button.js +2 -1
- package/dist/delivery-time/delivery-time.js +6 -5
- package/dist/display/info-display/info-display.d.ts +7 -0
- package/dist/display/info-display/info-display.js +8 -0
- package/dist/display/info-display/info-display.module.css.js +3 -0
- package/dist/exports.d.ts +13 -5
- package/dist/forms/input/input.d.ts +1 -0
- package/dist/forms/input/input.js +5 -3
- package/dist/forms/input/input.module.css.js +1 -1
- package/dist/forms/switch/switch.d.ts +2 -1
- package/dist/forms/switch/switch.js +2 -2
- package/dist/forms/text-field/password-reveal-toggle/password-reveal-toggle.d.ts +10 -0
- package/dist/forms/text-field/password-reveal-toggle/password-reveal-toggle.js +18 -0
- package/dist/forms/text-field/password-reveal-toggle/password-reveal-toggle.module.css.js +3 -0
- package/dist/forms/text-field/text-field.d.ts +3 -3
- package/dist/forms/text-field/text-field.js +9 -2
- package/dist/icons/stroke/stroke-dehashed-icon.js +7 -0
- package/dist/icons/stroke/stroke-hashed-icon.js +7 -0
- package/dist/index.js +14 -6
- package/dist/intl/translation-id.d.ts +1 -1
- package/dist/pages/checkout/cart-page/cart-page.js +6 -9
- package/dist/pages/checkout/cart-page/components/empty-cart-page.d.ts +1 -0
- package/dist/pages/checkout/cart-page/components/empty-cart-page.js +11 -0
- package/dist/pages/checkout/components/billing-and-invoice-information.d.ts +7 -0
- package/dist/pages/checkout/components/billing-and-invoice-information.js +12 -0
- package/dist/pages/checkout/components/billing-and-invoice-information.module.css.js +3 -0
- package/dist/pages/checkout/constants.d.ts +8 -0
- package/dist/pages/checkout/constants.js +10 -0
- package/dist/pages/checkout/layouts/checkout-page-layout/components/checkout-page-section.module.css.js +1 -1
- package/dist/pages/checkout/order-confirmation-page/order-confirmation-page.d.ts +4 -0
- package/dist/pages/checkout/order-confirmation-page/order-confirmation-page.js +115 -0
- package/dist/pages/checkout/order-confirmation-page/order-confirmation-page.module.css.js +3 -0
- package/dist/{checkout → pages/checkout/payment-page/components}/adyen-payment.js +5 -5
- package/dist/pages/checkout/payment-page/components/adyen-payment.module.css.js +3 -0
- package/dist/{checkout → pages/checkout/payment-page/components}/payment.d.ts +4 -2
- package/dist/{checkout → pages/checkout/payment-page/components}/payment.js +74 -44
- package/dist/pages/checkout/payment-page/components/payment.module.css.js +3 -0
- package/dist/pages/checkout/payment-page/payment-page.js +16 -10
- package/dist/{checkout → pages/checkout/payment-page/utils}/parse-amount.js +1 -1
- package/dist/pages/checkout/shipping-page/components/edit-address.js +8 -4
- package/dist/pages/checkout/shipping-page/hooks/use-patch-shipping-details.d.ts +12 -0
- package/dist/pages/checkout/shipping-page/hooks/use-patch-shipping-details.js +21 -0
- package/dist/pages/checkout/shipping-page/shipping-page.js +37 -51
- package/dist/pages/checkout/shipping-page/shipping-page.module.css.js +1 -1
- package/dist/pages/components/page/page.d.ts +3 -2
- package/dist/pages/components/page/page.js +3 -2
- package/dist/pages/components/page-meta-data/page-meta-data.d.ts +3 -4
- package/dist/pages/components/page-meta-data/page-meta-data.js +2 -2
- package/dist/pages/product/product-details-page/product-details-page.js +2 -10
- package/dist/pages/product/product-details-page/product-details.js +10 -4
- package/dist/pages/product/product-listing-page/product-listing-page-data-types.d.ts +1 -1
- package/dist/pages/product/product-listing-page/product-listing-page.js +2 -10
- package/dist/pages/product/product-listing-page/product-listing.js +8 -1
- package/dist/pages/product/search-result-page/search-results-page.js +4 -7
- package/dist/shared/api/bff/model/bff.model.d.ts +4 -4
- package/dist/shared/api/bff/services/bff-service.js +1 -4
- package/dist/shared/api/storefront/hooks/cart/use-fetch-cart-by-id.d.ts +8 -0
- package/dist/shared/api/storefront/hooks/cart/use-fetch-cart-by-id.js +20 -0
- package/dist/shared/api/storefront/hooks/cart/use-fetch-current-cart-count.d.ts +1 -1
- package/dist/shared/api/storefront/hooks/cart/use-fetch-current-cart-count.js +3 -3
- package/dist/shared/api/storefront/hooks/cart/use-fetch-current-cart.d.ts +2 -5
- package/dist/shared/api/storefront/hooks/cart/use-fetch-current-cart.js +3 -16
- package/dist/shared/api/storefront/hooks/cart/use-patch-cart.js +11 -8
- package/dist/shared/api/storefront/hooks/cart/use-place-order.js +2 -2
- package/dist/shared/model/address.d.ts +13 -0
- package/dist/shared/providers/react-query-container.d.ts +2 -1
- package/dist/shared/providers/react-query-container.js +2 -2
- package/dist/shared/utils/date.d.ts +2 -0
- package/dist/shared/utils/date.js +11 -1
- package/dist/styles.css +331 -261
- package/dist/tooltip/tooltip.js +10 -5
- package/package.json +1 -1
- package/dist/checkout/adyen-payment.module.css.js +0 -3
- package/dist/checkout/payment.module.css.js +0 -3
- /package/dist/{checkout → pages/checkout/payment-page/components}/adyen-payment.d.ts +0 -0
- /package/dist/{checkout → pages/checkout/payment-page/hooks}/use-get-adyen-redirect-result.d.ts +0 -0
- /package/dist/{checkout → pages/checkout/payment-page/hooks}/use-get-adyen-redirect-result.js +0 -0
- /package/dist/{checkout → pages/checkout/payment-page/hooks}/use-has-returned-from-adyen.d.ts +0 -0
- /package/dist/{checkout → pages/checkout/payment-page/hooks}/use-has-returned-from-adyen.js +0 -0
- /package/dist/{checkout → pages/checkout/payment-page/utils}/parse-amount.d.ts +0 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { PrintButton } from '../../../buttons/print-button/print-button.js';
|
|
4
|
+
import { OrderLineCard } from '../../../cards/orderline-card/orderline-card.js';
|
|
5
|
+
import { CartTotals } from '../../../cart-totals/cart-totals.js';
|
|
6
|
+
import { InfoDisplay } from '../../../display/info-display/info-display.js';
|
|
7
|
+
import { FormattedMessage } from '../../../intl/formatted-message.js';
|
|
8
|
+
import { useCultureCode } from '../../../intl/use-culture-code.js';
|
|
9
|
+
import { useFormattedMessage } from '../../../intl/use-formatted-message.js';
|
|
10
|
+
import { OrderLineList } from '../../../lists/orderline-list/orderline-list.js';
|
|
11
|
+
import { LoadingPage } from '../../loading-page/loading-page.js';
|
|
12
|
+
import { useIsAuthenticated } from '../../../shared/api/storefront/hooks/authentication/use-is-authenticated.js';
|
|
13
|
+
import { useFetchCartById } from '../../../shared/api/storefront/hooks/cart/use-fetch-cart-by-id.js';
|
|
14
|
+
import { useSaveCartForLater } from '../../../shared/api/storefront/hooks/cart/use-save-cart-for-later.js';
|
|
15
|
+
import { RouteButton } from '../../../shared/routing/route-button.js';
|
|
16
|
+
import { useNavigate } from '../../../shared/routing/route-provider.js';
|
|
17
|
+
import { formatDateToLocaleString } from '../../../shared/utils/date.js';
|
|
18
|
+
import { hasNo } from '../../../shared/utils/types.js';
|
|
19
|
+
import { useToast } from '../../../toast/use-toast.js';
|
|
20
|
+
import { Page } from '../../components/page/page.js';
|
|
21
|
+
import { ErrorPage } from '../../error-page/error-page.js';
|
|
22
|
+
import { BillingAndInvoiceInformation } from '../components/billing-and-invoice-information.js';
|
|
23
|
+
import { CHECKOUT_PATHS } from '../constants.js';
|
|
24
|
+
import { CheckoutPageLayout } from '../layouts/checkout-page-layout/checkout-page-layout.js';
|
|
25
|
+
import { CheckoutPageSection } from '../layouts/checkout-page-layout/components/checkout-page-section.js';
|
|
26
|
+
import { CheckoutPageSectionContent } from '../layouts/checkout-page-layout/components/checkout-page-section-content.js';
|
|
27
|
+
import styles from './order-confirmation-page.module.css.js';
|
|
28
|
+
|
|
29
|
+
function OrderConfirmationPage({ cartId }) {
|
|
30
|
+
const isAuthenticated = useIsAuthenticated();
|
|
31
|
+
const t = useFormattedMessage();
|
|
32
|
+
const cultureCode = useCultureCode();
|
|
33
|
+
const { addToast } = useToast();
|
|
34
|
+
const { isNavigating, navigate } = useNavigate();
|
|
35
|
+
const { data: cart, error, isLoading: cartIsLoading, } = useFetchCartById({
|
|
36
|
+
id: cartId,
|
|
37
|
+
});
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!cart)
|
|
40
|
+
return;
|
|
41
|
+
if (isAuthenticated === undefined)
|
|
42
|
+
return;
|
|
43
|
+
if (!isAuthenticated)
|
|
44
|
+
return navigate(CHECKOUT_PATHS.CART);
|
|
45
|
+
if (cart.status === 'Cart')
|
|
46
|
+
navigate(CHECKOUT_PATHS.CART);
|
|
47
|
+
}, [cart, navigate, isAuthenticated]);
|
|
48
|
+
const saveCartForLater = useSaveCartForLater({
|
|
49
|
+
onError: () => {
|
|
50
|
+
addToast({
|
|
51
|
+
body: jsx(FormattedMessage, { id: "Unable to save cart for later." }),
|
|
52
|
+
isUserDismissable: false,
|
|
53
|
+
messageType: 'danger',
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
onSuccess: () => {
|
|
57
|
+
addToast({
|
|
58
|
+
body: jsx(FormattedMessage, { id: "Saved cart for later." }),
|
|
59
|
+
isUserDismissable: false,
|
|
60
|
+
messageType: 'success',
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
if (error)
|
|
65
|
+
return jsx(ErrorPage, { error: error });
|
|
66
|
+
if (cartIsLoading || isNavigating)
|
|
67
|
+
return jsx(LoadingPage, {});
|
|
68
|
+
if (!isAuthenticated ||
|
|
69
|
+
hasNo(cart) ||
|
|
70
|
+
hasNo(cart.cartLines) ||
|
|
71
|
+
cart.cartLines.length === 0) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return (jsx(Page, { breadCrumb: [{ href: CHECKOUT_PATHS.HOME, label: t('Home') }], title: t('Order confirmation'), children: jsx(CheckoutPageLayout, { actions: {
|
|
75
|
+
primary: (jsx(RouteButton, { withArrow: true, "data-test-selector": "checkoutShippingCartTotalContinueButton", href: "/", children: jsx(FormattedMessage, { id: "Continue shopping" }) })),
|
|
76
|
+
secondary: (jsxs(Fragment, { children: [cart.canSaveOrder && (jsx(RouteButton, { color: "secondary", onClick: () => {
|
|
77
|
+
saveCartForLater.mutate({ cart });
|
|
78
|
+
}, variant: "outline", children: jsx(FormattedMessage, { id: "Save order" }) })), jsx(PrintButton, {})] })),
|
|
79
|
+
}, overview: jsx(CartTotals, { fulfillmentMethod: t(`fulfillmentmethod.${cart.fulfillmentMethod}`), orderNumber: cart.orderNumber, shippingCost: cart.shippingAndHandlingDisplay, subtotal: cart.orderSubTotalDisplay, tax: cart.totalTaxDisplay, total: cart.orderGrandTotalDisplay, vatPercentage: cart.cartLines[0]?.pricing?.vatRate }), children: jsxs("div", { children: [jsx(CheckoutPageSection, { hasBorder: false, title: t('General'), children: jsx(CheckoutPageSectionContent, { children: jsxs("div", { className: styles['general-order-info'], children: [cart.orderDate && (jsx(InfoDisplay, { id: "order-date", label: t('Order date'), value: formatDateToLocaleString(new Date(cart.orderDate), cultureCode) })), cart.requestedDeliveryDate && (jsx(InfoDisplay, { id: "requested-delivery-date", label: t('Requested delivery date'), value: formatDateToLocaleString(new Date(cart.requestedDeliveryDate), cultureCode) })), cart.poNumber && (jsx(InfoDisplay, { id: "po-number", label: t('PO Number'), value: cart.poNumber }))] }) }) }), jsx(CheckoutPageSection, { hasBorder: false, title: t('Billing and shipping information'), children: jsx(CheckoutPageSectionContent, { children: jsx(BillingAndInvoiceInformation, { billToAddress: {
|
|
80
|
+
address1: cart.billTo?.address1,
|
|
81
|
+
address2: cart.billTo?.address2,
|
|
82
|
+
address3: cart.billTo?.address3,
|
|
83
|
+
city: cart.billTo?.city,
|
|
84
|
+
companyName: cart.billTo?.companyName,
|
|
85
|
+
country: cart.billTo?.country?.name,
|
|
86
|
+
email: cart.billTo?.email,
|
|
87
|
+
phone: cart.billTo?.phone,
|
|
88
|
+
postalCode: cart.billTo?.postalCode,
|
|
89
|
+
}, shipToAddress: {
|
|
90
|
+
address1: cart.shipTo?.address1,
|
|
91
|
+
address2: cart.shipTo?.address2,
|
|
92
|
+
address3: cart.shipTo?.address3,
|
|
93
|
+
city: cart.shipTo?.city,
|
|
94
|
+
companyName: cart.shipTo?.companyName,
|
|
95
|
+
country: cart.shipTo?.country?.name,
|
|
96
|
+
email: cart.shipTo?.email,
|
|
97
|
+
phone: cart.shipTo?.phone,
|
|
98
|
+
postalCode: cart.shipTo?.postalCode,
|
|
99
|
+
} }) }) }), jsx(CheckoutPageSection, { hasBorder: false, title: t('Order'), children: jsx(CheckoutPageSectionContent, { stretch: true, children: jsx(OrderLineList, { children: cart.cartLines.map(cartLine => (jsx(OrderLineCard, { deliveryDate: cartLine.atp?.date, href: cartLine.productUri, image: {
|
|
100
|
+
fit: 'contain',
|
|
101
|
+
image: {
|
|
102
|
+
'1': cartLine.smallImagePath,
|
|
103
|
+
'2': cartLine.smallImagePath,
|
|
104
|
+
'3': cartLine.smallImagePath,
|
|
105
|
+
altText: cartLine.altText,
|
|
106
|
+
},
|
|
107
|
+
title: cartLine.altText,
|
|
108
|
+
}, isReadonly: true, price: {
|
|
109
|
+
originalTotalPrice: cartLine.pricing?.extendedUnitNetPrice || 0,
|
|
110
|
+
pricePerUnit: cartLine.pricing?.unitNetPrice || 0,
|
|
111
|
+
totalPrice: cartLine.pricing?.extendedUnitNetPrice || 0,
|
|
112
|
+
}, productId: cartLine.productId || '', quantity: cartLine.qtyOrdered || 1, sku: cartLine.erpNumber || '', tags: [], title: cartLine.productName }, cartLine.id))) }) }) })] }) }) }));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { OrderConfirmationPage };
|
|
@@ -3,11 +3,11 @@ import { useRef, useEffect } from 'react';
|
|
|
3
3
|
import AdyenCheckout from '@adyen/adyen-web';
|
|
4
4
|
import clsx from 'clsx';
|
|
5
5
|
import qs from 'query-string';
|
|
6
|
-
import { useCultureCode } from '
|
|
7
|
-
import { useCreateAdyenSession } from '
|
|
8
|
-
import { useFetchAdyenConfig } from '
|
|
9
|
-
import { getAdyenPaymentDetails, postAdyenPayment } from '
|
|
10
|
-
import { parseAmount } from '
|
|
6
|
+
import { useCultureCode } from '../../../../intl/use-culture-code.js';
|
|
7
|
+
import { useCreateAdyenSession } from '../../../../shared/api/storefront/hooks/payment/use-create-adyen-session.js';
|
|
8
|
+
import { useFetchAdyenConfig } from '../../../../shared/api/storefront/hooks/payment/use-fetch-adyen-config.js';
|
|
9
|
+
import { getAdyenPaymentDetails, postAdyenPayment } from '../../../../shared/api/storefront/services/cart-service.js';
|
|
10
|
+
import { parseAmount } from '../utils/parse-amount.js';
|
|
11
11
|
import styles from './adyen-payment.module.css.js';
|
|
12
12
|
|
|
13
13
|
function AdyenPayment({ amount, cartId, countryCode, currencyCode, customerId, dropinRef, environment, onComplete, onError, orderAmount, returnUrl, }) {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { AdyenPaymentModel, CartModel, CheckoutAtpEntry } from '
|
|
1
|
+
import { AdyenPaymentModel, CartModel, CheckoutAtpEntry } from '../../../../shared/api/storefront/model/storefront.model';
|
|
2
2
|
export declare function Payment({ atp, cart: _cart, form, onError: _onError, onPaymentComplete, onProcessing, onValidating, }: {
|
|
3
3
|
atp: CheckoutAtpEntry[];
|
|
4
4
|
cart: CartModel;
|
|
5
5
|
form: string;
|
|
6
6
|
onError?: (error: unknown, result: AdyenPaymentModel | null) => void;
|
|
7
|
-
onPaymentComplete: (
|
|
7
|
+
onPaymentComplete: ({ cartId }: {
|
|
8
|
+
cartId: string;
|
|
9
|
+
}) => void;
|
|
8
10
|
onProcessing: (processing: boolean) => void;
|
|
9
11
|
onValidating: (validating: boolean) => void;
|
|
10
12
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useRef, useState, useMemo, useEffect, useCallback } from 'react';
|
|
3
3
|
import { Form } from 'react-aria-components';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { Select } from '
|
|
10
|
-
import { TextField } from '
|
|
11
|
-
import { FormattedMessage } from '
|
|
12
|
-
import { useFormattedMessage } from '
|
|
13
|
-
import { usePatchSession } from '
|
|
14
|
-
import { usePatchCart } from '
|
|
15
|
-
import { usePlaceOrder } from '
|
|
16
|
-
import { useInvalidateAdyen } from '
|
|
17
|
-
import { validateVATNumber } from '
|
|
18
|
-
import { useDebouncedCallback } from '
|
|
19
|
-
import { currencySymbolToISO } from '
|
|
20
|
-
import { Tooltip } from '
|
|
4
|
+
import { Accordion } from '../../../../collapsables/accordion/accordion.js';
|
|
5
|
+
import { AccordionItem } from '../../../../collapsables/accordion/accordion-item.js';
|
|
6
|
+
import { config } from '../../../../config.js';
|
|
7
|
+
import { InfoDisplay } from '../../../../display/info-display/info-display.js';
|
|
8
|
+
import { Checkbox } from '../../../../forms/checkbox/checkbox.js';
|
|
9
|
+
import { Select } from '../../../../forms/select/select.js';
|
|
10
|
+
import { TextField } from '../../../../forms/text-field/text-field.js';
|
|
11
|
+
import { FormattedMessage } from '../../../../intl/formatted-message.js';
|
|
12
|
+
import { useFormattedMessage } from '../../../../intl/use-formatted-message.js';
|
|
13
|
+
import { usePatchSession } from '../../../../shared/api/storefront/hooks/authentication/use-patch-session.js';
|
|
14
|
+
import { usePatchCart } from '../../../../shared/api/storefront/hooks/cart/use-patch-cart.js';
|
|
15
|
+
import { usePlaceOrder } from '../../../../shared/api/storefront/hooks/cart/use-place-order.js';
|
|
16
|
+
import { useInvalidateAdyen } from '../../../../shared/api/storefront/hooks/payment/use-invalidate-adyen.js';
|
|
17
|
+
import { validateVATNumber } from '../../../../shared/api/storefront/services/finance-service.js';
|
|
18
|
+
import { useDebouncedCallback } from '../../../../shared/hooks/use-debounced-callback.js';
|
|
19
|
+
import { currencySymbolToISO } from '../../../../shared/model/currency.js';
|
|
20
|
+
import { Tooltip } from '../../../../tooltip/tooltip.js';
|
|
21
|
+
import { BillingAndInvoiceInformation } from '../../components/billing-and-invoice-information.js';
|
|
22
|
+
import { useHasReturnedFromAdyen } from '../hooks/use-has-returned-from-adyen.js';
|
|
21
23
|
import { AdyenPayment } from './adyen-payment.js';
|
|
22
|
-
import { useHasReturnedFromAdyen } from './use-has-returned-from-adyen.js';
|
|
23
24
|
import styles from './payment.module.css.js';
|
|
24
25
|
|
|
25
26
|
function Payment({ atp, cart: _cart, form, onError: _onError, onPaymentComplete, onProcessing, onValidating, }) {
|
|
@@ -34,11 +35,16 @@ function Payment({ atp, cart: _cart, form, onError: _onError, onPaymentComplete,
|
|
|
34
35
|
const t = useFormattedMessage();
|
|
35
36
|
const cartRef = useRef(_cart);
|
|
36
37
|
const cart = cartRef.current;
|
|
37
|
-
const hasAtp = atp.length >
|
|
38
|
+
const hasAtp = atp.length > 1;
|
|
38
39
|
const [asSoonAsPossible, setAsSoonAsPossible] = useState(!hasAtp);
|
|
39
40
|
const [deliveryDate, setDeliveryDate] = useState(cart.requestedDeliveryDateDisplay?.toString() || '');
|
|
40
|
-
const
|
|
41
|
+
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState(cart.paymentOptions?.paymentMethods?.[0]?.name || 'ADY');
|
|
41
42
|
const countryCode = _cart.billTo?.country?.abbreviation;
|
|
43
|
+
const isAdyenPayment = selectedPaymentMethod === 'ADY' &&
|
|
44
|
+
cart.paymentOptions &&
|
|
45
|
+
cart.billTo?.id &&
|
|
46
|
+
countryCode;
|
|
47
|
+
const hasReturnedFromAdyen = useHasReturnedFromAdyen();
|
|
42
48
|
const isProcessing = useMemo(() => hasReturnedFromAdyen || isPlacingCart, [isPlacingCart, hasReturnedFromAdyen]);
|
|
43
49
|
useEffect(() => {
|
|
44
50
|
cartRef.current = _cart;
|
|
@@ -53,7 +59,9 @@ function Payment({ atp, cart: _cart, form, onError: _onError, onPaymentComplete,
|
|
|
53
59
|
onProcessing(isProcessing);
|
|
54
60
|
}, [isProcessing, onProcessing]);
|
|
55
61
|
const [validationErrors, setValidationErrors] = useState({});
|
|
56
|
-
const atpSelectOptions = atp
|
|
62
|
+
const atpSelectOptions = atp
|
|
63
|
+
.filter(atp => Boolean(atp.date))
|
|
64
|
+
.reduce((acc, atp) => ({
|
|
57
65
|
...acc,
|
|
58
66
|
[atp.date || '']: atp.displayDate,
|
|
59
67
|
}), {});
|
|
@@ -96,10 +104,6 @@ function Payment({ atp, cart: _cart, form, onError: _onError, onPaymentComplete,
|
|
|
96
104
|
setIsValidatingVAT(false);
|
|
97
105
|
}
|
|
98
106
|
}, 500);
|
|
99
|
-
const isAdyenPayment = cart.paymentMethod?.name !== 'PBI' &&
|
|
100
|
-
cart.paymentOptions &&
|
|
101
|
-
cart.billTo?.id &&
|
|
102
|
-
countryCode;
|
|
103
107
|
async function onSubmit(e) {
|
|
104
108
|
const formData = new FormData(e.currentTarget);
|
|
105
109
|
const cart = cartRef.current;
|
|
@@ -115,18 +119,19 @@ function Payment({ atp, cart: _cart, form, onError: _onError, onPaymentComplete,
|
|
|
115
119
|
dropinRef.current.showValidation();
|
|
116
120
|
if (!dropinRef.current.isValid)
|
|
117
121
|
return;
|
|
122
|
+
const updatedCart = {
|
|
123
|
+
...cart,
|
|
124
|
+
customerVatNumber: formData.get('vatNumber')?.toString(),
|
|
125
|
+
poNumber: formData.get('poNumber')?.toString(),
|
|
126
|
+
properties: {
|
|
127
|
+
...cart.properties,
|
|
128
|
+
industry: formData.get('industry')?.toString() || '',
|
|
129
|
+
},
|
|
130
|
+
requestedDeliveryDate: formData.get('deliveryDate')?.toString(),
|
|
131
|
+
};
|
|
118
132
|
try {
|
|
119
|
-
await patchCart({
|
|
120
|
-
cart:
|
|
121
|
-
...cart,
|
|
122
|
-
customerVatNumber: formData.get('vatNumber')?.toString(),
|
|
123
|
-
poNumber: formData.get('poNumber')?.toString(),
|
|
124
|
-
properties: {
|
|
125
|
-
...cart.properties,
|
|
126
|
-
industry: formData.get('industry')?.toString() || '',
|
|
127
|
-
},
|
|
128
|
-
requestedDeliveryDate: formData.get('deliveryDate')?.toString(),
|
|
129
|
-
},
|
|
133
|
+
cartRef.current = await patchCart({
|
|
134
|
+
cart: updatedCart,
|
|
130
135
|
});
|
|
131
136
|
}
|
|
132
137
|
catch (error) {
|
|
@@ -146,15 +151,17 @@ function Payment({ atp, cart: _cart, form, onError: _onError, onPaymentComplete,
|
|
|
146
151
|
const cart = cartRef.current;
|
|
147
152
|
try {
|
|
148
153
|
await placeOrder({ cart });
|
|
154
|
+
return onPaymentComplete({ cartId: cart.trackId });
|
|
149
155
|
}
|
|
150
156
|
catch (error) {
|
|
151
157
|
console.error(error);
|
|
152
|
-
|
|
158
|
+
setPaymentError(error);
|
|
153
159
|
}
|
|
154
|
-
return onPaymentComplete();
|
|
155
160
|
}
|
|
156
161
|
}
|
|
157
|
-
const onComplete = useCallback(
|
|
162
|
+
const onComplete = useCallback(
|
|
163
|
+
/* Adyen payment continuation after submit */
|
|
164
|
+
async (result) => {
|
|
158
165
|
const cart = cartRef.current;
|
|
159
166
|
try {
|
|
160
167
|
await placeOrder({
|
|
@@ -168,12 +175,13 @@ function Payment({ atp, cart: _cart, form, onError: _onError, onPaymentComplete,
|
|
|
168
175
|
},
|
|
169
176
|
},
|
|
170
177
|
});
|
|
178
|
+
return onPaymentComplete({ cartId: cart.trackId });
|
|
171
179
|
}
|
|
172
180
|
catch (error) {
|
|
173
181
|
console.error(error);
|
|
182
|
+
// TODO: Errors from the placeOrder call are not displayed
|
|
174
183
|
setAPIError(error);
|
|
175
184
|
}
|
|
176
|
-
return onPaymentComplete();
|
|
177
185
|
}, [onPaymentComplete, placeOrder]);
|
|
178
186
|
const onError = useCallback((error, result) => {
|
|
179
187
|
invalidateAdyen();
|
|
@@ -201,13 +209,35 @@ function Payment({ atp, cart: _cart, form, onError: _onError, onPaymentComplete,
|
|
|
201
209
|
MA: 'Maritime',
|
|
202
210
|
OT: 'Other',
|
|
203
211
|
/* eslint-enable sort-keys-fix/sort-keys-fix */
|
|
204
|
-
}, variant: "solid" }), jsx(TextField, { showLabel: true, defaultValue: cart.customerVatNumber, isDisabled: hasReturnedFromAdyen || isProcessing, label: t('VAT Number'), name: "vatNumber", onInput: e => debouncedValidator(e.target.value), validate: () => validationErrors.vatNumber ?? true }), jsx(TextField, { showLabel: true, defaultValue: cart.poNumber, isDisabled: hasReturnedFromAdyen || isProcessing, label: t('PO Number'), name: "poNumber" }), paymentMethodOptions && Object.keys(paymentMethodOptions).length > 1 && (jsx(Select, { "data-test-selector": "paymentMethodSelect", defaultSelectedOption: cart.paymentOptions?.paymentMethods?.[0]?.name || 'ADY', isDisabled: (hasReturnedFromAdyen ||
|
|
212
|
+
}, variant: "solid" }), jsx(TextField, { showLabel: true, defaultValue: cart.customerVatNumber, isDisabled: hasReturnedFromAdyen || isProcessing, label: t('VAT Number'), name: "vatNumber", onInput: e => debouncedValidator(e.target.value), validate: () => validationErrors.vatNumber ?? true }), jsx(TextField, { showLabel: true, defaultValue: cart.poNumber, isDisabled: hasReturnedFromAdyen || isProcessing, isRequired: cart.requiresPoNumber, label: t('PO Number'), name: "poNumber" }), paymentMethodOptions && Object.keys(paymentMethodOptions).length > 1 && (jsx(Select, { "data-test-selector": "paymentMethodSelect", defaultSelectedOption: cart.paymentOptions?.paymentMethods?.[0]?.name || 'ADY', isDisabled: (hasReturnedFromAdyen ||
|
|
205
213
|
(cart.paymentOptions?.paymentMethods &&
|
|
206
214
|
cart.paymentOptions.paymentMethods.length <= 1)) ??
|
|
207
|
-
true, label: t('Payment method'), name: "paymentMethod", options: paymentMethodOptions, variant: "solid" })), isAdyenPayment && cart.billTo && (jsx(AdyenPayment, { amount: cart.orderGrandTotal, cartId: cart.trackId, countryCode: countryCode, currencyCode: currencySymbolToISO[cart.currencySymbol], customerId: cart.billTo.id, dropinRef: dropinRef,
|
|
215
|
+
true, label: t('Payment method'), name: "paymentMethod", onChange: setSelectedPaymentMethod, options: paymentMethodOptions, selectedOption: selectedPaymentMethod, variant: "solid" })), isAdyenPayment && cart.billTo && (jsx(AdyenPayment, { amount: cart.orderGrandTotal, cartId: cart.trackId, countryCode: countryCode, currencyCode: currencySymbolToISO[cart.currencySymbol], customerId: cart.billTo.id, dropinRef: dropinRef,
|
|
216
|
+
// TODO: Use the correct Adyen environment before going live
|
|
217
|
+
environment: "test", onComplete: onComplete, onError: onError, orderAmount: cart.orderGrandTotal, returnUrl: typeof window === 'undefined'
|
|
208
218
|
? ''
|
|
209
|
-
:
|
|
210
|
-
|
|
219
|
+
: /* eslint-disable ssr-friendly/no-dom-globals-in-react-fc */
|
|
220
|
+
`${config.SHOP_API_URL}${window.location.pathname}${window.location.search ? `${window.location.search}` : ''}` })), Boolean(paymentError) && (jsx("div", { className: styles['error-message'], children: jsx(FormattedMessage, { id: "An error occurred while processing your payment. Please try again." }) })), jsx(InfoDisplay, { id: "shipping-and-invoice-information", label: t('Billing and shipping address'), value: jsx(Accordion, { variant: "select", children: jsx(AccordionItem, { id: "invoice-and-shipping", title: "Factuur- en verzendinformatie", children: jsx(BillingAndInvoiceInformation, { billToAddress: {
|
|
221
|
+
address1: cart.billTo?.address1,
|
|
222
|
+
address2: cart.billTo?.address2,
|
|
223
|
+
address3: cart.billTo?.address3,
|
|
224
|
+
city: cart.billTo?.city,
|
|
225
|
+
companyName: cart.billTo?.companyName,
|
|
226
|
+
country: cart.billTo?.country?.name,
|
|
227
|
+
email: cart.billTo?.email,
|
|
228
|
+
phone: cart.billTo?.phone,
|
|
229
|
+
postalCode: cart.billTo?.postalCode,
|
|
230
|
+
}, shipToAddress: {
|
|
231
|
+
address1: cart.shipTo?.address1,
|
|
232
|
+
address2: cart.shipTo?.address2,
|
|
233
|
+
address3: cart.shipTo?.address3,
|
|
234
|
+
city: cart.shipTo?.city,
|
|
235
|
+
companyName: cart.shipTo?.companyName,
|
|
236
|
+
country: cart.shipTo?.country?.name,
|
|
237
|
+
email: cart.shipTo?.email,
|
|
238
|
+
phone: cart.shipTo?.phone,
|
|
239
|
+
postalCode: cart.shipTo?.postalCode,
|
|
240
|
+
} }) }) }) })] }));
|
|
211
241
|
}
|
|
212
242
|
|
|
213
243
|
export { Payment };
|
|
@@ -4,8 +4,6 @@ import { Button } from '../../../buttons/button/button.js';
|
|
|
4
4
|
import { OrderLineCard } from '../../../cards/orderline-card/orderline-card.js';
|
|
5
5
|
import { CartTotals } from '../../../cart-totals/cart-totals.js';
|
|
6
6
|
import { CartTotalsSummary } from '../../../cart-totals/cart-totals-summary.js';
|
|
7
|
-
import { Payment } from '../../../checkout/payment.js';
|
|
8
|
-
import { useHasReturnedFromAdyen } from '../../../checkout/use-has-returned-from-adyen.js';
|
|
9
7
|
import { FormattedMessage } from '../../../intl/formatted-message.js';
|
|
10
8
|
import { useFormattedMessage } from '../../../intl/use-formatted-message.js';
|
|
11
9
|
import { OrderLineList } from '../../../lists/orderline-list/orderline-list.js';
|
|
@@ -17,9 +15,12 @@ import { hasNo } from '../../../shared/utils/types.js';
|
|
|
17
15
|
import { Page } from '../../components/page/page.js';
|
|
18
16
|
import { ErrorPage } from '../../error-page/error-page.js';
|
|
19
17
|
import { LoadingPage } from '../../loading-page/loading-page.js';
|
|
18
|
+
import { CHECKOUT_PATHS } from '../constants.js';
|
|
20
19
|
import { CheckoutPageLayout } from '../layouts/checkout-page-layout/checkout-page-layout.js';
|
|
21
20
|
import { CheckoutPageSection } from '../layouts/checkout-page-layout/components/checkout-page-section.js';
|
|
22
21
|
import { CheckoutPageSectionContent } from '../layouts/checkout-page-layout/components/checkout-page-section-content.js';
|
|
22
|
+
import { Payment } from './components/payment.js';
|
|
23
|
+
import { useHasReturnedFromAdyen } from './hooks/use-has-returned-from-adyen.js';
|
|
23
24
|
|
|
24
25
|
const PAYMENT_FORM_ID = 'paymentForm';
|
|
25
26
|
function PaymentPage() {
|
|
@@ -29,28 +30,30 @@ function PaymentPage() {
|
|
|
29
30
|
forceRecalculation: !hasReturnedFromAdyen,
|
|
30
31
|
});
|
|
31
32
|
const { data: atp, isLoading: isAtpLoading } = useFetchCurrentCheckoutAtp();
|
|
32
|
-
const hasAtp = atp && atp.length >
|
|
33
|
+
const hasAtp = atp && atp.length > 1;
|
|
33
34
|
const isLoading = isLoadingCart || isAtpLoading;
|
|
34
35
|
const t = useFormattedMessage();
|
|
35
36
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
36
37
|
const [isValidating, setIsValidating] = useState(false);
|
|
37
38
|
const { isNavigating, navigate } = useNavigate();
|
|
38
|
-
const onPaymentComplete = useCallback(() =>
|
|
39
|
+
const onPaymentComplete = useCallback(({ cartId }) => {
|
|
40
|
+
navigate(`${CHECKOUT_PATHS.ORDER_CONFIRMATION}?cartId=${cartId}`);
|
|
41
|
+
}, [navigate]);
|
|
39
42
|
useEffect(() => {
|
|
40
43
|
if (isNavigating)
|
|
41
44
|
return;
|
|
42
45
|
if (isAuthenticated === undefined)
|
|
43
46
|
return;
|
|
44
47
|
if (!isAuthenticated)
|
|
45
|
-
return navigate(
|
|
48
|
+
return navigate(CHECKOUT_PATHS.CHECKOUT_SHIPPING_VIA_SIGNIN);
|
|
46
49
|
if (hasNo(cart))
|
|
47
50
|
return;
|
|
48
51
|
if (hasNo(cart.cartLines) || cart.cartLines.length === 0)
|
|
49
|
-
return navigate(
|
|
52
|
+
return navigate(CHECKOUT_PATHS.CART);
|
|
50
53
|
if (hasNo(cart.billTo))
|
|
51
|
-
return navigate(
|
|
54
|
+
return navigate(CHECKOUT_PATHS.CHECKOUT_SHIPPING_VIA_SIGNIN);
|
|
52
55
|
if (hasNo(cart.billTo.address1))
|
|
53
|
-
return navigate(
|
|
56
|
+
return navigate(CHECKOUT_PATHS.CHECKOUT_SHIPPING);
|
|
54
57
|
}, [cart, navigate, isAuthenticated, isNavigating]);
|
|
55
58
|
if (error)
|
|
56
59
|
return jsx(ErrorPage, { error: error });
|
|
@@ -64,8 +67,11 @@ function PaymentPage() {
|
|
|
64
67
|
hasNo(cart.billTo?.address1))
|
|
65
68
|
return null;
|
|
66
69
|
return (jsx(Page, { breadCrumb: [
|
|
67
|
-
{ href:
|
|
68
|
-
{
|
|
70
|
+
{ href: CHECKOUT_PATHS.HOME, label: t('Home') },
|
|
71
|
+
{
|
|
72
|
+
href: CHECKOUT_PATHS.REVIEW_AND_SUBMIT,
|
|
73
|
+
label: t('Review and payment'),
|
|
74
|
+
},
|
|
69
75
|
], title: t('Review and payment'), children: jsxs(CheckoutPageLayout, { actions: {
|
|
70
76
|
primary: (jsx(Button, { withArrow: true, "data-test-selector": "checkoutShippingCartTotalContinueButton", form: PAYMENT_FORM_ID, isDisabled: isProcessing, isLoading: isProcessing ? (jsx(FormattedMessage, { id: "Processing" })) : isValidating ? (jsx(FormattedMessage, { id: "Validating" })) : (false), type: "submit", children: jsx(FormattedMessage, { id: "Pay" }) })),
|
|
71
77
|
}, mobileSummary: jsx(CartTotalsSummary, { totalAmount: cart.orderGrandTotalDisplay }), overview: jsx(CartTotals, { deliveryDate: hasAtp ? undefined : cart.requestedDeliveryDate, fulfillmentMethod: cart.fulfillmentMethod, isPayByInvoice: (cart.paymentOptions?.paymentMethods?.length || 1) <= 1 &&
|
|
@@ -6,6 +6,7 @@ import { Checkbox } from '../../../../forms/checkbox/checkbox.js';
|
|
|
6
6
|
import { TextField } from '../../../../forms/text-field/text-field.js';
|
|
7
7
|
import { FormattedMessage } from '../../../../intl/formatted-message.js';
|
|
8
8
|
import { useFormattedMessage } from '../../../../intl/use-formatted-message.js';
|
|
9
|
+
import { useFetchCurrentCart } from '../../../../shared/api/storefront/hooks/cart/use-fetch-current-cart.js';
|
|
9
10
|
import { validatePhone, validateEmail } from '../../../../shared/model/address.js';
|
|
10
11
|
import { Tooltip } from '../../../../tooltip/tooltip.js';
|
|
11
12
|
import { CheckoutPageSection } from '../../layouts/checkout-page-layout/components/checkout-page-section.js';
|
|
@@ -18,10 +19,11 @@ function EditAddresses({ countries, isLoading, isPickup, onSubmit, }) {
|
|
|
18
19
|
const t = useFormattedMessage();
|
|
19
20
|
const [companyName, setCompanyName] = useState('');
|
|
20
21
|
const [lastName, setLastName] = useState('');
|
|
22
|
+
const { data: cart } = useFetchCurrentCart();
|
|
21
23
|
return (jsxs(Fragment, { children: [jsx(CheckoutPageSection, { title: jsx(FormattedMessage, { id: "Billing address" }), children: jsx(CheckoutPageSectionContent, { children: jsxs(Form, { className: styles.form, "data-test-selector": "billToAddressForm", id: EDIT_ADDRESS_FORM_ID, onSubmit: e => {
|
|
22
24
|
e.preventDefault();
|
|
23
25
|
const formData = new FormData(e.currentTarget);
|
|
24
|
-
const countryFormValue = formData.get('
|
|
26
|
+
const countryFormValue = formData.get('countrySelect')?.toString();
|
|
25
27
|
const country = countries.find(country => country.id === countryFormValue);
|
|
26
28
|
if (!country)
|
|
27
29
|
throw new Error(`Country not found ${formData.get('country')}`);
|
|
@@ -44,11 +46,13 @@ function EditAddresses({ countries, isLoading, isPickup, onSubmit, }) {
|
|
|
44
46
|
}, children: [jsx(TextField, { isDisabled: isLoading, label: t('First name'), name: "firstName", showLabel: true }), jsx(TextField, { isDisabled: isLoading, isRequired: !companyName, label: t('Last name'), minLength: 3, name: "lastName", onChange: setLastName, showLabel: true, value: lastName }, `lastname-${Boolean(companyName)}`), jsx(TextField, { isDisabled: isLoading, label: t('Company name'), name: "companyName", onChange: setCompanyName, showLabel: true, value: companyName }), jsx(TextField, { isDisabled: isLoading, label: t('Attention'), name: "attention", showLabel: true }), jsx("div", { className: styles['span-2'], children: jsx(TextField, { isRequired: true, isDisabled: isLoading, label: `${t('Address')} 1`, maxLength: 30, minLength: 3, name: "address1", showLabel: true }) }), jsx("div", { className: styles['span-2'], children: jsx(TextField, { isDisabled: isLoading, label: `${t('Address')} 2`, maxLength: 30, minLength: 3, name: "address2", showLabel: true }) }), jsx("div", { className: styles['span-2'], children: jsx(TextField, { isDisabled: isLoading, label: `${t('Address')} 3`, maxLength: 30, minLength: 3, name: "address3", showLabel: true }) }), jsx(TextField, { isRequired: true, isDisabled: isLoading, label: t('Postal Code'), maxLength: 10, minLength: 4, name: "postalCode", showLabel: true }), jsx(TextField, { isRequired: true, isDisabled: isLoading, label: t('City'), maxLength: 30, minLength: 3, name: "city", showLabel: true }), jsx("div", { className: styles['span-2'], children: jsx(CountrySelect, { isRequired: true, countries: countries, "data-test-selector": "countrySelect", name: "countrySelect" }) }), jsx("div", { className: styles['span-2'], children: jsx(TextField, { isRequired: true, isDisabled: isLoading, label: t('Phone'), name: "phone", showLabel: true, validate: value => {
|
|
45
47
|
if (!value)
|
|
46
48
|
return value;
|
|
47
|
-
return (validatePhone(value) ||
|
|
48
|
-
|
|
49
|
+
return (validatePhone(value) ||
|
|
50
|
+
t('Please enter a valid phone number'));
|
|
51
|
+
} }) }), jsx("div", { className: styles['span-2'], children: jsx(TextField, { isRequired: true, defaultValue: cart?.billTo?.email || '', isDisabled: isLoading, label: t('Email'), name: "email", showLabel: true, type: "email", validate: value => {
|
|
49
52
|
if (!value)
|
|
50
53
|
return value;
|
|
51
|
-
return validateEmail(value) ||
|
|
54
|
+
return (validateEmail(value) ||
|
|
55
|
+
t('Please enter a valid e-mail address'));
|
|
52
56
|
} }) }), jsx("div", { className: styles['span-2'], children: jsx(TextField, { isDisabled: isLoading, isMultiline: true, label: t('Add order notes'), name: "notes", rows: 3, showLabel: true }) })] }) }) }), jsx(CheckoutPageSection, { title: jsx(FormattedMessage, { id: isPickup ? 'Pickup address' : 'Shipping address' }), children: jsx(CheckoutPageSectionContent, { children: jsx(Fragment, { children: isPickup ? (jsx(SonicAddress, {})) : (jsxs("div", { className: styles['use-invoice-checkbox'], children: [jsx(Checkbox, { "data-test-selector": "checkboxUseBillingAddress", isDisabled: true, isSelected: true, children: jsx(FormattedMessage, { id: "Use billing address" }) }), jsx(Tooltip, { text: t('Changing your address is currently not possible. Please contact customer support to change your address.') })] })) }) }) })] }));
|
|
53
57
|
}
|
|
54
58
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BillToModel, CartModel } from '../../../../shared/api/storefront/model/storefront.model';
|
|
2
|
+
export declare function usePatchShippingDetails(): {
|
|
3
|
+
error: unknown;
|
|
4
|
+
isError: boolean;
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
isSuccess: boolean;
|
|
7
|
+
mutate: (args_0: {
|
|
8
|
+
billTo?: BillToModel;
|
|
9
|
+
cart: CartModel;
|
|
10
|
+
notes: string | undefined;
|
|
11
|
+
}) => Promise<void>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useAwaitableMutation } from '../../../../shared/api/shared/hooks/use-awaitable-mutation.js';
|
|
2
|
+
import { patchCart } from '../../../../shared/api/storefront/services/cart-service.js';
|
|
3
|
+
import { patchBillToAddress } from '../../../../shared/api/storefront/services/customer-service.js';
|
|
4
|
+
|
|
5
|
+
function usePatchShippingDetails() {
|
|
6
|
+
return useAwaitableMutation({
|
|
7
|
+
mutationFn: async ({ billTo, cart, notes, }) => {
|
|
8
|
+
await Promise.all([
|
|
9
|
+
billTo && patchBillToAddress({ billTo }),
|
|
10
|
+
patchCart({ cart: { ...cart, notes } }),
|
|
11
|
+
].filter(Boolean));
|
|
12
|
+
},
|
|
13
|
+
onSuccess: ({ queryClient }) => {
|
|
14
|
+
queryClient.removeQueries({ queryKey: ['customer'] });
|
|
15
|
+
queryClient.removeQueries({ queryKey: ['carts'] });
|
|
16
|
+
queryClient.removeQueries({ queryKey: ['session'] });
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { usePatchShippingDetails };
|