@graphcommerce/magento-product 8.1.0-canary.3 → 8.1.0-canary.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +97 -2
- package/components/AddProductsToCart/AddProductsToCartSnackbar.tsx +13 -14
- package/components/AddProductsToCart/findAddedItems.ts +81 -0
- package/components/AddProductsToCart/index.ts +3 -0
- package/components/AddProductsToCart/useAddProductsToCartAction.ts +4 -2
- package/components/JsonLdProduct/JsonLdProductOffer.graphql +2 -3
- package/components/JsonLdProduct/ProductPageJsonLd.tsx +9 -4
- package/components/JsonLdProduct/index.ts +1 -0
- package/components/ProductAddToCart/ProductAddToCart.tsx +6 -4
- package/components/ProductCustomizable/CustomizableAreaOption.tsx +41 -7
- package/components/ProductCustomizable/CustomizableDateOption.tsx +60 -7
- package/components/ProductCustomizable/CustomizableDropDownOption.tsx +63 -15
- package/components/ProductCustomizable/CustomizableFieldOption.tsx +40 -4
- package/components/ProductFiltersPro/ProductFiltersPro.tsx +25 -10
- package/components/ProductFiltersPro/ProductFiltersProAllFiltersChip.tsx +6 -2
- package/components/ProductFiltersPro/ProductFiltersProAllFiltersSidebar.tsx +6 -2
- package/components/ProductFiltersPro/ProductFiltersProSortChip.tsx +9 -28
- package/components/ProductFiltersPro/ProductFiltersProSortDirectionArrow.tsx +17 -0
- package/components/ProductFiltersPro/ProductFiltersProSortSection.tsx +7 -32
- package/components/ProductFiltersPro/useProductFiltersProSort.tsx +74 -0
- package/components/ProductListItems/CategoryDefault.graphql +5 -0
- package/components/ProductListItems/ProductListItemsBase.tsx +1 -1
- package/components/ProductListItems/filterTypes.tsx +1 -1
- package/components/ProductListItems/filteredProductList.tsx +1 -1
- package/components/ProductListItems/productListApplyCategoryDefaults.ts +28 -0
- package/components/ProductPageBreadcrumb/ProductPageBreadcrumb.tsx +5 -3
- package/components/ProductPagePrice/ProductPagePrice.graphql +3 -0
- package/components/ProductPagePrice/ProductPagePrice.tsx +11 -4
- package/components/ProductPagePrice/useCustomizableOptionPrice.ts +127 -0
- package/components/index.ts +2 -0
- package/hooks/useProductListLink.ts +10 -5
- package/hooks/useProductListLinkReplace.ts +3 -0
- package/package.json +13 -13
- package/tsconfig.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,8 +1,103 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## 8.1.0-canary.
|
3
|
+
## 8.1.0-canary.6
|
4
4
|
|
5
|
-
## 8.1.0-canary.
|
5
|
+
## 8.1.0-canary.5
|
6
|
+
|
7
|
+
### Patch Changes
|
8
|
+
|
9
|
+
- [#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.
|
10
|
+
([@FrankHarland](https://github.com/FrankHarland))
|
11
|
+
|
12
|
+
## 8.0.6-canary.4
|
13
|
+
|
14
|
+
### Patch Changes
|
15
|
+
|
16
|
+
- [#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.
|
17
|
+
([@FrankHarland](https://github.com/FrankHarland))
|
18
|
+
|
19
|
+
## 8.0.6-canary.3
|
20
|
+
|
21
|
+
## 8.0.6-canary.2
|
22
|
+
|
23
|
+
### Patch Changes
|
24
|
+
|
25
|
+
- [#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
|
26
|
+
([@FrankHarland](https://github.com/FrankHarland))
|
27
|
+
|
28
|
+
- [#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
|
29
|
+
([@FrankHarland](https://github.com/FrankHarland))
|
30
|
+
|
31
|
+
## 8.0.6-canary.1
|
32
|
+
|
33
|
+
## 8.0.6-canary.0
|
34
|
+
|
35
|
+
### Patch Changes
|
36
|
+
|
37
|
+
- [#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.
|
38
|
+
([@paales](https://github.com/paales))
|
39
|
+
|
40
|
+
## 8.0.5
|
41
|
+
|
42
|
+
### Patch Changes
|
43
|
+
|
44
|
+
- [#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
|
45
|
+
([@Jessevdpoel](https://github.com/Jessevdpoel))
|
46
|
+
|
47
|
+
## 8.0.5-canary.10
|
48
|
+
|
49
|
+
## 8.0.5-canary.9
|
50
|
+
|
51
|
+
## 8.0.5-canary.8
|
52
|
+
|
53
|
+
### Patch Changes
|
54
|
+
|
55
|
+
- [#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
|
56
|
+
([@Jessevdpoel](https://github.com/Jessevdpoel))
|
57
|
+
|
58
|
+
## 8.0.5-canary.7
|
59
|
+
|
60
|
+
## 8.0.5-canary.6
|
61
|
+
|
62
|
+
## 8.0.5-canary.5
|
63
|
+
|
64
|
+
## 8.0.5-canary.4
|
65
|
+
|
66
|
+
## 8.0.5-canary.3
|
67
|
+
|
68
|
+
## 8.0.5-canary.2
|
69
|
+
|
70
|
+
## 8.0.5-canary.1
|
71
|
+
|
72
|
+
## 8.0.5-canary.0
|
73
|
+
|
74
|
+
## 8.0.4
|
75
|
+
|
76
|
+
## 8.0.4-canary.1
|
77
|
+
|
78
|
+
## 8.0.4-canary.0
|
79
|
+
|
80
|
+
## 8.0.3
|
81
|
+
|
82
|
+
### Patch Changes
|
83
|
+
|
84
|
+
- [#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
|
85
|
+
([@JoshuaS98](https://github.com/JoshuaS98))
|
86
|
+
|
87
|
+
## 8.0.3-canary.6
|
88
|
+
|
89
|
+
### Patch Changes
|
90
|
+
|
91
|
+
- [#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
|
92
|
+
([@JoshuaS98](https://github.com/JoshuaS98))
|
93
|
+
|
94
|
+
## 8.0.3-canary.5
|
95
|
+
|
96
|
+
## 8.0.3-canary.4
|
97
|
+
|
98
|
+
## 8.0.3-canary.3
|
99
|
+
|
100
|
+
## 8.0.3-canary.2
|
6
101
|
|
7
102
|
## 8.0.3-canary.1
|
8
103
|
|
@@ -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 {
|
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(
|
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
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
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(
|
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
|
-
|
72
|
+
startTransition(() => {
|
73
|
+
onClickIncoming?.(e)
|
74
|
+
})
|
73
75
|
}),
|
74
76
|
onMouseDown: useEventCallback((e) => e.stopPropagation()),
|
75
77
|
showSuccess,
|
@@ -1,12 +1,17 @@
|
|
1
1
|
import { JsonLd } from '@graphcommerce/next-ui'
|
2
2
|
import { JsonLdProductFragment } from './JsonLdProduct.gql'
|
3
3
|
|
4
|
-
type ProductPageJsonLdProps<
|
5
|
-
|
6
|
-
|
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 }>(
|
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,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,
|
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
|
-
<
|
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
|
-
|
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 {
|
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
|
-
|
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
|
-
|
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 {
|
26
|
-
|
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={{
|
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
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
42
|
+
value={value ?? ''}
|
43
|
+
{...field}
|
44
|
+
inputRef={ref}
|
45
|
+
onChange={(event) => onChange(event.target.value)}
|
46
|
+
select
|
24
47
|
required={Boolean(required)}
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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' }}>+ </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
|
}
|