@graphcommerce/magento-product 8.1.0-canary.3 → 8.1.0-canary.30

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 (60) hide show
  1. package/Api/ProductListItem.graphql +1 -1
  2. package/Api/ProductPageItem.graphql +1 -1
  3. package/CHANGELOG.md +155 -2
  4. package/components/AddProductsToCart/AddProductsToCartButton.tsx +1 -0
  5. package/components/AddProductsToCart/AddProductsToCartForm.tsx +7 -2
  6. package/components/AddProductsToCart/AddProductsToCartSnackbar.tsx +13 -14
  7. package/components/AddProductsToCart/UseAddProductsToCartAction.graphql +1 -1
  8. package/components/AddProductsToCart/findAddedItems.ts +81 -0
  9. package/components/AddProductsToCart/index.ts +3 -0
  10. package/components/AddProductsToCart/useAddProductsToCartAction.ts +6 -3
  11. package/components/AddProductsToCart/useFormAddProductsToCart.ts +1 -2
  12. package/components/JsonLdProduct/JsonLdProduct.graphql +1 -1
  13. package/components/JsonLdProduct/JsonLdProductOffer.graphql +2 -3
  14. package/components/JsonLdProduct/ProductPageJsonLd.tsx +10 -5
  15. package/components/JsonLdProduct/index.ts +1 -0
  16. package/components/ProductAddToCart/ProductAddToCart.tsx +6 -4
  17. package/components/ProductCustomizable/CustomizableAreaOption.tsx +41 -7
  18. package/components/ProductCustomizable/CustomizableDateOption.tsx +60 -7
  19. package/components/ProductCustomizable/CustomizableDropDownOption.tsx +63 -15
  20. package/components/ProductCustomizable/CustomizableFieldOption.tsx +40 -4
  21. package/components/ProductCustomizable/ProductCustomizable.graphql +1 -1
  22. package/components/ProductCustomizable/index.ts +1 -0
  23. package/components/ProductCustomizable/productCustomizableSelectors.ts +59 -0
  24. package/components/ProductFiltersPro/ProductFiltersPro.tsx +49 -12
  25. package/components/ProductFiltersPro/ProductFiltersProAggregations.tsx +14 -0
  26. package/components/ProductFiltersPro/ProductFiltersProAllFiltersChip.tsx +10 -10
  27. package/components/ProductFiltersPro/ProductFiltersProAllFiltersSidebar.tsx +23 -9
  28. package/components/ProductFiltersPro/ProductFiltersProCategorySection.tsx +70 -0
  29. package/components/ProductFiltersPro/ProductFiltersProChips.tsx +10 -8
  30. package/components/ProductFiltersPro/ProductFiltersProLayoutSidebar.tsx +15 -7
  31. package/components/ProductFiltersPro/ProductFiltersProLimitSection.tsx +4 -1
  32. package/components/ProductFiltersPro/ProductFiltersProSortChip.tsx +9 -28
  33. package/components/ProductFiltersPro/ProductFiltersProSortDirectionArrow.tsx +15 -0
  34. package/components/ProductFiltersPro/ProductFiltersProSortSection.tsx +12 -32
  35. package/components/ProductFiltersPro/index.ts +6 -0
  36. package/components/ProductFiltersPro/useProductFiltersProSort.tsx +76 -0
  37. package/components/ProductListFiltersContainer/ProductListFiltersContainer.tsx +2 -4
  38. package/components/ProductListItems/CategoryDefault.graphql +5 -0
  39. package/components/ProductListItems/ProductListItemsBase.tsx +1 -1
  40. package/components/ProductListItems/filterTypes.tsx +1 -1
  41. package/components/ProductListItems/filteredProductList.tsx +1 -1
  42. package/components/ProductListItems/productListApplyCategoryDefaults.ts +28 -0
  43. package/components/ProductPageBreadcrumb/ProductPageBreadcrumb.graphql +3 -0
  44. package/components/ProductPageBreadcrumb/ProductPageBreadcrumb.tsx +8 -3
  45. package/components/ProductPageBreadcrumb/ProductPageBreadcrumbs.tsx +40 -0
  46. package/components/ProductPageBreadcrumb/index.ts +1 -0
  47. package/components/ProductPageDescription/ComplexTextValue.graphql +1 -1
  48. package/components/ProductPageGallery/ProductImage.graphql +1 -0
  49. package/components/ProductPageGallery/ProductPageGallery.tsx +14 -8
  50. package/components/ProductPagePrice/ProductPagePrice.graphql +3 -0
  51. package/components/ProductPagePrice/ProductPagePrice.tsx +11 -4
  52. package/components/ProductPagePrice/useCustomizableOptionPrice.ts +85 -0
  53. package/components/ProductShortDescription/ProductShortDescription.tsx +2 -0
  54. package/components/ProductStaticPaths/getProductStaticPaths.ts +2 -3
  55. package/components/ProductStaticPaths/getSitemapPaths.ts +3 -0
  56. package/components/index.ts +2 -0
  57. package/hooks/useProductListLink.ts +10 -5
  58. package/hooks/useProductListLinkReplace.ts +3 -0
  59. package/package.json +14 -13
  60. package/tsconfig.json +1 -1
@@ -1,4 +1,4 @@
1
- fragment ProductListItem on ProductInterface @injectable {
1
+ fragment ProductListItem on ProductInterface {
2
2
  uid
3
3
  ...ProductLink
4
4
  sku
@@ -1,4 +1,4 @@
1
- fragment ProductPageItem on ProductInterface @injectable {
1
+ fragment ProductPageItem on ProductInterface {
2
2
  __typename
3
3
  uid
4
4
  ...ProductListItem
package/CHANGELOG.md CHANGED
@@ -1,8 +1,161 @@
1
1
  # Change Log
2
2
 
3
- ## 8.1.0-canary.3
3
+ ## 8.1.0-canary.30
4
4
 
5
- ## 8.1.0-canary.2
5
+ ## 8.1.0-canary.29
6
+
7
+ ## 8.1.0-canary.28
8
+
9
+ ## 8.1.0-canary.27
10
+
11
+ ### Minor Changes
12
+
13
+ - [#2273](https://github.com/graphcommerce-org/graphcommerce/pull/2273) [`77955c5`](https://github.com/graphcommerce-org/graphcommerce/commit/77955c56ac8633ab1c5e0f3ddb25e3a87236e2bb) - Improve Breadcrumbs on Category and Product pages
14
+ ([@Jessevdpoel](https://github.com/Jessevdpoel))
15
+
16
+ ## 8.1.0-canary.26
17
+
18
+ ## 8.1.0-canary.25
19
+
20
+ ## 8.1.0-canary.24
21
+
22
+ ## 8.1.0-canary.23
23
+
24
+ ## 8.1.0-canary.22
25
+
26
+ ## 8.1.0-canary.21
27
+
28
+ ## 8.1.0-canary.20
29
+
30
+ ## 8.1.0-canary.19
31
+
32
+ ## 8.1.0-canary.18
33
+
34
+ ## 8.1.0-canary.17
35
+
36
+ ## 8.1.0-canary.16
37
+
38
+ ## 8.1.0-canary.15
39
+
40
+ ## 8.1.0-canary.14
41
+
42
+ ## 8.1.0-canary.13
43
+
44
+ ## 8.1.0-canary.12
45
+
46
+ ## 8.1.0-canary.11
47
+
48
+ ### Patch Changes
49
+
50
+ - [#2255](https://github.com/graphcommerce-org/graphcommerce/pull/2255) [`679d07d`](https://github.com/graphcommerce-org/graphcommerce/commit/679d07dad5b5e2dab3c0f3d537716b6115af8ef7) - make the ProductPageMeta fragment injectable
51
+ ([@carlocarels90](https://github.com/carlocarels90))
52
+
53
+ ## 8.1.0-canary.10
54
+
55
+ ## 8.1.0-canary.9
56
+
57
+ ## 8.1.0-canary.8
58
+
59
+ ## 8.1.0-canary.7
60
+
61
+ ## 8.1.0-canary.6
62
+
63
+ ## 8.1.0-canary.5
64
+
65
+ ### Patch Changes
66
+
67
+ - [#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.
68
+ ([@FrankHarland](https://github.com/FrankHarland))
69
+
70
+ ## 8.0.6-canary.4
71
+
72
+ ### Patch Changes
73
+
74
+ - [#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.
75
+ ([@FrankHarland](https://github.com/FrankHarland))
76
+
77
+ ## 8.0.6-canary.3
78
+
79
+ ## 8.0.6-canary.2
80
+
81
+ ### Patch Changes
82
+
83
+ - [#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
84
+ ([@FrankHarland](https://github.com/FrankHarland))
85
+
86
+ - [#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
87
+ ([@FrankHarland](https://github.com/FrankHarland))
88
+
89
+ ## 8.0.6-canary.1
90
+
91
+ ## 8.0.6-canary.0
92
+
93
+ ### Patch Changes
94
+
95
+ - [#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.
96
+ ([@paales](https://github.com/paales))
97
+
98
+ ## 8.0.5
99
+
100
+ ### Patch Changes
101
+
102
+ - [#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
103
+ ([@Jessevdpoel](https://github.com/Jessevdpoel))
104
+
105
+ ## 8.0.5-canary.10
106
+
107
+ ## 8.0.5-canary.9
108
+
109
+ ## 8.0.5-canary.8
110
+
111
+ ### Patch Changes
112
+
113
+ - [#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
114
+ ([@Jessevdpoel](https://github.com/Jessevdpoel))
115
+
116
+ ## 8.0.5-canary.7
117
+
118
+ ## 8.0.5-canary.6
119
+
120
+ ## 8.0.5-canary.5
121
+
122
+ ## 8.0.5-canary.4
123
+
124
+ ## 8.0.5-canary.3
125
+
126
+ ## 8.0.5-canary.2
127
+
128
+ ## 8.0.5-canary.1
129
+
130
+ ## 8.0.5-canary.0
131
+
132
+ ## 8.0.4
133
+
134
+ ## 8.0.4-canary.1
135
+
136
+ ## 8.0.4-canary.0
137
+
138
+ ## 8.0.3
139
+
140
+ ### Patch Changes
141
+
142
+ - [#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
143
+ ([@JoshuaS98](https://github.com/JoshuaS98))
144
+
145
+ ## 8.0.3-canary.6
146
+
147
+ ### Patch Changes
148
+
149
+ - [#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
150
+ ([@JoshuaS98](https://github.com/JoshuaS98))
151
+
152
+ ## 8.0.3-canary.5
153
+
154
+ ## 8.0.3-canary.4
155
+
156
+ ## 8.0.3-canary.3
157
+
158
+ ## 8.0.3-canary.2
6
159
 
7
160
  ## 8.0.3-canary.1
8
161
 
@@ -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>
@@ -1,4 +1,4 @@
1
- fragment UseAddProductsToCartAction on ProductInterface @injectable {
1
+ fragment UseAddProductsToCartAction on ProductInterface {
2
2
  uid
3
3
  sku
4
4
  only_x_left_in_stock
@@ -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
 
@@ -1,4 +1,4 @@
1
- fragment JsonLdProduct on ProductInterface @inject(into: ["ProductPageItem"]) @injectable {
1
+ fragment JsonLdProduct on ProductInterface @inject(into: ["ProductPageItem"]) {
2
2
  uid
3
3
  name
4
4
  sku
@@ -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
- return <JsonLd<T> item={render(product)} />
16
+ return <JsonLd<T> item={render(product)} keyVal='product-jsonld' />
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
  }