@sonic-equipment/ui 132.0.0 → 134.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 (130) hide show
  1. package/dist/address/address.d.ts +2 -13
  2. package/dist/algolia/{algolia-intialization.js → algolia-initialization.js} +7 -0
  3. package/dist/algolia/algolia-insights-provider.js +7 -0
  4. package/dist/algolia/algolia-search-provider.js +19 -0
  5. package/dist/algolia/use-algolia-insights.d.ts +13 -6
  6. package/dist/algolia/use-algolia-insights.js +93 -10
  7. package/dist/buttons/add-to-cart-button/connected-add-to-cart-button.js +1 -1
  8. package/dist/buttons/button/button.d.ts +4 -1
  9. package/dist/buttons/button/button.js +3 -2
  10. package/dist/country-selector/country-select/country-select.d.ts +1 -0
  11. package/dist/country-selector/country-select/country-select.js +2 -2
  12. package/dist/delivery-time/delivery-time.js +6 -5
  13. package/dist/display/info-display/info-display.d.ts +7 -0
  14. package/dist/display/info-display/info-display.js +8 -0
  15. package/dist/display/info-display/info-display.module.css.js +3 -0
  16. package/dist/exports.d.ts +20 -7
  17. package/dist/forms/checkbox/checkbox.d.ts +2 -1
  18. package/dist/forms/checkbox/checkbox.js +2 -2
  19. package/dist/forms/field-error/field-error.d.ts +2 -1
  20. package/dist/forms/field-error/field-error.js +3 -2
  21. package/dist/forms/input/input.d.ts +2 -0
  22. package/dist/forms/input/input.js +5 -3
  23. package/dist/forms/input/input.module.css.js +1 -1
  24. package/dist/forms/label/label.d.ts +3 -1
  25. package/dist/forms/label/label.js +3 -2
  26. package/dist/forms/switch/switch.d.ts +3 -1
  27. package/dist/forms/switch/switch.js +2 -2
  28. package/dist/forms/text-field/password-reveal-toggle/password-reveal-toggle.d.ts +10 -0
  29. package/dist/forms/text-field/password-reveal-toggle/password-reveal-toggle.js +18 -0
  30. package/dist/forms/text-field/password-reveal-toggle/password-reveal-toggle.module.css.js +3 -0
  31. package/dist/forms/text-field/text-field.d.ts +5 -3
  32. package/dist/forms/text-field/text-field.js +11 -3
  33. package/dist/forms/text-field/text-field.module.css.js +1 -1
  34. package/dist/forms/textarea/textarea.d.ts +1 -0
  35. package/dist/forms/textarea/textarea.js +3 -2
  36. package/dist/global-search/plugins/categories-plugin.js +2 -1
  37. package/dist/global-search/plugins/popular-categories-plugin.js +2 -0
  38. package/dist/global-search/plugins/query-suggestions-plugin.js +1 -0
  39. package/dist/global-search/plugins/quick-access-plugin.js +2 -2
  40. package/dist/global-search/search-result-panel/sections/with-results.js +0 -1
  41. package/dist/icons/stroke/stroke-dehashed-icon.js +7 -0
  42. package/dist/icons/stroke/stroke-hashed-icon.js +7 -0
  43. package/dist/index.js +22 -9
  44. package/dist/info-icon-tooltip/info-icon-tooltip.d.ts +7 -0
  45. package/dist/info-icon-tooltip/info-icon-tooltip.js +20 -0
  46. package/dist/info-icon-tooltip/info-icon-tooltip.module.css.js +3 -0
  47. package/dist/intl/translation-id.d.ts +1 -1
  48. package/dist/pages/checkout/cart-page/cart-page.js +6 -9
  49. package/dist/pages/checkout/cart-page/components/empty-cart-page.d.ts +1 -0
  50. package/dist/pages/checkout/cart-page/components/empty-cart-page.js +11 -0
  51. package/dist/pages/checkout/components/billing-and-invoice-information.d.ts +7 -0
  52. package/dist/pages/checkout/components/billing-and-invoice-information.js +12 -0
  53. package/dist/pages/checkout/components/billing-and-invoice-information.module.css.js +3 -0
  54. package/dist/pages/checkout/constants.d.ts +8 -0
  55. package/dist/pages/checkout/constants.js +10 -0
  56. package/dist/pages/checkout/layouts/checkout-page-layout/components/checkout-page-section.module.css.js +1 -1
  57. package/dist/pages/checkout/order-confirmation-page/order-confirmation-page-content.d.ts +5 -0
  58. package/dist/pages/checkout/order-confirmation-page/order-confirmation-page-content.js +83 -0
  59. package/dist/pages/checkout/order-confirmation-page/order-confirmation-page.d.ts +4 -0
  60. package/dist/pages/checkout/order-confirmation-page/order-confirmation-page.js +41 -0
  61. package/dist/pages/checkout/order-confirmation-page/order-confirmation-page.module.css.js +3 -0
  62. package/dist/{checkout → pages/checkout/payment-page/components}/adyen-payment.js +5 -5
  63. package/dist/pages/checkout/payment-page/components/adyen-payment.module.css.js +3 -0
  64. package/dist/{checkout → pages/checkout/payment-page/components}/payment.d.ts +4 -2
  65. package/dist/pages/checkout/payment-page/components/payment.js +288 -0
  66. package/dist/pages/checkout/payment-page/components/payment.module.css.js +3 -0
  67. package/dist/pages/checkout/payment-page/payment-page-content.d.ts +15 -0
  68. package/dist/pages/checkout/payment-page/payment-page-content.js +43 -0
  69. package/dist/pages/checkout/payment-page/payment-page.js +13 -41
  70. package/dist/{checkout → pages/checkout/payment-page/utils}/parse-amount.js +1 -1
  71. package/dist/pages/checkout/shipping-page/components/edit-address.js +11 -7
  72. package/dist/pages/checkout/shipping-page/hooks/use-patch-shipping-details.d.ts +12 -0
  73. package/dist/pages/checkout/shipping-page/hooks/use-patch-shipping-details.js +21 -0
  74. package/dist/pages/checkout/shipping-page/shipping-page-content.d.ts +14 -0
  75. package/dist/pages/checkout/shipping-page/shipping-page-content.js +40 -0
  76. package/dist/pages/checkout/shipping-page/shipping-page.js +55 -78
  77. package/dist/pages/checkout/shipping-page/shipping-page.module.css.js +1 -1
  78. package/dist/pages/components/page/page.d.ts +3 -2
  79. package/dist/pages/components/page/page.js +3 -2
  80. package/dist/pages/components/page-meta-data/page-meta-data.d.ts +3 -4
  81. package/dist/pages/components/page-meta-data/page-meta-data.js +2 -2
  82. package/dist/pages/product/product-details-page/components/product-details-panel/product-details-panel.js +11 -1
  83. package/dist/pages/product/product-details-page/product-details-page.js +2 -10
  84. package/dist/pages/product/product-details-page/product-details.js +10 -4
  85. package/dist/pages/product/product-listing-page/product-listing-page-data-types.d.ts +1 -1
  86. package/dist/pages/product/product-listing-page/product-listing-page.js +2 -10
  87. package/dist/pages/product/product-listing-page/product-listing-product-overview/product-listing-product-overview.js +0 -1
  88. package/dist/pages/product/product-listing-page/product-listing.js +8 -1
  89. package/dist/pages/product/search-result-page/search-result-product-overview/search-result-product-overview.js +0 -1
  90. package/dist/pages/product/search-result-page/search-results-page.js +4 -7
  91. package/dist/shared/api/bff/model/bff.model.d.ts +4 -4
  92. package/dist/shared/api/bff/services/bff-service.js +1 -4
  93. package/dist/shared/api/storefront/hooks/cart/use-fetch-cart-by-id.d.ts +8 -0
  94. package/dist/shared/api/storefront/hooks/cart/use-fetch-cart-by-id.js +20 -0
  95. package/dist/shared/api/storefront/hooks/cart/use-fetch-current-cart-count.d.ts +1 -1
  96. package/dist/shared/api/storefront/hooks/cart/use-fetch-current-cart-count.js +3 -3
  97. package/dist/shared/api/storefront/hooks/cart/use-fetch-current-cart.d.ts +2 -5
  98. package/dist/shared/api/storefront/hooks/cart/use-fetch-current-cart.js +3 -16
  99. package/dist/shared/api/storefront/hooks/cart/use-patch-cart.js +11 -8
  100. package/dist/shared/api/storefront/hooks/cart/use-place-order.js +2 -2
  101. package/dist/shared/fetch/request.d.ts +18 -13
  102. package/dist/shared/fetch/request.js +22 -9
  103. package/dist/shared/ga/use-data-layer.d.ts +7 -3
  104. package/dist/shared/ga/use-data-layer.js +6 -1
  105. package/dist/shared/model/address.d.ts +13 -0
  106. package/dist/shared/providers/react-query-container.d.ts +2 -1
  107. package/dist/shared/providers/react-query-container.js +2 -2
  108. package/dist/shared/utils/date.d.ts +2 -0
  109. package/dist/shared/utils/date.js +11 -1
  110. package/dist/shared/utils/promise.d.ts +2 -0
  111. package/dist/shared/utils/promise.js +9 -0
  112. package/dist/sign-in-form/sign-in-form.d.ts +19 -0
  113. package/dist/sign-in-form/sign-in-form.js +49 -0
  114. package/dist/sign-in-form/sign-in-form.module.css.js +3 -0
  115. package/dist/styles.css +532 -314
  116. package/dist/tooltip/tooltip.d.ts +3 -2
  117. package/dist/tooltip/tooltip.js +12 -6
  118. package/package.json +1 -1
  119. package/dist/checkout/adyen-payment.module.css.js +0 -3
  120. package/dist/checkout/payment.js +0 -213
  121. package/dist/checkout/payment.module.css.js +0 -3
  122. package/dist/shared/utils/wait.d.ts +0 -1
  123. package/dist/shared/utils/wait.js +0 -5
  124. /package/dist/algolia/{algolia-intialization.d.ts → algolia-initialization.d.ts} +0 -0
  125. /package/dist/{checkout → pages/checkout/payment-page/components}/adyen-payment.d.ts +0 -0
  126. /package/dist/{checkout → pages/checkout/payment-page/hooks}/use-get-adyen-redirect-result.d.ts +0 -0
  127. /package/dist/{checkout → pages/checkout/payment-page/hooks}/use-get-adyen-redirect-result.js +0 -0
  128. /package/dist/{checkout → pages/checkout/payment-page/hooks}/use-has-returned-from-adyen.d.ts +0 -0
  129. /package/dist/{checkout → pages/checkout/payment-page/hooks}/use-has-returned-from-adyen.js +0 -0
  130. /package/dist/{checkout → pages/checkout/payment-page/utils}/parse-amount.d.ts +0 -0
@@ -4,10 +4,11 @@ import { Form } from 'react-aria-components';
4
4
  import { CountrySelect } from '../../../../country-selector/country-select/country-select.js';
5
5
  import { Checkbox } from '../../../../forms/checkbox/checkbox.js';
6
6
  import { TextField } from '../../../../forms/text-field/text-field.js';
7
+ import { InfoIconTooltip } from '../../../../info-icon-tooltip/info-icon-tooltip.js';
7
8
  import { FormattedMessage } from '../../../../intl/formatted-message.js';
8
9
  import { useFormattedMessage } from '../../../../intl/use-formatted-message.js';
10
+ import { useFetchCurrentCart } from '../../../../shared/api/storefront/hooks/cart/use-fetch-current-cart.js';
9
11
  import { validatePhone, validateEmail } from '../../../../shared/model/address.js';
10
- import { Tooltip } from '../../../../tooltip/tooltip.js';
11
12
  import { CheckoutPageSection } from '../../layouts/checkout-page-layout/components/checkout-page-section.js';
12
13
  import { CheckoutPageSectionContent } from '../../layouts/checkout-page-layout/components/checkout-page-section-content.js';
13
14
  import { SonicAddress } from './sonic-address.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('country')?.toString();
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')}`);
@@ -41,15 +43,17 @@ function EditAddresses({ countries, isLoading, isPickup, onSubmit, }) {
41
43
  },
42
44
  notes: formData.get('notes')?.toString() || '',
43
45
  });
44
- }, 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 => {
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", isDisabled: isLoading, 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) || 'Vul een geldig telefoonnummer in');
48
- } }) }), jsx("div", { className: styles['span-2'], children: jsx(TextField, { isRequired: true, isDisabled: isLoading, label: t('Email'), name: "email", showLabel: true, type: "email", validate: value => {
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) || 'Vul een geldig emailadres in';
52
- } }) }), 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.') })] })) }) }) })] }));
54
+ return (validateEmail(value) ||
55
+ t('Please enter a valid e-mail address'));
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(InfoIconTooltip, { variant: "stroke", children: t('Changing your address is currently not possible. Please contact customer support to change your address.') })] })) }) }) })] }));
53
57
  }
54
58
 
55
59
  export { EDIT_ADDRESS_FORM_ID, EditAddresses };
@@ -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 };
@@ -0,0 +1,14 @@
1
+ import { ReactNode } from 'react';
2
+ import { CartModel } from '../../../shared/api/storefront/model/storefront.model';
3
+ export interface ShippingPageContentProps {
4
+ cart: CartModel;
5
+ editAddress: ReactNode;
6
+ errorPatchBillingAddress?: unknown;
7
+ fulfillmentMethods: string[] | undefined;
8
+ isLoadingFulfillmentMethods: boolean;
9
+ isPatching: boolean;
10
+ isPatchingSession: boolean;
11
+ onChangeFulfillmentMethod: (value: string) => void;
12
+ readOnlyAddress: ReactNode;
13
+ }
14
+ export declare function ShippingPageContent({ cart, editAddress, errorPatchBillingAddress, fulfillmentMethods, isLoadingFulfillmentMethods, isPatching, isPatchingSession, onChangeFulfillmentMethod, readOnlyAddress, }: ShippingPageContentProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,40 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import { Button } from '../../../buttons/button/button.js';
4
+ import { CartTotals } from '../../../cart-totals/cart-totals.js';
5
+ import { CartTotalsSummary } from '../../../cart-totals/cart-totals-summary.js';
6
+ import { Select } from '../../../forms/select/select.js';
7
+ import { FormattedMessage } from '../../../intl/formatted-message.js';
8
+ import { useFormattedMessage } from '../../../intl/use-formatted-message.js';
9
+ import { Page } from '../../components/page/page.js';
10
+ import { CHECKOUT_PATHS } from '../constants.js';
11
+ import { CheckoutPageLayout } from '../layouts/checkout-page-layout/checkout-page-layout.js';
12
+ import { CheckoutPageSection } from '../layouts/checkout-page-layout/components/checkout-page-section.js';
13
+ import { CheckoutPageSectionContent } from '../layouts/checkout-page-layout/components/checkout-page-section-content.js';
14
+ import { EDIT_ADDRESS_FORM_ID } from './components/edit-address.js';
15
+ import styles from './shipping-page.module.css.js';
16
+
17
+ function ShippingPageContent({ cart, editAddress, errorPatchBillingAddress, fulfillmentMethods, isLoadingFulfillmentMethods, isPatching, isPatchingSession, onChangeFulfillmentMethod, readOnlyAddress, }) {
18
+ const t = useFormattedMessage();
19
+ const fulfillmentMethodOptions = fulfillmentMethods?.reduce((acc, method) => ({
20
+ ...acc,
21
+ [method]: t(`fulfillmentMethod.${method}`),
22
+ }), {});
23
+ const hasBillToAddress = useMemo(() => {
24
+ return Boolean(cart.billTo?.address1);
25
+ // eslint-disable-next-line react-hooks/exhaustive-deps
26
+ }, [Boolean(cart)]);
27
+ return (jsx(Page, { breadCrumb: [
28
+ { href: CHECKOUT_PATHS.HOME, label: t('Home') },
29
+ {
30
+ href: CHECKOUT_PATHS.CHECKOUT_SHIPPING,
31
+ label: t('Shipping details'),
32
+ },
33
+ ], title: t('Shipping details'), children: jsxs(CheckoutPageLayout, { actions: {
34
+ primary: (jsx(Button, { withArrow: true, "data-test-selector": "checkoutShippingCartTotalContinueButton", form: EDIT_ADDRESS_FORM_ID, isDisabled: isPatching, isLoading: isPatching || isPatchingSession ? (jsx(FormattedMessage, { id: "Updating address" })) : undefined, type: "submit", children: jsx(FormattedMessage, { id: "Continue shopping" }) })),
35
+ }, mobileSummary: jsx(CartTotalsSummary, { totalAmount: cart.orderGrandTotalDisplay }), overview: jsx(CartTotals, { fulfillmentMethod: fulfillmentMethods && fulfillmentMethods.length === 1
36
+ ? cart.fulfillmentMethod
37
+ : undefined, shippingCost: cart.shippingAndHandlingDisplay, subtotal: cart.orderSubTotalDisplay, tax: cart.totalTaxDisplay, total: cart.orderGrandTotalDisplay, vatPercentage: cart.cartLines?.[0]?.pricing?.vatRate }), children: [jsxs(Fragment, { children: [fulfillmentMethods && fulfillmentMethods.length > 1 && (jsx(CheckoutPageSection, { hasBorder: true, title: t('Fulfillment method'), children: jsx(CheckoutPageSectionContent, { children: jsx("div", { className: styles['fulfillment-select-wrapper'], children: jsx(Select, { isRequired: true, showLabel: true, "data-test-selector": "fulfillmentMethodSelect", defaultSelectedOption: cart.fulfillmentMethod, isLoading: isLoadingFulfillmentMethods, label: t('Fulfillment method'), name: "fulfillmentMethod", onChange: onChangeFulfillmentMethod, options: fulfillmentMethodOptions || {}, variant: "solid" }) }) }) })), hasBillToAddress ? readOnlyAddress : editAddress] }), Boolean(errorPatchBillingAddress) && (jsx("div", { className: styles['error-message'], children: jsx(FormattedMessage, { id: "An unexpected error occured" }) }))] }) }));
38
+ }
39
+
40
+ export { ShippingPageContent };
@@ -1,69 +1,36 @@
1
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { useMemo, useEffect } from 'react';
3
- import { Button } from '../../../buttons/button/button.js';
4
- import { CartTotals } from '../../../cart-totals/cart-totals.js';
5
- import { CartTotalsSummary } from '../../../cart-totals/cart-totals-summary.js';
6
- import { Select } from '../../../forms/select/select.js';
7
- import { FormattedMessage } from '../../../intl/formatted-message.js';
8
- import { useFormattedMessage } from '../../../intl/use-formatted-message.js';
9
- import { useAwaitableMutation } from '../../../shared/api/shared/hooks/use-awaitable-mutation.js';
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useRef, useEffect } from 'react';
10
3
  import { useFetchSession } from '../../../shared/api/storefront/hooks/authentication/use-fetch-session.js';
11
4
  import { useIsAuthenticated } from '../../../shared/api/storefront/hooks/authentication/use-is-authenticated.js';
12
5
  import { usePatchSession } from '../../../shared/api/storefront/hooks/authentication/use-patch-session.js';
13
6
  import { useFetchCurrentCart } from '../../../shared/api/storefront/hooks/cart/use-fetch-current-cart.js';
14
7
  import { useFetchFulfillmentMethodsForCurrentCart } from '../../../shared/api/storefront/hooks/customer/use-fetch-fulfillment-methods-for-current-cart.js';
15
8
  import { useFetchCountries } from '../../../shared/api/storefront/hooks/website/use-fetch-countries.js';
16
- import { patchCart } from '../../../shared/api/storefront/services/cart-service.js';
17
- import { patchBillToAddress } from '../../../shared/api/storefront/services/customer-service.js';
9
+ import { useDataLayer } from '../../../shared/ga/use-data-layer.js';
18
10
  import { useNavigate } from '../../../shared/routing/route-provider.js';
19
11
  import { hasNo } from '../../../shared/utils/types.js';
20
- import { Page } from '../../components/page/page.js';
21
12
  import { ErrorPage } from '../../error-page/error-page.js';
22
13
  import { LoadingPage } from '../../loading-page/loading-page.js';
23
- import { CheckoutPageLayout } from '../layouts/checkout-page-layout/checkout-page-layout.js';
24
- import { CheckoutPageSection } from '../layouts/checkout-page-layout/components/checkout-page-section.js';
25
- import { CheckoutPageSectionContent } from '../layouts/checkout-page-layout/components/checkout-page-section-content.js';
26
- import { EDIT_ADDRESS_FORM_ID, EditAddresses } from './components/edit-address.js';
14
+ import { CHECKOUT_PATHS } from '../constants.js';
15
+ import { EditAddresses } from './components/edit-address.js';
27
16
  import { ReadOnlyAddresses } from './components/readonly-address.js';
28
- import styles from './shipping-page.module.css.js';
17
+ import { usePatchShippingDetails } from './hooks/use-patch-shipping-details.js';
18
+ import { ShippingPageContent } from './shipping-page-content.js';
29
19
 
30
- const REVIEW_AND_SUBMIT_PATH = '/CheckoutReviewAndSubmit';
31
- function usePatchCartAndBillingAddress() {
32
- return useAwaitableMutation({
33
- mutationFn: async ({ billTo, cart, notes, }) => {
34
- await Promise.all([
35
- billTo && patchBillToAddress({ billTo }),
36
- patchCart({ cart: { ...cart, notes } }),
37
- ].filter(Boolean));
38
- },
39
- onSuccess: ({ queryClient }) => {
40
- queryClient.removeQueries({ queryKey: ['customer'] });
41
- queryClient.removeQueries({ queryKey: ['carts'] });
42
- queryClient.removeQueries({ queryKey: ['session'] });
43
- },
44
- });
45
- }
46
20
  function ShippingPage() {
47
21
  const isAuthenticated = useIsAuthenticated();
48
- const { data: cart, error, isLoading: isLoadingCart, refetch: refetchCart, } = useFetchCurrentCart();
22
+ const { createEcommerceEvent, dataLayer } = useDataLayer();
23
+ const gaEventPushed = useRef(false);
24
+ const { data: cart, error: errorFetchCart, isLoading: isLoadingCart, refetch: refetchCart, } = useFetchCurrentCart();
49
25
  const { data: countries, isLoading: isLoadingCountries } = useFetchCountries({
50
26
  enabled: hasNo(cart?.billTo?.address1),
51
27
  });
52
28
  const { data: session } = useFetchSession();
53
29
  const { data: fulfillmentMethods, isLoading: isLoadingFulfillmentMethods } = useFetchFulfillmentMethodsForCurrentCart();
54
30
  const { isLoading: isPatchingSession, mutate: patchSession } = usePatchSession();
55
- const { isError, isLoading: isPatching, isSuccess, mutate: patchCartAndBillingAddress, } = usePatchCartAndBillingAddress();
31
+ const { error: errorPatchBillingAddress, isError, isLoading: isPatching, isSuccess, mutate: patchShippingDetails, } = usePatchShippingDetails();
56
32
  const isLoading = isLoadingCart || isLoadingCountries || isLoadingFulfillmentMethods;
57
- const t = useFormattedMessage();
58
33
  const { isNavigating, navigate } = useNavigate();
59
- const hasBillToAddress = useMemo(() => {
60
- return Boolean(cart?.billTo?.address1);
61
- // eslint-disable-next-line react-hooks/exhaustive-deps
62
- }, [Boolean(cart)]);
63
- const fulfillmentMethodOptions = fulfillmentMethods?.reduce((acc, method) => ({
64
- ...acc,
65
- [method]: t(`fulfillmentMethod.${method}`),
66
- }), {});
67
34
  const isPickup = cart?.fulfillmentMethod === 'PickUp';
68
35
  useEffect(() => {
69
36
  /* Initial guards. When these are not met, we should not proceed */
@@ -72,13 +39,13 @@ function ShippingPage() {
72
39
  if (isAuthenticated === undefined)
73
40
  return;
74
41
  if (!isAuthenticated)
75
- return navigate('/signin?returnUrl=/CheckoutShipping');
42
+ return navigate(CHECKOUT_PATHS.CHECKOUT_SHIPPING_VIA_SIGNIN);
76
43
  if (hasNo(cart))
77
44
  return;
78
45
  if (hasNo(cart.cartLines) || cart.cartLines.length === 0)
79
- return navigate('/cart');
46
+ return navigate(CHECKOUT_PATHS.CART);
80
47
  if (hasNo(cart.billTo))
81
- return navigate('/signin?returnUrl=/CheckoutShipping');
48
+ return navigate(CHECKOUT_PATHS.CHECKOUT_SHIPPING_VIA_SIGNIN);
82
49
  }, [cart, navigate, isAuthenticated, isNavigating]);
83
50
  useEffect(() => {
84
51
  /* Guards for when the bill to address is saved and we should navigate to the next page */
@@ -92,10 +59,19 @@ function ShippingPage() {
92
59
  return location?.reload();
93
60
  if (hasNo(cart?.billTo?.address1))
94
61
  return;
95
- return navigate(REVIEW_AND_SUBMIT_PATH);
62
+ return navigate(CHECKOUT_PATHS.REVIEW_AND_SUBMIT);
96
63
  }, [isSuccess, cart, navigate, isNavigating, isPatching, isError]);
97
- if (error)
98
- return jsx(ErrorPage, { error: error });
64
+ useEffect(() => {
65
+ if (!cart || gaEventPushed.current)
66
+ return;
67
+ dataLayer.push(createEcommerceEvent({
68
+ cart,
69
+ event: { event: 'begin_checkout' },
70
+ }));
71
+ gaEventPushed.current = true;
72
+ }, [cart, createEcommerceEvent, dataLayer]);
73
+ if (errorFetchCart)
74
+ return jsx(ErrorPage, { error: errorFetchCart });
99
75
  if (isLoading || isNavigating || isError || isSuccess)
100
76
  return jsx(LoadingPage, {});
101
77
  if (!isAuthenticated ||
@@ -105,34 +81,35 @@ function ShippingPage() {
105
81
  hasNo(cart.billTo) ||
106
82
  (hasNo(cart.billTo.address1) && hasNo(countries)))
107
83
  return null;
108
- return (jsx(Page, { breadCrumb: [
109
- { href: '/', label: t('Home') },
110
- { href: '/CheckoutShipping', label: t('Shipping details') },
111
- ], title: t('Shipping details'), children: jsx(CheckoutPageLayout, { actions: {
112
- primary: (jsx(Button, { withArrow: true, "data-test-selector": "checkoutShippingCartTotalContinueButton", form: EDIT_ADDRESS_FORM_ID, isDisabled: isPatching, isLoading: isPatching || isPatchingSession ? (jsx(FormattedMessage, { id: "Updating address" })) : undefined, type: "submit", children: jsx(FormattedMessage, { id: "Continue shopping" }) })),
113
- }, mobileSummary: jsx(CartTotalsSummary, { totalAmount: cart.orderGrandTotalDisplay }), overview: jsx(CartTotals, { fulfillmentMethod: fulfillmentMethods && fulfillmentMethods.length === 1
114
- ? cart.fulfillmentMethod
115
- : undefined, shippingCost: cart.shippingAndHandlingDisplay, subtotal: cart.orderSubTotalDisplay, tax: cart.totalTaxDisplay, total: cart.orderGrandTotalDisplay, vatPercentage: cart.cartLines[0]?.pricing?.vatRate }), children: jsxs(Fragment, { children: [fulfillmentMethods && fulfillmentMethods.length > 1 && (jsx(CheckoutPageSection, { hasBorder: true, title: t('Fulfillment method'), children: jsx(CheckoutPageSectionContent, { children: jsx("div", { className: styles['fulfillment-select-wrapper'], children: jsx(Select, { isRequired: true, showLabel: true, "data-test-selector": "fulfillmentMethodSelect", defaultSelectedOption: cart.fulfillmentMethod, isLoading: isLoadingFulfillmentMethods, label: t('Fulfillment method'), name: "fulfillmentMethod", onChange: async (value) => {
116
- await patchSession({
117
- session: {
118
- ...session,
119
- fulfillmentMethod: value,
120
- },
121
- });
122
- await refetchCart();
123
- }, options: fulfillmentMethodOptions || {}, variant: "solid" }) }) }) })), hasBillToAddress ? (jsx(ReadOnlyAddresses, { billTo: cart.billTo, isLoading: isPatching, isPickup: isPickup, notes: cart.notes, onSubmit: async ({ notes }) => {
124
- if (!cart.billTo)
125
- return;
126
- await patchCartAndBillingAddress({ cart, notes });
127
- }, shipTo: cart.shipTo })) : (jsx(EditAddresses, { countries: countries || [], isLoading: isPatching, isPickup: isPickup, onSubmit: async ({ address, notes }) => {
128
- if (!cart.billTo)
129
- return;
130
- await patchCartAndBillingAddress({
131
- billTo: { ...cart.billTo, ...address },
132
- cart,
133
- notes,
134
- });
135
- } }))] }) }) }));
84
+ return (jsx(ShippingPageContent, { cart: cart, editAddress: jsx(EditAddresses, { countries: countries || [], isLoading: isPatching, isPickup: isPickup, onSubmit: async ({ address, notes }) => {
85
+ if (!cart.billTo)
86
+ return;
87
+ await patchShippingDetails({
88
+ billTo: { ...cart.billTo, ...address },
89
+ cart,
90
+ notes,
91
+ });
92
+ dataLayer.push(createEcommerceEvent({
93
+ cart,
94
+ event: { event: 'add_shipping_info' },
95
+ }));
96
+ } }), errorPatchBillingAddress: errorPatchBillingAddress, fulfillmentMethods: fulfillmentMethods, isLoadingFulfillmentMethods: isLoadingFulfillmentMethods, isPatching: isPatching, isPatchingSession: isPatchingSession, onChangeFulfillmentMethod: async (value) => {
97
+ await patchSession({
98
+ session: {
99
+ ...session,
100
+ fulfillmentMethod: value,
101
+ },
102
+ });
103
+ await refetchCart();
104
+ }, readOnlyAddress: jsx(ReadOnlyAddresses, { billTo: cart.billTo, isLoading: isPatching, isPickup: isPickup, notes: cart.notes, onSubmit: async ({ notes }) => {
105
+ if (!cart.billTo)
106
+ return;
107
+ await patchShippingDetails({ cart, notes });
108
+ dataLayer.push(createEcommerceEvent({
109
+ cart,
110
+ event: { event: 'add_shipping_info' },
111
+ }));
112
+ }, shipTo: cart.shipTo }) }));
136
113
  }
137
114
 
138
115
  export { ShippingPage };
@@ -1,3 +1,3 @@
1
- var styles = {"fulfillment-select-wrapper":"shipping-page-module-v6oci"};
1
+ var styles = {"fulfillment-select-wrapper":"shipping-page-module-v6oci","error-message":"shipping-page-module-FK0yX"};
2
2
 
3
3
  export { styles as default };
@@ -1,9 +1,10 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { BreadcrumbLink } from '../../../breadcrumbs/breadcrumb';
3
- export interface PageProps {
3
+ import { PageMetaDataProps } from '../page-meta-data/page-meta-data';
4
+ export interface PageProps extends PageMetaDataProps {
4
5
  breadCrumb: BreadcrumbLink[];
5
6
  children: ReactNode;
6
7
  className?: string;
7
8
  title?: string;
8
9
  }
9
- export declare function Page({ breadCrumb, children, className, title }: PageProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare function Page({ alternateLanguageUrls, breadCrumb, canonicalUrl, children, className, meta, openGraph, title, windowTitle, }: PageProps): import("react/jsx-runtime").JSX.Element;
@@ -4,13 +4,14 @@ import clsx from 'clsx';
4
4
  import { Breadcrumb } from '../../../breadcrumbs/breadcrumb.js';
5
5
  import { Heading } from '../../../typography/heading/heading.js';
6
6
  import { PageContainer } from '../page-container/page-container.js';
7
+ import { PageMetaData } from '../page-meta-data/page-meta-data.js';
7
8
  import styles from './page.module.css.js';
8
9
 
9
10
  function PageTitle({ children }) {
10
11
  return (jsx(Heading, { italic: true, uppercase: true, breakpoints: { lg: 'm', xxl: 'xl' }, className: styles.title, size: "s", tag: "h1", children: children }));
11
12
  }
12
- function Page({ breadCrumb, children, className, title }) {
13
- return (jsxs(PageContainer, { className: clsx(styles.page, className), children: [jsx("div", { className: styles.breadcrumb, children: jsx(Breadcrumb, { links: breadCrumb }) }), title && jsx(PageTitle, { children: title }), children] }));
13
+ function Page({ alternateLanguageUrls, breadCrumb, canonicalUrl, children, className, meta, openGraph, title, windowTitle, }) {
14
+ return (jsxs(PageContainer, { className: clsx(styles.page, className), children: [jsx(PageMetaData, { alternateLanguageUrls: alternateLanguageUrls, canonicalUrl: canonicalUrl, meta: meta, openGraph: openGraph, windowTitle: windowTitle }), jsx("div", { className: styles.breadcrumb, children: jsx(Breadcrumb, { links: breadCrumb }) }), title && jsx(PageTitle, { children: title }), children] }));
14
15
  }
15
16
 
16
17
  export { Page };
@@ -1,6 +1,6 @@
1
- interface PageMetaDataProps {
1
+ export interface PageMetaDataProps {
2
2
  alternateLanguageUrls?: Record<string, string> | null;
3
- canonicalUrl: string;
3
+ canonicalUrl?: string;
4
4
  meta?: {
5
5
  description: string;
6
6
  keywords: string;
@@ -10,7 +10,6 @@ interface PageMetaDataProps {
10
10
  title: string;
11
11
  url: string;
12
12
  };
13
- title: string;
13
+ windowTitle?: string;
14
14
  }
15
15
  export declare function PageMetaData(props: PageMetaDataProps): null;
16
- export {};
@@ -21,8 +21,8 @@ function removeElements(selector) {
21
21
  function PageMetaData(props) {
22
22
  if (typeof document === 'undefined')
23
23
  return null;
24
- if (props.title) {
25
- document.title = props.title;
24
+ if (props.windowTitle) {
25
+ document.title = props.windowTitle;
26
26
  }
27
27
  if (props.canonicalUrl) {
28
28
  addOrUpdateElement('link[rel="canonical"]', {
@@ -10,10 +10,20 @@ import { FormattedMessage } from '../../../../../intl/formatted-message.js';
10
10
  import { DownloadDocumentList } from '../../../../../lists/download-document-list/download-document-list.js';
11
11
  import { FeatureList } from '../../../../../lists/feature-list/feature-list.js';
12
12
  import { Heading } from '../../../../../typography/heading/heading.js';
13
+ import { useAlgoliaInsights } from '../../../../../algolia/use-algolia-insights.js';
13
14
  import styles from './product-details-panel.module.css.js';
14
15
 
15
16
  function ProductDetailsPanel({ priceComponent, product, }) {
16
- return (jsxs("div", { className: styles['product-details-panel'], children: [jsxs("div", { className: styles.heading, children: [jsx(Heading, { italic: true, size: "xs", children: product.productTitle }), jsx(ProductSku, { sku: product.productNumber })] }), jsxs("div", { className: styles['price-action-container'], children: [priceComponent || (jsx(ProductPrice, { "data-test": "productPrice_unitNetPrice", isVatIncluded: product.isVatIncluded, originalPrice: product.originalPrice, price: product.price })), jsxs("div", { className: styles['action-container'], children: [product.canAddToCart && (jsx(ConnectedAddToCartButton, { productId: product.storefrontId })), product.canAddToWishlist && (jsx(ConnectedFavoriteButton, { productId: product.storefrontId }))] })] }), jsxs("div", { children: [jsx(Heading, { italic: true, className: styles['product-features-heading'], size: "xxs", children: jsx(FormattedMessage, { id: "Product Features" }) }), product.content.htmlContent && (jsx(ShowAll, { initialHeight: 216, children: jsx("div", { className: styles.description, dangerouslySetInnerHTML: {
17
+ const { sendAddToCartFromProductDetailsPageEvent, sendAddToWishListFromProductDetailsPageEvent, } = useAlgoliaInsights();
18
+ return (jsxs("div", { className: styles['product-details-panel'], children: [jsxs("div", { className: styles.heading, children: [jsx(Heading, { italic: true, size: "xs", children: product.productTitle }), jsx(ProductSku, { sku: product.productNumber })] }), jsxs("div", { className: styles['price-action-container'], children: [priceComponent || (jsx(ProductPrice, { "data-test": "productPrice_unitNetPrice", isVatIncluded: product.isVatIncluded, originalPrice: product.originalPrice, price: product.price })), jsxs("div", { className: styles['action-container'], children: [product.canAddToCart && (jsx(ConnectedAddToCartButton, { onAddToCart: ({ cartLine }) => {
19
+ sendAddToCartFromProductDetailsPageEvent({
20
+ cartLine,
21
+ });
22
+ }, productId: product.storefrontId })), product.canAddToWishlist && (jsx(ConnectedFavoriteButton, { onFavoriting: () => {
23
+ sendAddToWishListFromProductDetailsPageEvent({
24
+ objectId: product.productNumber,
25
+ });
26
+ }, productId: product.storefrontId }))] })] }), jsxs("div", { children: [jsx(Heading, { italic: true, className: styles['product-features-heading'], size: "xxs", children: jsx(FormattedMessage, { id: "Product Features" }) }), product.content.htmlContent && (jsx(ShowAll, { initialHeight: 216, children: jsx("div", { className: styles.description, dangerouslySetInnerHTML: {
17
27
  __html: product.content.htmlContent,
18
28
  } }) }))] }), jsx("div", { children: jsxs(Accordion, { indented: true, borderType: ['top', 'middle-accentuated'], children: [product.attributeTypes.length > 0 && (jsx(AccordionItem, { id: "specification", title: jsx(FormattedMessage, { id: "Specifications" }), children: jsx(FeatureList, { className: styles['feature-list'], features: product.attributeTypes.map(attribute => ({
19
29
  id: `${attribute.id}`,
@@ -1,8 +1,7 @@
1
1
  "use client";
2
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { jsx } from 'react/jsx-runtime';
3
3
  import { useCultureCode } from '../../../intl/use-culture-code.js';
4
4
  import { useFetchProductDetailsPageData } from '../../../shared/api/bff/hooks/use-fetch-product-details-page-data.js';
5
- import { PageMetaData } from '../../components/page-meta-data/page-meta-data.js';
6
5
  import { ErrorPage } from '../../error-page/error-page.js';
7
6
  import { LoadingPage } from '../../loading-page/loading-page.js';
8
7
  import { ProductDetails } from './product-details.js';
@@ -17,14 +16,7 @@ function ProductDetailsPage({ pageUrl }) {
17
16
  return jsx(ErrorPage, { error: error });
18
17
  if (!data || isFetching)
19
18
  return jsx(LoadingPage, {});
20
- return (jsxs(Fragment, { children: [data.page && (jsx(PageMetaData, { alternateLanguageUrls: data.page.alternateLanguageUrls, canonicalUrl: data.page.canonicalPath, meta: {
21
- description: data.page.metaDescription,
22
- keywords: data.page.metaKeywords,
23
- }, openGraph: {
24
- image: data.page.openGraphImage,
25
- title: data.page.openGraphTitle,
26
- url: data.page.openGraphUrl,
27
- }, title: data.page.title })), jsx(ProductDetails, { data: data })] }));
19
+ return jsx(ProductDetails, { data: data });
28
20
  }
29
21
 
30
22
  export { ProductDetailsPage };
@@ -13,20 +13,26 @@ import { ProductDetailsPanel } from './components/product-details-panel/product-
13
13
  import { ProductDetailsRecentlyViewed } from './components/product-details-recently-viewed/product-details-recently-viewed.js';
14
14
 
15
15
  function ProductDetails({ data, priceComponent, recentlyViewedComponent, }) {
16
- const { breadCrumb, included, product, recentlyViewed, usps } = data;
16
+ const { breadCrumb, included, page, product, recentlyViewed, usps } = data;
17
17
  useDataLayer({
18
18
  event: {
19
19
  event: 'view_item',
20
20
  },
21
21
  product,
22
22
  });
23
- return (jsx(Page, { breadCrumb: breadCrumb, children: jsx(ProductDetailsPageLayout, { imageGallery: jsx(ProductDetailImages, { images: product.images }), included: included !== undefined &&
24
- included.length > 0 && (jsxs(Fragment, { children: [jsx(Heading, { size: "s", tag: "h2", children: jsx(FormattedMessage, { id: "Includes" }) }), jsx(CardCarousel, { allowExpandToGrid: true, hasOverflow: true, cards: included.map(product => (jsx(ConnectedProductCard, { href: product.href, id: product.productId, image: {
23
+ return (jsx(Page, { alternateLanguageUrls: page.alternateLanguageUrls, breadCrumb: breadCrumb, canonicalUrl: page.canonicalPath, meta: {
24
+ description: page.metaDescription,
25
+ keywords: page.metaKeywords,
26
+ }, openGraph: {
27
+ image: page.openGraphImage,
28
+ title: page.openGraphTitle,
29
+ url: page.openGraphUrl,
30
+ }, windowTitle: page.title, children: jsx(ProductDetailsPageLayout, { imageGallery: jsx(ProductDetailImages, { images: product.images }), included: included.length > 0 && (jsxs(Fragment, { children: [jsx(Heading, { size: "s", tag: "h2", children: jsx(FormattedMessage, { id: "Includes" }) }), jsx(CardCarousel, { allowExpandToGrid: true, hasOverflow: true, cards: included.map(product => (jsx(ConnectedProductCard, { href: product.href, id: product.productId, image: {
25
31
  fit: 'contain',
26
32
  image: product.image,
27
33
  title: product.image?.altText || product.title,
28
34
  }, price: product.price, sku: product.sku, tags: product.tags, title: product.title }, product.storefrontId))) })] })), productInformation: jsx(ProductDetailsPanel, { priceComponent: priceComponent, product: product }), recentlyViewed: recentlyViewedComponent ||
29
- (recentlyViewed !== undefined && recentlyViewed.length > 0 && (jsx(ProductDetailsRecentlyViewed, { recentlyViewed: recentlyViewed }))), usp: usps && jsx(ProductUSPCarousel, { usps: usps }) }) }));
35
+ (recentlyViewed.length > 0 && (jsx(ProductDetailsRecentlyViewed, { recentlyViewed: recentlyViewed }))), usp: usps && jsx(ProductUSPCarousel, { usps: usps }) }) }));
30
36
  }
31
37
 
32
38
  export { ProductDetails };
@@ -18,7 +18,7 @@ export interface ProductListingPageData {
18
18
  category: CategoryType | undefined;
19
19
  categoryPages: string;
20
20
  hierarchicalCategories: string[];
21
- page?: PageModel;
21
+ page: PageModel;
22
22
  promoBanners?: {
23
23
  top: PromoBannerType[] | undefined;
24
24
  };
@@ -1,10 +1,9 @@
1
1
  "use client";
2
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { jsx } from 'react/jsx-runtime';
3
3
  import { createSonicSearchClient } from '../../../algolia/algolia-sonic-searchclient.js';
4
4
  import { config } from '../../../config.js';
5
5
  import { useCultureCode } from '../../../intl/use-culture-code.js';
6
6
  import { useFetchProductListingPageData } from '../../../shared/api/bff/hooks/use-fetch-product-listing-page-data.js';
7
- import { PageMetaData } from '../../components/page-meta-data/page-meta-data.js';
8
7
  import { ErrorPage } from '../../error-page/error-page.js';
9
8
  import { LoadingPage } from '../../loading-page/loading-page.js';
10
9
  import { ProductListing } from './product-listing.js';
@@ -24,14 +23,7 @@ function ProductListingPage({ pageUrl, searchClient = defaultSearchClient, }) {
24
23
  return jsx(ErrorPage, { error: error });
25
24
  if (!data || isFetching)
26
25
  return jsx(LoadingPage, {});
27
- return (jsxs(Fragment, { children: [data.page && (jsx(PageMetaData, { alternateLanguageUrls: data.page.alternateLanguageUrls, canonicalUrl: data.page.canonicalPath, meta: {
28
- description: data.page.metaDescription,
29
- keywords: data.page.metaKeywords,
30
- }, openGraph: {
31
- image: data.page.openGraphImage,
32
- title: data.page.openGraphTitle,
33
- url: data.page.openGraphUrl,
34
- }, title: data.page.title })), jsx(ProductListing, { data: data, searchClient: searchClient })] }));
26
+ return jsx(ProductListing, { data: data, searchClient: searchClient });
35
27
  }
36
28
 
37
29
  export { ProductListingPage };
@@ -15,7 +15,6 @@ function ProductHitCard({ hit }) {
15
15
  title: hit.name,
16
16
  }, onAddToCart: ({ cartLine }) => sendAddToCartFromProductListPageEvent({
17
17
  cartLine,
18
- objectId: hit.objectId,
19
18
  }), onClick: () => sendProductClickFromProductListPageEvent({
20
19
  objectId: hit.objectId,
21
20
  position: hit.position,
@@ -24,7 +24,14 @@ import styles from './product-listing-page.module.css.js';
24
24
  function ProductListing({ data, searchClient, }) {
25
25
  const languageCode = useLanguageCode();
26
26
  const category = data.breadCrumb.slice(1).map(breadCrumb => breadCrumb.label);
27
- return (jsx(AlgoliaProvider, { categoryPages: data.categoryPages, hierarchicalCategories: data.hierarchicalCategories, languageCode: languageCode, offlineSearchClient: offlineSearchClient, searchClient: searchClient, children: jsx(Page, { breadCrumb: data.breadCrumb, className: styles['product-listing'], title: [...category].pop(), children: jsx(ProductListingPageContent, { currentCategoryPath: data.hierarchicalCategories, promoBanners: data.promoBanners?.top }) }) }, data.categoryPages));
27
+ return (jsx(AlgoliaProvider, { categoryPages: data.categoryPages, hierarchicalCategories: data.hierarchicalCategories, languageCode: languageCode, offlineSearchClient: offlineSearchClient, searchClient: searchClient, children: jsx(Page, { alternateLanguageUrls: data.page.alternateLanguageUrls, breadCrumb: data.breadCrumb, canonicalUrl: data.page.canonicalPath, className: styles['product-listing'], meta: {
28
+ description: data.page.metaDescription,
29
+ keywords: data.page.metaKeywords,
30
+ }, openGraph: {
31
+ image: data.page.openGraphImage,
32
+ title: data.page.openGraphTitle,
33
+ url: data.page.openGraphUrl,
34
+ }, title: [...category].pop(), windowTitle: data.page.title, children: jsx(ProductListingPageContent, { currentCategoryPath: data.hierarchicalCategories, promoBanners: data.promoBanners?.top }) }) }, data.categoryPages));
28
35
  }
29
36
  function ProductListingPageContent({ currentCategoryPath, promoBanners, }) {
30
37
  const { hits, isLoading } = useAlgoliaHits();
@@ -15,7 +15,6 @@ function ProductHitCard({ hit }) {
15
15
  title: hit.name,
16
16
  }, onAddToCart: ({ cartLine }) => sendAddToCartFromSearchResultPageEvent({
17
17
  cartLine,
18
- objectId: hit.objectId,
19
18
  }), onClick: () => {
20
19
  sendProductClickFromSearchResultPageEvent({
21
20
  objectId: hit.objectId,
@@ -14,7 +14,6 @@ import { scrollToTop } from '../../../shared/hooks/use-scroll-to.js';
14
14
  import { Sidebar } from '../../../sidebar/sidebar.js';
15
15
  import { ToggleSidebarButton } from '../../../sidebar/toggle-sidebar-button.js';
16
16
  import { Page } from '../../components/page/page.js';
17
- import { PageMetaData } from '../../components/page-meta-data/page-meta-data.js';
18
17
  import { LoadingPage } from '../../loading-page/loading-page.js';
19
18
  import { NoResults } from '../product-listing-page/no-results/no-results.js';
20
19
  import { SearchResultProductOverview } from './search-result-product-overview/search-result-product-overview.js';
@@ -28,13 +27,11 @@ function SearchResultsPage({ location, searchClient, }) {
28
27
  const keyword = Array.isArray(keywords) ? keywords.join(' ') : keywords;
29
28
  if (!keyword)
30
29
  return jsx("h1", { children: "No search results...." });
31
- return (jsxs(AlgoliaProvider, { languageCode: languageCode, query: keyword, searchClient: searchClient, children: [jsx(PageMetaData
30
+ return (jsx(AlgoliaProvider, { languageCode: languageCode, query: keyword, searchClient: searchClient, children: jsx(Page, { breadCrumb: [],
32
31
  // eslint-disable-next-line ssr-friendly/no-dom-globals-in-react-fc
33
- , {
34
- // eslint-disable-next-line ssr-friendly/no-dom-globals-in-react-fc
35
- canonicalUrl: typeof window === 'undefined' ? '' : window.location.href, title: t('Search') }), jsx(Page, { breadCrumb: [], className: styles['search-results'], title: t("'{0}' in all products", {
36
- replacementValues: { 0: keyword },
37
- }), children: jsx(SearchResultsPageContent, { keyword: keyword }) })] }));
32
+ canonicalUrl: typeof window === 'undefined' ? '' : window.location.href, className: styles['search-results'], title: t("'{0}' in all products", {
33
+ replacementValues: { 0: keyword },
34
+ }), windowTitle: `${t('Search')} - ${t('Sonic Equipment')}`, children: jsx(SearchResultsPageContent, { keyword: keyword }) }) }));
38
35
  }
39
36
  function SearchResultsPageContent({ keyword }) {
40
37
  const { hits, isLoading } = useAlgoliaHits();