@graphcommerce/magento-product 8.1.0-canary.2 → 8.1.0-canary.20

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 (43) hide show
  1. package/CHANGELOG.md +131 -1
  2. package/components/AddProductsToCart/AddProductsToCartButton.tsx +1 -0
  3. package/components/AddProductsToCart/AddProductsToCartForm.tsx +7 -2
  4. package/components/AddProductsToCart/AddProductsToCartSnackbar.tsx +13 -14
  5. package/components/AddProductsToCart/findAddedItems.ts +81 -0
  6. package/components/AddProductsToCart/index.ts +3 -0
  7. package/components/AddProductsToCart/useAddProductsToCartAction.ts +6 -3
  8. package/components/AddProductsToCart/useFormAddProductsToCart.ts +1 -2
  9. package/components/JsonLdProduct/JsonLdProductOffer.graphql +2 -3
  10. package/components/JsonLdProduct/ProductPageJsonLd.tsx +9 -4
  11. package/components/JsonLdProduct/index.ts +1 -0
  12. package/components/ProductAddToCart/ProductAddToCart.tsx +6 -4
  13. package/components/ProductCustomizable/CustomizableAreaOption.tsx +41 -7
  14. package/components/ProductCustomizable/CustomizableDateOption.tsx +60 -7
  15. package/components/ProductCustomizable/CustomizableDropDownOption.tsx +63 -15
  16. package/components/ProductCustomizable/CustomizableFieldOption.tsx +40 -4
  17. package/components/ProductCustomizable/index.ts +1 -0
  18. package/components/ProductCustomizable/productCustomizableSelectors.ts +59 -0
  19. package/components/ProductFiltersPro/ProductFiltersPro.tsx +25 -10
  20. package/components/ProductFiltersPro/ProductFiltersProAllFiltersChip.tsx +6 -2
  21. package/components/ProductFiltersPro/ProductFiltersProAllFiltersSidebar.tsx +6 -2
  22. package/components/ProductFiltersPro/ProductFiltersProSortChip.tsx +9 -28
  23. package/components/ProductFiltersPro/ProductFiltersProSortDirectionArrow.tsx +15 -0
  24. package/components/ProductFiltersPro/ProductFiltersProSortSection.tsx +7 -32
  25. package/components/ProductFiltersPro/useProductFiltersProSort.tsx +74 -0
  26. package/components/ProductListItems/CategoryDefault.graphql +5 -0
  27. package/components/ProductListItems/ProductListItemsBase.tsx +1 -1
  28. package/components/ProductListItems/filterTypes.tsx +1 -1
  29. package/components/ProductListItems/filteredProductList.tsx +1 -1
  30. package/components/ProductListItems/productListApplyCategoryDefaults.ts +28 -0
  31. package/components/ProductPageBreadcrumb/ProductPageBreadcrumb.tsx +5 -3
  32. package/components/ProductPageMeta/ProductPageMeta.graphql +1 -1
  33. package/components/ProductPagePrice/ProductPagePrice.graphql +3 -0
  34. package/components/ProductPagePrice/ProductPagePrice.tsx +11 -4
  35. package/components/ProductPagePrice/useCustomizableOptionPrice.ts +85 -0
  36. package/components/ProductShortDescription/ProductShortDescription.tsx +2 -0
  37. package/components/ProductStaticPaths/getProductStaticPaths.ts +2 -3
  38. package/components/ProductStaticPaths/getSitemapPaths.ts +3 -0
  39. package/components/index.ts +2 -0
  40. package/hooks/useProductListLink.ts +10 -5
  41. package/hooks/useProductListLinkReplace.ts +3 -0
  42. package/package.json +13 -13
  43. package/tsconfig.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,6 +1,136 @@
1
1
  # Change Log
2
2
 
3
- ## 8.1.0-canary.2
3
+ ## 8.1.0-canary.20
4
+
5
+ ## 8.1.0-canary.19
6
+
7
+ ## 8.1.0-canary.18
8
+
9
+ ## 8.1.0-canary.17
10
+
11
+ ## 8.1.0-canary.16
12
+
13
+ ## 8.1.0-canary.15
14
+
15
+ ## 8.1.0-canary.14
16
+
17
+ ## 8.1.0-canary.13
18
+
19
+ ## 8.1.0-canary.12
20
+
21
+ ## 8.1.0-canary.11
22
+
23
+ ### Patch Changes
24
+
25
+ - [#2255](https://github.com/graphcommerce-org/graphcommerce/pull/2255) [`679d07d`](https://github.com/graphcommerce-org/graphcommerce/commit/679d07dad5b5e2dab3c0f3d537716b6115af8ef7) - make the ProductPageMeta fragment injectable
26
+ ([@carlocarels90](https://github.com/carlocarels90))
27
+
28
+ ## 8.1.0-canary.10
29
+
30
+ ## 8.1.0-canary.9
31
+
32
+ ## 8.1.0-canary.8
33
+
34
+ ## 8.1.0-canary.7
35
+
36
+ ## 8.1.0-canary.6
37
+
38
+ ## 8.1.0-canary.5
39
+
40
+ ### Patch Changes
41
+
42
+ - [#2224](https://github.com/graphcommerce-org/graphcommerce/pull/2224) [`4dd2d01`](https://github.com/graphcommerce-org/graphcommerce/commit/4dd2d01b3e14e3107ee3e337bef2a8528d654e75) - When applying a filter it would not always scroll to the #products.
43
+ ([@FrankHarland](https://github.com/FrankHarland))
44
+
45
+ ## 8.0.6-canary.4
46
+
47
+ ### Patch Changes
48
+
49
+ - [#2227](https://github.com/graphcommerce-org/graphcommerce/pull/2227) [`d597719`](https://github.com/graphcommerce-org/graphcommerce/commit/d597719baaabbe079660ac063fd021d871831511) - Added option to change sort order (ASC / DESC) for sort options (Name, price, position etc) on catalog and search pages.
50
+ ([@FrankHarland](https://github.com/FrankHarland))
51
+
52
+ ## 8.0.6-canary.3
53
+
54
+ ## 8.0.6-canary.2
55
+
56
+ ### Patch Changes
57
+
58
+ - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`0767bc4`](https://github.com/graphcommerce-org/graphcommerce/commit/0767bc40f7b596209f24ca4e745ff0441f3275c9) - Upgrade input components to no longer use muiRegister, which improves INP scores
59
+ ([@FrankHarland](https://github.com/FrankHarland))
60
+
61
+ - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`43bd04a`](https://github.com/graphcommerce-org/graphcommerce/commit/43bd04a777c5800cc7e01bee1e123a5aad82f194) - Prevent BillingPage query from rerunning on each mutation
62
+ ([@FrankHarland](https://github.com/FrankHarland))
63
+
64
+ ## 8.0.6-canary.1
65
+
66
+ ## 8.0.6-canary.0
67
+
68
+ ### Patch Changes
69
+
70
+ - [#2196](https://github.com/graphcommerce-org/graphcommerce/pull/2196) [`84c50e4`](https://github.com/graphcommerce-org/graphcommerce/commit/84c50e49a1a7f154d4a8f4045c37e773e20283ad) - Allow Lingui to use linguiLocale with country identifiers like `en-us`, it would always load `en` in this case. Introced a new `useLocale` hook to use the correct locale string to use in Intl methods.
71
+ ([@paales](https://github.com/paales))
72
+
73
+ ## 8.0.5
74
+
75
+ ### Patch Changes
76
+
77
+ - [#2239](https://github.com/graphcommerce-org/graphcommerce/pull/2239) [`a2a6e78`](https://github.com/graphcommerce-org/graphcommerce/commit/a2a6e78291ab978f4ef1236a4476b4d54555af7f) - Prices of products are now updated when customizable options are selected
78
+ ([@Jessevdpoel](https://github.com/Jessevdpoel))
79
+
80
+ ## 8.0.5-canary.10
81
+
82
+ ## 8.0.5-canary.9
83
+
84
+ ## 8.0.5-canary.8
85
+
86
+ ### Patch Changes
87
+
88
+ - [#2239](https://github.com/graphcommerce-org/graphcommerce/pull/2239) [`a2a6e78`](https://github.com/graphcommerce-org/graphcommerce/commit/a2a6e78291ab978f4ef1236a4476b4d54555af7f) - Prices of products are now updated when customizable options are selected
89
+ ([@Jessevdpoel](https://github.com/Jessevdpoel))
90
+
91
+ ## 8.0.5-canary.7
92
+
93
+ ## 8.0.5-canary.6
94
+
95
+ ## 8.0.5-canary.5
96
+
97
+ ## 8.0.5-canary.4
98
+
99
+ ## 8.0.5-canary.3
100
+
101
+ ## 8.0.5-canary.2
102
+
103
+ ## 8.0.5-canary.1
104
+
105
+ ## 8.0.5-canary.0
106
+
107
+ ## 8.0.4
108
+
109
+ ## 8.0.4-canary.1
110
+
111
+ ## 8.0.4-canary.0
112
+
113
+ ## 8.0.3
114
+
115
+ ### Patch Changes
116
+
117
+ - [#2217](https://github.com/graphcommerce-org/graphcommerce/pull/2217) [`2b750da`](https://github.com/graphcommerce-org/graphcommerce/commit/2b750da77723d37485da01df05f52d19b80cc90c) - fix for breadcrumbs on pdp when no category attached to product
118
+ ([@JoshuaS98](https://github.com/JoshuaS98))
119
+
120
+ ## 8.0.3-canary.6
121
+
122
+ ### Patch Changes
123
+
124
+ - [#2217](https://github.com/graphcommerce-org/graphcommerce/pull/2217) [`2b750da`](https://github.com/graphcommerce-org/graphcommerce/commit/2b750da77723d37485da01df05f52d19b80cc90c) - fix for breadcrumbs on pdp when no category attached to product
125
+ ([@JoshuaS98](https://github.com/JoshuaS98))
126
+
127
+ ## 8.0.3-canary.5
128
+
129
+ ## 8.0.3-canary.4
130
+
131
+ ## 8.0.3-canary.3
132
+
133
+ ## 8.0.3-canary.2
4
134
 
5
135
  ## 8.0.3-canary.1
6
136
 
@@ -17,6 +17,7 @@ export type AddProductsToCartButtonProps = UseAddProductsToCartActionProps &
17
17
  | 'onClick'
18
18
  | 'sx'
19
19
  | 'children'
20
+ | 'type'
20
21
  >
21
22
 
22
23
  export function AddProductsToCartButton(props: AddProductsToCartButtonProps) {
@@ -90,7 +90,7 @@ export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
90
90
  const requestData = {
91
91
  cartId,
92
92
  cartItems: cartItems
93
- .filter((cartItem) => cartItem.sku)
93
+ .filter((cartItem) => cartItem.sku && cartItem.quantity !== 0)
94
94
  .map(({ customizable_options, ...cartItem }) => {
95
95
  const options = Object.values(customizable_options ?? {})
96
96
  .flat(1)
@@ -103,7 +103,12 @@ export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
103
103
  ...(cartItem.selected_options ?? []).filter(Boolean),
104
104
  ...options,
105
105
  ],
106
- entered_options: cartItem.entered_options?.filter((option) => option?.value),
106
+ entered_options: [
107
+ ...(cartItem.entered_options
108
+ ?.filter((option) => option?.value)
109
+ .filter(nonNullable)
110
+ .map((option) => ({ uid: option.uid, value: `${option?.value}` })) ?? []),
111
+ ],
107
112
  }
108
113
  }),
109
114
  }
@@ -4,14 +4,16 @@ import {
4
4
  Button,
5
5
  ErrorSnackbar,
6
6
  ErrorSnackbarProps,
7
- filterNonNullableKeys,
8
7
  iconChevronRight,
9
8
  IconSvg,
10
9
  MessageSnackbar,
11
10
  MessageSnackbarProps,
11
+ nonNullable,
12
+ useLocale,
12
13
  } from '@graphcommerce/next-ui'
13
14
  import { Trans } from '@lingui/react'
14
- import { useRouter } from 'next/router'
15
+ import { useMemo } from 'react'
16
+ import { findAddedItems } from './findAddedItems'
15
17
  import { toUserErrors } from './toUserErrors'
16
18
  import { useFormAddProductsToCart } from './useFormAddProductsToCart'
17
19
 
@@ -24,9 +26,8 @@ export function AddProductsToCartSnackbar(props: AddProductsToCartSnackbarProps)
24
26
  const { errorSnackbar, successSnackbar } = props
25
27
  const { error, data, redirect, control, submittedVariables } = useFormAddProductsToCart()
26
28
  const formState = useFormState({ control })
27
- const { locale } = useRouter()
28
29
 
29
- const formatter = new Intl.ListFormat(locale, { style: 'long', type: 'conjunction' })
30
+ const formatter = new Intl.ListFormat(useLocale(), { style: 'long', type: 'conjunction' })
30
31
 
31
32
  const userErrors = toUserErrors(data)
32
33
 
@@ -37,14 +38,10 @@ export function AddProductsToCartSnackbar(props: AddProductsToCartSnackbarProps)
37
38
  !userErrors.length &&
38
39
  !redirect
39
40
 
40
- const items = filterNonNullableKeys(data?.addProductsToCart?.cart.items)
41
-
42
- const productsAdded = items
43
- .filter(
44
- (item) =>
45
- submittedVariables?.cartItems?.find((cartItem) => cartItem.sku === item.product.sku),
46
- )
47
- .map((product) => product.product.name || '')
41
+ const addedItems = useMemo(
42
+ () => findAddedItems(data, submittedVariables),
43
+ [data, submittedVariables],
44
+ )
48
45
 
49
46
  const showErrorSnackbar = userErrors.length > 0
50
47
 
@@ -80,13 +77,15 @@ export function AddProductsToCartSnackbar(props: AddProductsToCartSnackbarProps)
80
77
  >
81
78
  <Trans
82
79
  id={
83
- productsAdded.length === 1
80
+ addedItems.length === 1
84
81
  ? '<0>{name}</0> has been added to your shopping cart!'
85
82
  : '<0>{name}</0> have been added to your shopping cart!'
86
83
  }
87
84
  components={{ 0: <strong /> }}
88
85
  values={{
89
- name: formatter.format(productsAdded),
86
+ name: formatter.format(
87
+ addedItems.map((item) => item?.itemInCart?.product.name).filter(nonNullable),
88
+ ),
90
89
  }}
91
90
  />
92
91
  </MessageSnackbar>
@@ -0,0 +1,81 @@
1
+ import { filterNonNullableKeys, isTypename } from '@graphcommerce/next-ui'
2
+ import { AddProductsToCartMutation } from './AddProductsToCart.gql'
3
+ import { AddProductsToCartFields } from './useFormAddProductsToCart'
4
+
5
+ export function findAddedItems(
6
+ data: AddProductsToCartMutation | null | undefined,
7
+ variables: AddProductsToCartFields | null | undefined,
8
+ ) {
9
+ if (!data || !variables) return []
10
+
11
+ return variables.cartItems.map((itemVariable) => ({
12
+ itemVariable,
13
+ itemInCart: data.addProductsToCart?.cart.items?.find((cartItem) => {
14
+ if (!cartItem) return false
15
+
16
+ let isCandidate = cartItem.product.sku === itemVariable.sku
17
+ if (isTypename(cartItem, ['ConfigurableCartItem'])) {
18
+ if (itemVariable.parent_sku) {
19
+ isCandidate =
20
+ cartItem?.product.sku === itemVariable.parent_sku &&
21
+ cartItem.configured_variant.sku === itemVariable.sku
22
+ }
23
+ }
24
+ if (!isCandidate) {
25
+ // console.log("The SKU's dont match, so this isn't the product.")
26
+ return false
27
+ }
28
+
29
+ let selectedOptions = itemVariable.selected_options
30
+ if (isTypename(cartItem, ['ConfigurableCartItem'])) {
31
+ // Check if the requested options match the selected options
32
+ if (
33
+ !cartItem.configurable_options.every((option) => {
34
+ const foundItem = itemVariable.selected_options?.find(
35
+ (selectedOption) => selectedOption === option?.configurable_product_option_value_uid,
36
+ )
37
+
38
+ selectedOptions = itemVariable.selected_options?.filter(
39
+ (selectedOption) => selectedOption !== option?.configurable_product_option_value_uid,
40
+ )
41
+
42
+ return foundItem
43
+ })
44
+ ) {
45
+ // console.log("SKU matche, this isn't the configurable")
46
+ return false
47
+ }
48
+ }
49
+
50
+ const customizable_options = isTypename(cartItem, ['ConfigurableCartItem'])
51
+ ? cartItem.configurable_customizable
52
+ : cartItem.customizable_options
53
+
54
+ const matchEntered = filterNonNullableKeys(itemVariable.entered_options).every(
55
+ (requestOption) =>
56
+ customizable_options.find(
57
+ (itemOption) =>
58
+ itemOption?.customizable_option_uid === requestOption?.uid &&
59
+ itemOption?.values?.find((value) => value?.value === requestOption?.value),
60
+ ),
61
+ )
62
+ if (!matchEntered) {
63
+ // console.log('Entered options do not match', itemVariable.entered_options)
64
+ return false
65
+ }
66
+
67
+ const matchSelected = selectedOptions?.every((requestOption) =>
68
+ customizable_options.find((customizableOption) =>
69
+ customizableOption?.values.find(
70
+ (value) => value?.customizable_option_value_uid === requestOption,
71
+ ),
72
+ ),
73
+ )
74
+ if (!matchSelected) {
75
+ // console.log('Selected options do not match')
76
+ return false
77
+ }
78
+ return true
79
+ }),
80
+ }))
81
+ }
@@ -3,6 +3,9 @@ export * from './AddProductsToCartButton'
3
3
  export * from './AddProductsToCartError'
4
4
  export * from './AddProductsToCartFab'
5
5
  export * from './AddProductsToCartForm'
6
+ export * from './AddProductsToCartSnackbar'
6
7
  export * from './AddProductsToCartQuantity'
7
8
  export * from './useAddProductsToCartAction'
8
9
  export * from './useFormAddProductsToCart'
10
+ export * from './toUserErrors'
11
+ export * from './findAddedItems'
@@ -1,6 +1,6 @@
1
1
  import { useFormState } from '@graphcommerce/ecommerce-ui'
2
2
  import { useEventCallback } from '@mui/material'
3
- import { useEffect, useState } from 'react'
3
+ import { startTransition, useEffect, useState } from 'react'
4
4
  import { UseAddProductsToCartActionFragment } from './UseAddProductsToCartAction.gql'
5
5
  import { toUserErrors } from './toUserErrors'
6
6
  import { AddToCartItemSelector, useFormAddProductsToCart } from './useFormAddProductsToCart'
@@ -68,8 +68,11 @@ export function useAddProductsToCartAction(
68
68
  if (process.env.NODE_ENV !== 'production') {
69
69
  if (!sku) console.warn(`You must provide a 'sku' to useAddProductsToCartAction`)
70
70
  }
71
- setValue(`cartItems.${index}.sku`, sku ?? '')
72
- onClickIncoming?.(e)
71
+ // TODO should be removed, setting the form value on submission isn't a great idea.
72
+ if (!getValues(`cartItems.${index}.sku`)) setValue(`cartItems.${index}.sku`, sku ?? '')
73
+ startTransition(() => {
74
+ onClickIncoming?.(e)
75
+ })
73
76
  }),
74
77
  onMouseDown: useEventCallback((e) => e.stopPropagation()),
75
78
  showSuccess,
@@ -1,11 +1,10 @@
1
1
  import { UseFormGqlMutationReturn } from '@graphcommerce/ecommerce-ui'
2
2
  import { createContext, useContext } from 'react'
3
- import type { LiteralUnion } from 'type-fest'
3
+ import type { Simplify, LiteralUnion } from 'type-fest'
4
4
  import {
5
5
  AddProductsToCartMutation,
6
6
  AddProductsToCartMutationVariables,
7
7
  } from './AddProductsToCart.gql'
8
- import { Simplify } from 'type-fest'
9
8
 
10
9
  export type RedirectType = LiteralUnion<'added' | undefined | false, `/${string}`>
11
10
 
@@ -2,11 +2,10 @@ fragment JsonLdProductOffer on ProductInterface {
2
2
  price_range {
3
3
  minimum_price {
4
4
  regular_price {
5
- value
6
- currency
5
+ ...Money
7
6
  }
8
7
  final_price {
9
- value
8
+ ...Money
10
9
  }
11
10
  }
12
11
  }
@@ -1,12 +1,17 @@
1
1
  import { JsonLd } from '@graphcommerce/next-ui'
2
2
  import { JsonLdProductFragment } from './JsonLdProduct.gql'
3
3
 
4
- type ProductPageJsonLdProps<T extends { '@type': string }> = {
5
- product: JsonLdProductFragment
6
- render: (product: JsonLdProductFragment) => T & { '@context': 'https://schema.org' }
4
+ export type ProductPageJsonLdProps<
5
+ T extends { '@type': string },
6
+ P extends JsonLdProductFragment,
7
+ > = {
8
+ product: P
9
+ render: (product: P) => T & { '@context': 'https://schema.org' }
7
10
  }
8
11
 
9
- export function ProductPageJsonLd<T extends { '@type': string }>(props: ProductPageJsonLdProps<T>) {
12
+ export function ProductPageJsonLd<T extends { '@type': string }, P extends JsonLdProductFragment>(
13
+ props: ProductPageJsonLdProps<T, P>,
14
+ ) {
10
15
  const { product, render } = props
11
16
  return <JsonLd<T> item={render(product)} />
12
17
  }
@@ -1,2 +1,3 @@
1
1
  export * from './jsonLdProduct'
2
2
  export * from './ProductPageJsonLd'
3
+ export * from './JsonLdProduct.gql'
@@ -1,10 +1,10 @@
1
+ import { NumberFieldElement } from '@graphcommerce/ecommerce-ui'
1
2
  import type { ProductInterface } from '@graphcommerce/graphql-mesh'
2
3
  import { ApolloCartErrorAlert, useFormGqlMutationCart } from '@graphcommerce/magento-cart'
3
4
  import { Money, MoneyProps } from '@graphcommerce/magento-store'
4
5
  import {
5
6
  Button,
6
7
  MessageSnackbar,
7
- TextInputNumber,
8
8
  iconChevronRight,
9
9
  IconSvg,
10
10
  extendableComponent,
@@ -40,7 +40,7 @@ export function ProductAddToCart(
40
40
  defaultValues: { ...variables },
41
41
  })
42
42
 
43
- const { handleSubmit, formState, error, muiRegister, required, data } = form
43
+ const { handleSubmit, formState, error, control, required, data } = form
44
44
  const submitHandler = handleSubmit(() => {})
45
45
 
46
46
  return (
@@ -58,15 +58,17 @@ export function ProductAddToCart(
58
58
  <Money {...price} />
59
59
  </Typography>
60
60
 
61
- <TextInputNumber
61
+ <NumberFieldElement
62
62
  variant='outlined'
63
63
  error={formState.isSubmitted && !!formState.errors.quantity}
64
64
  required={required.quantity}
65
65
  inputProps={{ min: 1 }}
66
- {...muiRegister('quantity', { required: required.quantity })}
66
+ name='quantity'
67
+ rules={{ required: required.quantity }}
67
68
  helperText={formState.isSubmitted && formState.errors.quantity?.message}
68
69
  disabled={formState.isSubmitting}
69
70
  size='small'
71
+ control={control}
70
72
  />
71
73
  {children}
72
74
  <Box
@@ -1,7 +1,9 @@
1
1
  import { TextFieldElement } from '@graphcommerce/ecommerce-ui'
2
2
  import { CurrencyEnum } from '@graphcommerce/graphql-mesh'
3
- import { TypeRenderer } from '@graphcommerce/next-ui'
3
+ import { Money } from '@graphcommerce/magento-store'
4
+ import { SectionHeader, TypeRenderer } from '@graphcommerce/next-ui'
4
5
  import { i18n } from '@lingui/core'
6
+ import { Box } from '@mui/material'
5
7
  import React from 'react'
6
8
  import { useFormAddProductsToCart } from '../AddProductsToCart'
7
9
  import { ProductCustomizableFragment } from './ProductCustomizable.gql'
@@ -20,26 +22,58 @@ type CustomizableAreaOptionProps = React.ComponentProps<
20
22
  >
21
23
 
22
24
  export function CustomizableAreaOption(props: CustomizableAreaOptionProps) {
23
- const { uid, areaValue, required, optionIndex, index, title } = props
25
+ const { uid, areaValue, required, optionIndex, index, title, currency, productPrice } = props
24
26
  const maxLength = areaValue?.max_characters ?? undefined
25
- const { control, register } = useFormAddProductsToCart()
27
+ const { control, register, getValues } = useFormAddProductsToCart()
28
+
29
+ if (!areaValue) return null
26
30
 
27
31
  return (
28
- <>
32
+ <Box>
29
33
  <input
30
34
  type='hidden'
31
35
  {...register(`cartItems.${index}.entered_options.${optionIndex}.uid`)}
32
36
  value={uid}
33
37
  />
38
+ <SectionHeader labelLeft={title} sx={{ mt: 0 }} />
34
39
  <TextFieldElement
40
+ sx={{ width: '100%' }}
35
41
  color='primary'
36
42
  multiline
37
43
  minRows={3}
38
44
  control={control}
39
45
  name={`cartItems.${index}.entered_options.${optionIndex}.value`}
40
- label={title}
46
+ InputProps={{
47
+ endAdornment:
48
+ areaValue.price === 0
49
+ ? null
50
+ : areaValue.price && (
51
+ <Box
52
+ sx={{
53
+ display: 'flex',
54
+ typography: 'body1',
55
+ '&.sizeMedium': { typographty: 'subtitle1' },
56
+ '&.sizeLarge': { typography: 'h6' },
57
+ color: getValues(`cartItems.${index}.entered_options.${optionIndex}.value`)
58
+ ? 'text.primary'
59
+ : 'text.secondary',
60
+ }}
61
+ >
62
+ {/* Change fontFamily so the + is properly outlined */}
63
+ <span style={{ fontFamily: 'arial', paddingTop: '1px' }}>+{'\u00A0'}</span>
64
+ <Money
65
+ value={
66
+ areaValue.price_type === 'PERCENT'
67
+ ? productPrice * (areaValue.price / 100)
68
+ : areaValue.price
69
+ }
70
+ currency={currency}
71
+ />
72
+ </Box>
73
+ ),
74
+ }}
41
75
  required={Boolean(required)}
42
- validation={{ maxLength }}
76
+ rules={{ maxLength }}
43
77
  helperText={
44
78
  (maxLength ?? 0) > 0 &&
45
79
  i18n._(/* i18n*/ 'There is a maximum of ‘{maxLength}’ characters', {
@@ -47,6 +81,6 @@ export function CustomizableAreaOption(props: CustomizableAreaOptionProps) {
47
81
  })
48
82
  }
49
83
  />
50
- </>
84
+ </Box>
51
85
  )
52
86
  }
@@ -4,6 +4,7 @@ import { Trans } from '@lingui/react'
4
4
  import { Box } from '@mui/material'
5
5
  import { useFormAddProductsToCart } from '../AddProductsToCart'
6
6
  import { OptionTypeRenderer } from './CustomizableAreaOption'
7
+ import { Money } from '@graphcommerce/magento-store'
7
8
 
8
9
  type CustomizableDateOptionProps = React.ComponentProps<
9
10
  OptionTypeRenderer['CustomizableDateOption']
@@ -21,14 +22,56 @@ export function CustomizableDateOption(props: CustomizableDateOptionProps) {
21
22
  title,
22
23
  minDate = new Date('1950-11-12T00:00'),
23
24
  maxDate = new Date('9999-11-12T00:00'),
25
+ dateValue,
26
+ currency,
27
+ productPrice,
24
28
  } = props
25
- const { register, setValue, setError, getFieldState, clearErrors, control } =
26
- useFormAddProductsToCart()
29
+ const {
30
+ register,
31
+ setValue,
32
+ setError,
33
+ getFieldState,
34
+ clearErrors,
35
+ control,
36
+ resetField,
37
+ getValues,
38
+ } = useFormAddProductsToCart()
27
39
 
28
40
  const { invalid } = getFieldState(`cartItems.${index}.entered_options.${optionIndex}.value`)
29
41
 
30
42
  minDate.setSeconds(0, 0)
31
43
  maxDate.setSeconds(0, 0)
44
+ if (!dateValue) return null
45
+
46
+ const price =
47
+ dateValue.price === 0
48
+ ? null
49
+ : dateValue.price && (
50
+ <Box
51
+ sx={{
52
+ display: 'flex',
53
+ typography: 'body1',
54
+ '&.sizeMedium': { typographty: 'subtitle1' },
55
+ '&.sizeLarge': { typography: 'h6' },
56
+ color:
57
+ dateValue.uid ===
58
+ getValues(`cartItems.${index}.entered_options.${optionIndex}.value`)
59
+ ? 'text.primary'
60
+ : 'text.secondary',
61
+ }}
62
+ >
63
+ {/* Change fontFamily so the + is properly outlined */}
64
+ <span style={{ fontFamily: 'arial' }}>+{'\u00A0'}</span>
65
+ <Money
66
+ value={
67
+ dateValue.price_type === 'PERCENT'
68
+ ? productPrice * (dateValue.price / 100)
69
+ : dateValue.price
70
+ }
71
+ currency={currency}
72
+ />
73
+ </Box>
74
+ )
32
75
  return (
33
76
  <Box>
34
77
  <input
@@ -41,12 +84,20 @@ export function CustomizableDateOption(props: CustomizableDateOptionProps) {
41
84
  <TextFieldElement
42
85
  control={control}
43
86
  name={`cartItems.${index}.entered_options.${optionIndex}.value`}
44
- sx={{ width: '100%' }}
87
+ sx={{
88
+ width: '100%',
89
+ '& input[type="datetime-local"]::-webkit-calendar-picker-indicator': {
90
+ filter: (theme) => (theme.palette.mode === 'dark' ? 'invert(100%)' : 'none'),
91
+ mr: '10px',
92
+ },
93
+ }}
94
+ defaultValue=''
45
95
  required={!!required}
46
96
  error={invalid}
47
97
  helperText={invalid ? <Trans id='Invalid date' /> : ''}
48
98
  type='datetime-local'
49
99
  InputProps={{
100
+ endAdornment: price,
50
101
  inputProps: {
51
102
  min: minDate.toISOString().replace(/:00.000Z/, ''),
52
103
  max: maxDate.toISOString().replace(/:00.000Z/, ''),
@@ -61,10 +112,12 @@ export function CustomizableDateOption(props: CustomizableDateOptionProps) {
61
112
  } else {
62
113
  clearErrors(`cartItems.${index}.entered_options.${optionIndex}.value`)
63
114
  }
64
- setValue(
65
- `cartItems.${index}.entered_options.${optionIndex}.value`,
66
- `${data.currentTarget.value.replace('T', ' ')}:00`,
67
- )
115
+ if (data.currentTarget.value)
116
+ setValue(
117
+ `cartItems.${index}.entered_options.${optionIndex}.value`,
118
+ `${data.currentTarget.value.replace('T', ' ')}:00`,
119
+ )
120
+ else resetField(`cartItems.${index}.entered_options.${optionIndex}.value`)
68
121
  }}
69
122
  />
70
123
  </Box>