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

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 (34) hide show
  1. package/CHANGELOG.md +96 -1
  2. package/components/AddProductsToCart/AddProductsToCartSnackbar.tsx +13 -14
  3. package/components/AddProductsToCart/findAddedItems.ts +81 -0
  4. package/components/AddProductsToCart/index.ts +3 -0
  5. package/components/AddProductsToCart/useAddProductsToCartAction.ts +4 -2
  6. package/components/JsonLdProduct/JsonLdProductOffer.graphql +2 -3
  7. package/components/JsonLdProduct/ProductPageJsonLd.tsx +9 -4
  8. package/components/JsonLdProduct/index.ts +1 -0
  9. package/components/ProductAddToCart/ProductAddToCart.tsx +6 -4
  10. package/components/ProductCustomizable/CustomizableAreaOption.tsx +41 -7
  11. package/components/ProductCustomizable/CustomizableDateOption.tsx +60 -7
  12. package/components/ProductCustomizable/CustomizableDropDownOption.tsx +63 -15
  13. package/components/ProductCustomizable/CustomizableFieldOption.tsx +40 -4
  14. package/components/ProductFiltersPro/ProductFiltersPro.tsx +25 -10
  15. package/components/ProductFiltersPro/ProductFiltersProAllFiltersChip.tsx +6 -2
  16. package/components/ProductFiltersPro/ProductFiltersProAllFiltersSidebar.tsx +6 -2
  17. package/components/ProductFiltersPro/ProductFiltersProSortChip.tsx +9 -28
  18. package/components/ProductFiltersPro/ProductFiltersProSortDirectionArrow.tsx +17 -0
  19. package/components/ProductFiltersPro/ProductFiltersProSortSection.tsx +7 -32
  20. package/components/ProductFiltersPro/useProductFiltersProSort.tsx +74 -0
  21. package/components/ProductListItems/CategoryDefault.graphql +5 -0
  22. package/components/ProductListItems/ProductListItemsBase.tsx +1 -1
  23. package/components/ProductListItems/filterTypes.tsx +1 -1
  24. package/components/ProductListItems/filteredProductList.tsx +1 -1
  25. package/components/ProductListItems/productListApplyCategoryDefaults.ts +28 -0
  26. package/components/ProductPageBreadcrumb/ProductPageBreadcrumb.tsx +5 -3
  27. package/components/ProductPagePrice/ProductPagePrice.graphql +3 -0
  28. package/components/ProductPagePrice/ProductPagePrice.tsx +11 -4
  29. package/components/ProductPagePrice/useCustomizableOptionPrice.ts +127 -0
  30. package/components/index.ts +2 -0
  31. package/hooks/useProductListLink.ts +10 -5
  32. package/hooks/useProductListLinkReplace.ts +3 -0
  33. package/package.json +13 -13
  34. package/tsconfig.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,6 +1,101 @@
1
1
  # Change Log
2
2
 
3
- ## 8.1.0-canary.2
3
+ ## 8.1.0-canary.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#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.
8
+ ([@FrankHarland](https://github.com/FrankHarland))
9
+
10
+ ## 8.0.6-canary.4
11
+
12
+ ### Patch Changes
13
+
14
+ - [#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.
15
+ ([@FrankHarland](https://github.com/FrankHarland))
16
+
17
+ ## 8.0.6-canary.3
18
+
19
+ ## 8.0.6-canary.2
20
+
21
+ ### Patch Changes
22
+
23
+ - [#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
24
+ ([@FrankHarland](https://github.com/FrankHarland))
25
+
26
+ - [#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
27
+ ([@FrankHarland](https://github.com/FrankHarland))
28
+
29
+ ## 8.0.6-canary.1
30
+
31
+ ## 8.0.6-canary.0
32
+
33
+ ### Patch Changes
34
+
35
+ - [#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.
36
+ ([@paales](https://github.com/paales))
37
+
38
+ ## 8.0.5
39
+
40
+ ### Patch Changes
41
+
42
+ - [#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
43
+ ([@Jessevdpoel](https://github.com/Jessevdpoel))
44
+
45
+ ## 8.0.5-canary.10
46
+
47
+ ## 8.0.5-canary.9
48
+
49
+ ## 8.0.5-canary.8
50
+
51
+ ### Patch Changes
52
+
53
+ - [#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
54
+ ([@Jessevdpoel](https://github.com/Jessevdpoel))
55
+
56
+ ## 8.0.5-canary.7
57
+
58
+ ## 8.0.5-canary.6
59
+
60
+ ## 8.0.5-canary.5
61
+
62
+ ## 8.0.5-canary.4
63
+
64
+ ## 8.0.5-canary.3
65
+
66
+ ## 8.0.5-canary.2
67
+
68
+ ## 8.0.5-canary.1
69
+
70
+ ## 8.0.5-canary.0
71
+
72
+ ## 8.0.4
73
+
74
+ ## 8.0.4-canary.1
75
+
76
+ ## 8.0.4-canary.0
77
+
78
+ ## 8.0.3
79
+
80
+ ### Patch Changes
81
+
82
+ - [#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
83
+ ([@JoshuaS98](https://github.com/JoshuaS98))
84
+
85
+ ## 8.0.3-canary.6
86
+
87
+ ### Patch Changes
88
+
89
+ - [#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
90
+ ([@JoshuaS98](https://github.com/JoshuaS98))
91
+
92
+ ## 8.0.3-canary.5
93
+
94
+ ## 8.0.3-canary.4
95
+
96
+ ## 8.0.3-canary.3
97
+
98
+ ## 8.0.3-canary.2
4
99
 
5
100
  ## 8.0.3-canary.1
6
101
 
@@ -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'
@@ -69,7 +69,9 @@ export function useAddProductsToCartAction(
69
69
  if (!sku) console.warn(`You must provide a 'sku' to useAddProductsToCartAction`)
70
70
  }
71
71
  setValue(`cartItems.${index}.sku`, sku ?? '')
72
- onClickIncoming?.(e)
72
+ startTransition(() => {
73
+ onClickIncoming?.(e)
74
+ })
73
75
  }),
74
76
  onMouseDown: useEventCallback((e) => e.stopPropagation()),
75
77
  showSuccess,
@@ -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>
@@ -1,33 +1,81 @@
1
- import { SelectElement } from '@graphcommerce/ecommerce-ui'
1
+ import { SelectElement, useController } from '@graphcommerce/ecommerce-ui'
2
2
  import { SectionHeader, filterNonNullableKeys } from '@graphcommerce/next-ui'
3
- import { Box } from '@mui/material'
3
+ import { Box, ListItemText, MenuItem, TextField, Typography } from '@mui/material'
4
4
  import { useFormAddProductsToCart } from '../AddProductsToCart'
5
5
  import { OptionTypeRenderer } from './CustomizableAreaOption'
6
+ import { Money } from '@graphcommerce/magento-store'
6
7
 
7
8
  type CustomizableDropDownOptionProps = React.ComponentProps<
8
9
  OptionTypeRenderer['CustomizableDropDownOption']
9
10
  >
10
11
 
11
12
  export function CustomizableDropDownOption(props: CustomizableDropDownOptionProps) {
12
- const { uid, required, index, title, dropdownValue } = props
13
- const { control } = useFormAddProductsToCart()
13
+ const { uid, required, index, title, dropdownValue, productPrice, currency } = props
14
+ const { control, getValues } = useFormAddProductsToCart()
15
+
16
+ const {
17
+ field: { onChange, value, ref, ...field },
18
+ fieldState: { invalid, error },
19
+ } = useController({
20
+ name: `cartItems.${index}.customizable_options.${uid}`,
21
+ rules: {
22
+ required: Boolean(required),
23
+ },
24
+ control,
25
+ defaultValue: '',
26
+ })
14
27
 
15
28
  return (
16
29
  <Box>
17
30
  <SectionHeader labelLeft={title} sx={{ mt: 0 }} />
18
- <SelectElement
19
- sx={{ width: '100%' }}
31
+
32
+ <TextField
33
+ sx={{
34
+ width: '100%',
35
+ '& .MuiSelect-select': {
36
+ display: 'flex',
37
+ justifyContent: 'space-between',
38
+ alignItems: 'center',
39
+ },
40
+ }}
20
41
  color='primary'
21
- control={control}
22
- name={`cartItems.${index}.customizable_options.${uid}`}
23
- label={title}
42
+ value={value ?? ''}
43
+ {...field}
44
+ inputRef={ref}
45
+ onChange={(event) => onChange(event.target.value)}
46
+ select
24
47
  required={Boolean(required)}
25
- defaultValue=''
26
- options={filterNonNullableKeys(dropdownValue, ['title']).map((option) => ({
27
- id: option.uid,
28
- label: option.title,
29
- }))}
30
- />
48
+ error={invalid}
49
+ helperText={error?.message}
50
+ >
51
+ {filterNonNullableKeys(dropdownValue, ['title']).map((option) => (
52
+ <MenuItem key={option.uid} value={option.uid}>
53
+ <Box>{option.title}</Box>
54
+
55
+ {option.price ? (
56
+ <Box
57
+ sx={{
58
+ // display: 'flex',
59
+ typography: 'body1',
60
+ '&.sizeMedium': { typographty: 'subtitle1' },
61
+ '&.sizeLarge': { typography: 'h6' },
62
+ color: option.uid === value ? 'text.primary' : 'text.secondary',
63
+ }}
64
+ >
65
+ <span style={{ fontFamily: 'arial', paddingTop: '1px' }}>+&nbsp;</span>
66
+ <Money
67
+ value={
68
+ option.price_type === 'PERCENT'
69
+ ? productPrice * (option.price / 100)
70
+ : option.price
71
+ }
72
+ currency={currency}
73
+ />
74
+ </Box>
75
+ ) : null}
76
+ </MenuItem>
77
+ ))}
78
+ </TextField>
31
79
  </Box>
32
80
  )
33
81
  }