@graphcommerce/magento-product 9.1.0-canary.18 → 9.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.
- package/CHANGELOG.md +26 -0
- package/components/AddProductsToCart/AddProductsToCartForm.tsx +20 -45
- package/components/AddProductsToCart/useFormAddProductsToCart.ts +16 -1
- package/components/ProductCustomizable/CustomizableAreaOption.tsx +22 -37
- package/components/ProductCustomizable/CustomizableCheckboxOption.tsx +61 -41
- package/components/ProductCustomizable/CustomizableDateOption.graphql +1 -0
- package/components/ProductCustomizable/CustomizableDateOption.tsx +42 -87
- package/components/ProductCustomizable/CustomizableDropDownOption.tsx +13 -5
- package/components/ProductCustomizable/CustomizableFieldOption.tsx +22 -40
- package/components/ProductCustomizable/CustomizableMultipleOption.tsx +54 -35
- package/components/ProductCustomizable/CustomizablePrice.tsx +40 -0
- package/components/ProductCustomizable/CustomizableRadioOption.tsx +56 -36
- package/components/ProductCustomizable/ProductCustomizable.graphql +1 -0
- package/components/ProductCustomizable/ProductCustomizable.tsx +10 -2
- package/components/ProductCustomizable/productCustomizableSelectors.ts +5 -7
- package/components/ProductListItem/ProductDiscountLabel.tsx +1 -1
- package/components/ProductListItem/ProductListItem.tsx +5 -2
- package/components/ProductListItem/ProductNewLabel.tsx +36 -0
- package/components/ProductListItems/renderer.tsx +1 -1
- package/components/ProductPageDescription/ProductPageDescription.tsx +10 -1
- package/components/ProductPageGallery/ProductPageGallery.tsx +22 -19
- package/components/ProductPageGallery/ProductVideo.graphql +1 -0
- package/components/ProductPageGallery/ProductVideo.tsx +169 -0
- package/components/ProductPageMeta/ProductPageMeta.graphql +1 -0
- package/components/ProductPageMeta/ProductPageMeta.tsx +2 -0
- package/components/ProductPagePrice/ProductPagePrice.graphql +1 -10
- package/components/ProductPagePrice/ProductPrice.graphql +12 -0
- package/components/ProductPagePrice/getProductTierPrice.ts +3 -5
- package/components/ProductPagePrice/useCustomizableOptionPrice.ts +38 -29
- package/{Api → graphql/fragments}/ProductListItem.graphql +2 -0
- package/graphql/index.ts +2 -0
- package/index.ts +1 -2
- package/package.json +13 -13
- /package/{Api → graphql/fragments}/ProductPageItem.graphql +0 -0
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 9.1.0-canary.20
|
4
|
+
|
5
|
+
## 9.1.0-canary.19
|
6
|
+
|
7
|
+
### Patch Changes
|
8
|
+
|
9
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`89e785d`](https://github.com/graphcommerce-org/graphcommerce/commit/89e785de9d62c2f6cf6b2885da72ff63b16fc70d) - Added support for TIME and DATE for the customizable options. Added required stars. ([@paales](https://github.com/paales))
|
10
|
+
|
11
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`7565120`](https://github.com/graphcommerce-org/graphcommerce/commit/756512031642371609258fba322a7f3a4845a17b) - Customizable Product Options wouldn't be properly selected because the parent woudln't rerender anymore. ([@paales](https://github.com/paales))
|
12
|
+
|
13
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`37c00d8`](https://github.com/graphcommerce-org/graphcommerce/commit/37c00d80419b209850457559d7b7eca4101f5705) - Forward productListRenderer for all locations that can be rendered by pagebuilder ([@paales](https://github.com/paales))
|
14
|
+
|
15
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`e9da6a9`](https://github.com/graphcommerce-org/graphcommerce/commit/e9da6a9e55a9344a1f8ef8f1f20060af2bb38ee9) - Added support for video's on the product page. ([@paales](https://github.com/paales))
|
16
|
+
|
17
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`1e38811`](https://github.com/graphcommerce-org/graphcommerce/commit/1e3881177065548165b7141a29cff8ab27692b25) - Added support for meta_keyword for products and categories ([@paales](https://github.com/paales))
|
18
|
+
|
19
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`b497116`](https://github.com/graphcommerce-org/graphcommerce/commit/b497116798e26419950982a6a9d05932a9e99961) - Make sure CustomizableOptions are sorted correctly ([@paales](https://github.com/paales))
|
20
|
+
|
21
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`89e785d`](https://github.com/graphcommerce-org/graphcommerce/commit/89e785de9d62c2f6cf6b2885da72ff63b16fc70d) - Created a CustomizablePrice component that will highlight the price, to prevent duplicating logic and preventing rerenders. ([@paales](https://github.com/paales))
|
22
|
+
|
23
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`722763f`](https://github.com/graphcommerce-org/graphcommerce/commit/722763f01f9c4726126d5a30919bdcd25929a330) - Support for new_from_date and new_to_date labels ([@paales](https://github.com/paales))
|
24
|
+
|
25
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`35fdadd`](https://github.com/graphcommerce-org/graphcommerce/commit/35fdadd8896619a2c84e91e39279f5928c0c9007) - Refactored the price calculation of customizable options on the product page so required options are correctly handled. ([@paales](https://github.com/paales))
|
26
|
+
|
27
|
+
- [#2499](https://github.com/graphcommerce-org/graphcommerce/pull/2499) [`35fdadd`](https://github.com/graphcommerce-org/graphcommerce/commit/35fdadd8896619a2c84e91e39279f5928c0c9007) - Renamed customizable_options_entered to entered_options_record and customizable_options to selected_options_record ([@paales](https://github.com/paales))
|
28
|
+
|
3
29
|
## 9.1.0-canary.18
|
4
30
|
|
5
31
|
## 9.1.0-canary.17
|
@@ -3,7 +3,6 @@ import type { ApolloQueryResult } from '@graphcommerce/graphql'
|
|
3
3
|
import { useApolloClient } from '@graphcommerce/graphql'
|
4
4
|
import type { CrosssellsQuery } from '@graphcommerce/magento-cart'
|
5
5
|
import { CrosssellsDocument, useFormGqlMutationCart } from '@graphcommerce/magento-cart'
|
6
|
-
import type { ErrorSnackbarProps, MessageSnackbarProps } from '@graphcommerce/next-ui'
|
7
6
|
import { nonNullable } from '@graphcommerce/next-ui'
|
8
7
|
import type { SxProps, Theme } from '@mui/material'
|
9
8
|
import { Box } from '@mui/material'
|
@@ -22,13 +21,6 @@ export type AddProductsToCartFormProps = {
|
|
22
21
|
sx?: SxProps<Theme>
|
23
22
|
redirect?: RedirectType
|
24
23
|
snackbarProps?: AddProductsToCartSnackbarProps
|
25
|
-
|
26
|
-
/** @deprecated Use snackbarProps.errorSnackbar instead */
|
27
|
-
errorSnackbar?: Omit<ErrorSnackbarProps, 'open'>
|
28
|
-
/** @deprecated Use snackbarProps.successSnackbar instead */
|
29
|
-
successSnackbar?: Omit<MessageSnackbarProps, 'open' | 'action'>
|
30
|
-
/** @deprecated Use snackbarProps.disableSuccessSnackbar instead */
|
31
|
-
disableSuccessSnackbar?: boolean
|
32
24
|
} & UseFormGraphQlOptions<AddProductsToCartMutation, AddProductsToCartFields>
|
33
25
|
|
34
26
|
const name = 'AddProductsToCartForm'
|
@@ -45,17 +37,7 @@ const name = 'AddProductsToCartForm'
|
|
45
37
|
* - Redirects the user to the cart/checkout/added page after successful submission.
|
46
38
|
*/
|
47
39
|
export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
|
48
|
-
let {
|
49
|
-
children,
|
50
|
-
redirect,
|
51
|
-
onComplete,
|
52
|
-
sx,
|
53
|
-
disableSuccessSnackbar,
|
54
|
-
errorSnackbar,
|
55
|
-
successSnackbar,
|
56
|
-
snackbarProps,
|
57
|
-
...formProps
|
58
|
-
} = props
|
40
|
+
let { children, redirect, onComplete, sx, snackbarProps, ...formProps } = props
|
59
41
|
const router = useRouter()
|
60
42
|
const client = useApolloClient()
|
61
43
|
const crosssellsQuery = useRef<Promise<ApolloQueryResult<CrosssellsQuery>>>()
|
@@ -78,26 +60,24 @@ export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
|
|
78
60
|
cartId,
|
79
61
|
cartItems: cartItems
|
80
62
|
.filter((cartItem) => cartItem.sku && cartItem.quantity !== 0)
|
81
|
-
.map(({
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
}
|
100
|
-
}),
|
63
|
+
.map(({ selected_options_record = {}, entered_options_record = {}, ...cartItem }) => ({
|
64
|
+
...cartItem,
|
65
|
+
quantity: cartItem.quantity || 1,
|
66
|
+
selected_options: [
|
67
|
+
...(cartItem.selected_options ?? []).filter(nonNullable),
|
68
|
+
...Object.values(selected_options_record).flat(1).filter(nonNullable),
|
69
|
+
],
|
70
|
+
entered_options: [
|
71
|
+
...(cartItem.entered_options ?? []).filter(nonNullable),
|
72
|
+
...Object.entries(entered_options_record).map(([uid, value]) => {
|
73
|
+
if (value instanceof Date) {
|
74
|
+
const dateValue = value.toISOString().replace(/.000Z/, '').replace('T', ' ')
|
75
|
+
return { uid, value: dateValue }
|
76
|
+
}
|
77
|
+
return { uid, value: value.toString() }
|
78
|
+
}),
|
79
|
+
],
|
80
|
+
})),
|
101
81
|
}
|
102
82
|
|
103
83
|
const sku = requestData.cartItems[requestData.cartItems.length - 1]?.sku
|
@@ -147,12 +127,7 @@ export function AddProductsToCartForm(props: AddProductsToCartFormProps) {
|
|
147
127
|
<Box component='form' onSubmit={submit} noValidate sx={sx} className={name}>
|
148
128
|
{children}
|
149
129
|
</Box>
|
150
|
-
<AddProductsToCartSnackbar
|
151
|
-
errorSnackbar={errorSnackbar}
|
152
|
-
successSnackbar={successSnackbar}
|
153
|
-
disableSuccessSnackbar={disableSuccessSnackbar}
|
154
|
-
{...snackbarProps}
|
155
|
-
/>
|
130
|
+
<AddProductsToCartSnackbar {...snackbarProps} />
|
156
131
|
</AddProductsToCartContext.Provider>
|
157
132
|
)
|
158
133
|
}
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import type { UseFormGqlMutationReturn } from '@graphcommerce/ecommerce-ui'
|
2
|
+
import type { EnteredOptionInput } from '@graphcommerce/graphql-mesh'
|
2
3
|
import { createContext, useContext } from 'react'
|
3
4
|
import type { LiteralUnion, Simplify } from 'type-fest'
|
4
5
|
import type {
|
@@ -10,7 +11,21 @@ export type RedirectType = LiteralUnion<'added' | undefined | false, `/${string}
|
|
10
11
|
|
11
12
|
type Item = Simplify<
|
12
13
|
AddProductsToCartMutationVariables['cartItems'][number] & {
|
13
|
-
|
14
|
+
/**
|
15
|
+
* The value of the selected_options_record values will be added to the selected_options array.
|
16
|
+
*
|
17
|
+
* This format exists to prevent name collisions and without having to select by index in the
|
18
|
+
* selected_options array.
|
19
|
+
*/
|
20
|
+
selected_options_record?: Record<string, string | string[]>
|
21
|
+
/**
|
22
|
+
* The value of the entered_options_record entries will be coverted to entries for the
|
23
|
+
* entered_options array.
|
24
|
+
*
|
25
|
+
* This format exists to prevent name collisions and without having to select by index in the
|
26
|
+
* entered_options array.
|
27
|
+
*/
|
28
|
+
entered_options_record?: Record<string, string | number | Date>
|
14
29
|
}
|
15
30
|
>
|
16
31
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { TextFieldElement } from '@graphcommerce/ecommerce-ui'
|
1
|
+
import { TextFieldElement, useWatch } from '@graphcommerce/ecommerce-ui'
|
2
2
|
import type { CurrencyEnum } from '@graphcommerce/graphql-mesh'
|
3
3
|
import { Money } from '@graphcommerce/magento-store'
|
4
4
|
import type { TypeRenderer } from '@graphcommerce/next-ui'
|
@@ -7,11 +7,11 @@ import { i18n } from '@lingui/core'
|
|
7
7
|
import { Box } from '@mui/material'
|
8
8
|
import React from 'react'
|
9
9
|
import { useFormAddProductsToCart } from '../AddProductsToCart'
|
10
|
+
import { CustomizablePrice } from './CustomizablePrice'
|
10
11
|
import type { ProductCustomizableFragment } from './ProductCustomizable.gql'
|
11
12
|
|
12
13
|
export type OptionTypeRenderer = TypeRenderer<
|
13
14
|
NonNullable<NonNullable<ProductCustomizableFragment['options']>[number]> & {
|
14
|
-
optionIndex: number
|
15
15
|
index: number
|
16
16
|
currency: CurrencyEnum
|
17
17
|
productPrice: number
|
@@ -23,55 +23,40 @@ export type CustomizableAreaOptionProps = React.ComponentProps<
|
|
23
23
|
>
|
24
24
|
|
25
25
|
export function CustomizableAreaOption(props: CustomizableAreaOptionProps) {
|
26
|
-
const { uid, areaValue, required,
|
26
|
+
const { uid, areaValue, required, index, title, currency, productPrice } = props
|
27
27
|
const maxLength = areaValue?.max_characters ?? undefined
|
28
|
-
const { control
|
28
|
+
const { control } = useFormAddProductsToCart()
|
29
29
|
|
30
|
+
const name = `cartItems.${index}.entered_options_record.${uid}` as const
|
30
31
|
if (!areaValue) return null
|
31
32
|
|
32
33
|
return (
|
33
34
|
<Box>
|
34
|
-
<
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
<SectionHeader
|
36
|
+
labelLeft={
|
37
|
+
<>
|
38
|
+
{title} {required && ' *'}
|
39
|
+
</>
|
40
|
+
}
|
41
|
+
sx={{ mt: 0 }}
|
38
42
|
/>
|
39
|
-
<SectionHeader labelLeft={title} sx={{ mt: 0 }} />
|
40
43
|
<TextFieldElement
|
41
44
|
sx={{ width: '100%' }}
|
42
45
|
color='primary'
|
43
46
|
multiline
|
44
47
|
minRows={3}
|
45
48
|
control={control}
|
46
|
-
name={
|
49
|
+
name={name}
|
47
50
|
InputProps={{
|
48
|
-
endAdornment:
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
'&.sizeLarge': { typography: 'h6' },
|
58
|
-
color: 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', paddingTop: '1px' }}>+{'\u00A0'}</span>
|
65
|
-
<Money
|
66
|
-
value={
|
67
|
-
areaValue.price_type === 'PERCENT'
|
68
|
-
? productPrice * (areaValue.price / 100)
|
69
|
-
: areaValue.price
|
70
|
-
}
|
71
|
-
currency={currency}
|
72
|
-
/>
|
73
|
-
</Box>
|
74
|
-
),
|
51
|
+
endAdornment: (
|
52
|
+
<CustomizablePrice
|
53
|
+
name={name}
|
54
|
+
price_type={areaValue.price_type}
|
55
|
+
productPrice={productPrice}
|
56
|
+
currency={currency}
|
57
|
+
value={areaValue.price}
|
58
|
+
/>
|
59
|
+
),
|
75
60
|
}}
|
76
61
|
required={Boolean(required)}
|
77
62
|
rules={{ maxLength }}
|
@@ -1,25 +1,71 @@
|
|
1
1
|
import { ActionCardListForm } from '@graphcommerce/ecommerce-ui'
|
2
2
|
import { Money } from '@graphcommerce/magento-store'
|
3
3
|
import type { ActionCardProps } from '@graphcommerce/next-ui'
|
4
|
-
import { ActionCard,
|
4
|
+
import { ActionCard, nonNullable, SectionHeader } from '@graphcommerce/next-ui'
|
5
5
|
import { i18n } from '@lingui/core'
|
6
6
|
import { Box, Checkbox } from '@mui/material'
|
7
7
|
import { useFormAddProductsToCart } from '../AddProductsToCart'
|
8
8
|
import type { OptionTypeRenderer } from './CustomizableAreaOption'
|
9
|
+
import type { CustomizableCheckboxOptionFragment } from './CustomizableCheckboxOption.gql'
|
9
10
|
|
10
11
|
export type CustomizableCheckboxOptionProps = React.ComponentProps<
|
11
12
|
OptionTypeRenderer['CustomizableCheckboxOption']
|
12
13
|
>
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
type CheckboxActionCardProps = Pick<CustomizableCheckboxOptionProps, 'productPrice' | 'currency'> &
|
16
|
+
Pick<ActionCardProps, 'value' | 'selected'> & {
|
17
|
+
option: NonNullable<NonNullable<CustomizableCheckboxOptionFragment['checkboxValue']>[number]>
|
18
|
+
}
|
19
|
+
|
20
|
+
function CustomizableCheckboxActionCard(props: CheckboxActionCardProps) {
|
21
|
+
const { productPrice, currency, value, selected, option, ...rest } = props
|
22
|
+
const { title, price, price_type } = option
|
23
|
+
|
24
|
+
return (
|
25
|
+
<ActionCard
|
26
|
+
{...rest}
|
27
|
+
value={value}
|
28
|
+
selected={selected}
|
29
|
+
title={
|
30
|
+
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
31
|
+
<Checkbox checked={selected} /> {title}
|
32
|
+
</Box>
|
33
|
+
}
|
34
|
+
price={
|
35
|
+
price === 0
|
36
|
+
? null
|
37
|
+
: price && (
|
38
|
+
<Box
|
39
|
+
sx={{
|
40
|
+
color: selected ? 'text.primary' : 'text.secondary',
|
41
|
+
}}
|
42
|
+
>
|
43
|
+
<span style={{ fontFamily: 'arial' }}>{'+ '}</span>
|
44
|
+
<Money
|
45
|
+
value={price_type === 'PERCENT' ? productPrice * (price / 100) : price}
|
46
|
+
currency={currency}
|
47
|
+
/>
|
48
|
+
</Box>
|
49
|
+
)
|
50
|
+
}
|
51
|
+
/>
|
52
|
+
)
|
53
|
+
}
|
17
54
|
|
18
|
-
|
55
|
+
export function CustomizableCheckboxOption(props: CustomizableCheckboxOptionProps) {
|
56
|
+
const { uid, required, index, title: label, checkboxValue, productPrice, currency } = props
|
57
|
+
const { control } = useFormAddProductsToCart()
|
19
58
|
|
20
59
|
return (
|
21
60
|
<Box>
|
22
|
-
<SectionHeader
|
61
|
+
<SectionHeader
|
62
|
+
labelLeft={
|
63
|
+
<>
|
64
|
+
{label} {required && ' *'}
|
65
|
+
</>
|
66
|
+
}
|
67
|
+
sx={{ mt: 0 }}
|
68
|
+
/>
|
23
69
|
<ActionCardListForm
|
24
70
|
sx={(theme) => ({
|
25
71
|
mt: theme.spacings.xxs,
|
@@ -31,41 +77,15 @@ export function CustomizableCheckboxOption(props: CustomizableCheckboxOptionProp
|
|
31
77
|
? i18n._(/* i18n*/ 'Please select a value for ‘{label}’', { label })
|
32
78
|
: false,
|
33
79
|
}}
|
34
|
-
render={
|
35
|
-
name={`cartItems.${index}.
|
36
|
-
items={
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
</Box>
|
44
|
-
),
|
45
|
-
price:
|
46
|
-
checkboxVal.price === 0
|
47
|
-
? null
|
48
|
-
: checkboxVal.price && (
|
49
|
-
<Box
|
50
|
-
sx={{
|
51
|
-
color: allSelected.includes(checkboxVal.uid)
|
52
|
-
? 'text.primary'
|
53
|
-
: 'text.secondary',
|
54
|
-
}}
|
55
|
-
>
|
56
|
-
<span style={{ fontFamily: 'arial' }}>{'+ '}</span>
|
57
|
-
<Money
|
58
|
-
value={
|
59
|
-
checkboxVal.price_type === 'PERCENT'
|
60
|
-
? productPrice * (checkboxVal.price / 100)
|
61
|
-
: checkboxVal.price
|
62
|
-
}
|
63
|
-
currency={currency}
|
64
|
-
/>
|
65
|
-
</Box>
|
66
|
-
),
|
67
|
-
}) satisfies ActionCardProps,
|
68
|
-
)}
|
80
|
+
render={CustomizableCheckboxActionCard}
|
81
|
+
name={`cartItems.${index}.selected_options_record.${uid}`}
|
82
|
+
items={(checkboxValue ?? []).filter(nonNullable).map((checkboxVal) => ({
|
83
|
+
productPrice,
|
84
|
+
currency,
|
85
|
+
title: checkboxVal.title ?? '',
|
86
|
+
value: checkboxVal.uid,
|
87
|
+
option: checkboxVal,
|
88
|
+
}))}
|
69
89
|
errorMessage=''
|
70
90
|
/>
|
71
91
|
</Box>
|
@@ -1,10 +1,11 @@
|
|
1
1
|
import { TextFieldElement } from '@graphcommerce/ecommerce-ui'
|
2
|
-
import {
|
2
|
+
import type { CustomizableDateTypeEnum } from '@graphcommerce/graphql-mesh'
|
3
3
|
import { SectionHeader } from '@graphcommerce/next-ui'
|
4
|
-
import {
|
4
|
+
import { t } from '@lingui/macro'
|
5
5
|
import { Box } from '@mui/material'
|
6
6
|
import { useFormAddProductsToCart } from '../AddProductsToCart'
|
7
7
|
import type { OptionTypeRenderer } from './CustomizableAreaOption'
|
8
|
+
import { CustomizablePrice } from './CustomizablePrice'
|
8
9
|
|
9
10
|
export type CustomizableDateOptionProps = React.ComponentProps<
|
10
11
|
OptionTypeRenderer['CustomizableDateOption']
|
@@ -13,111 +14,65 @@ export type CustomizableDateOptionProps = React.ComponentProps<
|
|
13
14
|
maxDate?: Date
|
14
15
|
}
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
minDate = new Date('1950-11-12T00:00'),
|
24
|
-
maxDate = new Date('9999-11-12T00:00'),
|
25
|
-
dateValue,
|
26
|
-
currency,
|
27
|
-
productPrice,
|
28
|
-
} = props
|
29
|
-
const {
|
30
|
-
register,
|
31
|
-
setValue,
|
32
|
-
setError,
|
33
|
-
getFieldState,
|
34
|
-
clearErrors,
|
35
|
-
control,
|
36
|
-
resetField,
|
37
|
-
getValues,
|
38
|
-
} = useFormAddProductsToCart()
|
17
|
+
function getInputType(
|
18
|
+
type: CustomizableDateTypeEnum | null | undefined,
|
19
|
+
): React.HTMLInputTypeAttribute {
|
20
|
+
if (type === 'DATE') return 'date'
|
21
|
+
if (type === 'TIME') return 'time'
|
22
|
+
return 'datetime-local'
|
23
|
+
}
|
39
24
|
|
40
|
-
|
25
|
+
export function CustomizableDateOption(props: CustomizableDateOptionProps) {
|
26
|
+
const { uid, required, index, title, minDate, maxDate, dateValue, currency, productPrice } = props
|
27
|
+
const { control } = useFormAddProductsToCart()
|
41
28
|
|
42
|
-
|
43
|
-
maxDate.setSeconds(0, 0)
|
29
|
+
const name = `cartItems.${index}.entered_options_record.${uid}` as const
|
44
30
|
if (!dateValue) return null
|
45
31
|
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
+
minDate?.setSeconds(0, 0)
|
33
|
+
maxDate?.setSeconds(0, 0)
|
34
|
+
|
75
35
|
return (
|
76
36
|
<Box>
|
77
|
-
<input
|
78
|
-
type='hidden'
|
79
|
-
{...register(`cartItems.${index}.entered_options.${optionIndex}.uid`)}
|
80
|
-
value={uid}
|
81
|
-
/>
|
82
|
-
|
83
37
|
<SectionHeader labelLeft={title} sx={{ mt: 0 }} />
|
84
38
|
<TextFieldElement
|
85
39
|
control={control}
|
86
|
-
name={
|
40
|
+
name={name}
|
87
41
|
sx={{
|
88
42
|
width: '100%',
|
89
|
-
'&
|
43
|
+
'& ::-webkit-calendar-picker-indicator': {
|
90
44
|
filter: (theme) => (theme.palette.mode === 'dark' ? 'invert(100%)' : 'none'),
|
91
45
|
mr: '10px',
|
92
46
|
},
|
93
47
|
}}
|
94
|
-
defaultValue=''
|
95
48
|
required={!!required}
|
96
|
-
|
97
|
-
helperText={invalid ? <Trans id='Invalid date' /> : ''}
|
98
|
-
type='datetime-local'
|
49
|
+
type={getInputType(dateValue.type)}
|
99
50
|
InputProps={{
|
100
|
-
endAdornment:
|
51
|
+
endAdornment: (
|
52
|
+
<CustomizablePrice
|
53
|
+
name={name}
|
54
|
+
price_type={dateValue.price_type}
|
55
|
+
currency={currency}
|
56
|
+
value={dateValue.price}
|
57
|
+
productPrice={productPrice}
|
58
|
+
/>
|
59
|
+
),
|
101
60
|
inputProps: {
|
102
|
-
min: minDate
|
103
|
-
max: maxDate
|
61
|
+
min: minDate?.toISOString().replace(/.000Z/, ''),
|
62
|
+
max: maxDate?.toISOString().replace(/.000Z/, ''),
|
104
63
|
},
|
105
64
|
}}
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
`cartItems.${index}.entered_options.${optionIndex}.value`,
|
118
|
-
`${data.currentTarget.value.replace('T', ' ')}:00`,
|
119
|
-
)
|
120
|
-
else resetField(`cartItems.${index}.entered_options.${optionIndex}.value`)
|
65
|
+
rules={{
|
66
|
+
validate: (value) => {
|
67
|
+
if (!(value instanceof Date)) return true
|
68
|
+
|
69
|
+
if (minDate && value < minDate)
|
70
|
+
return t`Date must be after ${minDate.toLocaleDateString()}`
|
71
|
+
if (maxDate && value > maxDate)
|
72
|
+
return t`Date must be before ${maxDate.toLocaleDateString()}`
|
73
|
+
|
74
|
+
return true
|
75
|
+
},
|
121
76
|
}}
|
122
77
|
/>
|
123
78
|
</Box>
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { useController } from '@graphcommerce/ecommerce-ui'
|
2
2
|
import { Money } from '@graphcommerce/magento-store'
|
3
3
|
import { filterNonNullableKeys, SectionHeader } from '@graphcommerce/next-ui'
|
4
|
+
import { i18n } from '@lingui/core'
|
4
5
|
import { Box, MenuItem, TextField } from '@mui/material'
|
5
6
|
import { useFormAddProductsToCart } from '../AddProductsToCart'
|
6
7
|
import type { OptionTypeRenderer } from './CustomizableAreaOption'
|
@@ -17,9 +18,11 @@ export function CustomizableDropDownOption(props: CustomizableDropDownOptionProp
|
|
17
18
|
field: { onChange, value, ref, ...field },
|
18
19
|
fieldState: { invalid, error },
|
19
20
|
} = useController({
|
20
|
-
name: `cartItems.${index}.
|
21
|
+
name: `cartItems.${index}.selected_options_record.${uid}`,
|
21
22
|
rules: {
|
22
|
-
required:
|
23
|
+
required: required
|
24
|
+
? i18n._(/* i18n*/ 'Please select a value for ‘{label}’', { label: title })
|
25
|
+
: false,
|
23
26
|
},
|
24
27
|
control,
|
25
28
|
defaultValue: '',
|
@@ -27,8 +30,14 @@ export function CustomizableDropDownOption(props: CustomizableDropDownOptionProp
|
|
27
30
|
|
28
31
|
return (
|
29
32
|
<Box>
|
30
|
-
<SectionHeader
|
31
|
-
|
33
|
+
<SectionHeader
|
34
|
+
labelLeft={
|
35
|
+
<>
|
36
|
+
{title} {required && ' *'}
|
37
|
+
</>
|
38
|
+
}
|
39
|
+
sx={{ mt: 0 }}
|
40
|
+
/>
|
32
41
|
<TextField
|
33
42
|
sx={{
|
34
43
|
width: '100%',
|
@@ -44,7 +53,6 @@ export function CustomizableDropDownOption(props: CustomizableDropDownOptionProp
|
|
44
53
|
inputRef={ref}
|
45
54
|
onChange={(event) => onChange(event.target.value)}
|
46
55
|
select
|
47
|
-
required={Boolean(required)}
|
48
56
|
error={invalid}
|
49
57
|
helperText={error?.message}
|
50
58
|
>
|