@graphcommerce/magento-cart-items 8.1.0-canary.19 → 8.1.0-canary.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/components/CartItem/CartItem.tsx +1 -4
- package/components/CartItemActionCard/CartItemActionCard.tsx +43 -58
- package/components/CartItemsActionCards/CartItemsActionCards.tsx +6 -0
- package/components/EditCartItem/EditCartItemButton/EditCartItemButton.tsx +15 -0
- package/components/EditCartItem/EditCartItemForm/EditCartItemForm.graphql +8 -0
- package/components/EditCartItem/EditCartItemForm/EditCartItemForm.tsx +59 -0
- package/components/EditCartItem/index.ts +3 -0
- package/components/RemoveItemFromCart/RemoveItemFromCart.tsx +8 -9
- package/components/RemoveItemFromCart/RemoveItemFromCartFab.tsx +9 -8
- package/components/SelectedCustomizableOptions/SelectedCustomizableOptions.tsx +5 -14
- package/hooks/useRemoveItemFromCart.ts +28 -0
- package/index.ts +2 -0
- package/package.json +13 -12
- package/utils/cartItemToCartItemInput.ts +117 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 8.1.0-canary.20
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#2246](https://github.com/graphcommerce-org/graphcommerce/pull/2246) [`13524f9`](https://github.com/graphcommerce-org/graphcommerce/commit/13524f991a810c1679db49b3b8b4f04f90d0d6c1) - Added the ability to edit cart items with full support for all product types and custom options
|
|
8
|
+
([@Jessevdpoel](https://github.com/Jessevdpoel))
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- [#2246](https://github.com/graphcommerce-org/graphcommerce/pull/2246) [`fc5c04d`](https://github.com/graphcommerce-org/graphcommerce/commit/fc5c04d4a2c0301be7d3cc983d9b31f6fcaf6fe6) - Create useRemoveItemFromCart hook to allow for reuse while keeping compatibility with plugins.
|
|
13
|
+
([@Jessevdpoel](https://github.com/Jessevdpoel))
|
|
14
|
+
|
|
3
15
|
## 8.1.0-canary.19
|
|
4
16
|
|
|
5
17
|
## 8.1.0-canary.18
|
|
@@ -93,10 +93,7 @@ export function CartItem(props: CartItemProps) {
|
|
|
93
93
|
color='default'
|
|
94
94
|
badgeContent={
|
|
95
95
|
<RemoveItemFromCartFab
|
|
96
|
-
|
|
97
|
-
quantity={quantity}
|
|
98
|
-
prices={prices}
|
|
99
|
-
product={product}
|
|
96
|
+
{...props}
|
|
100
97
|
fabProps={{ className: classes.badge }}
|
|
101
98
|
sx={(theme) => ({
|
|
102
99
|
'& > button': {
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { Image } from '@graphcommerce/image'
|
|
2
2
|
import { useDisplayInclTax } from '@graphcommerce/magento-cart/hooks'
|
|
3
|
+
import { ProductLinkProps } from '@graphcommerce/magento-product'
|
|
3
4
|
import { Money } from '@graphcommerce/magento-store'
|
|
4
5
|
import {
|
|
5
6
|
ActionCard,
|
|
6
7
|
ActionCardProps,
|
|
7
|
-
responsiveVal,
|
|
8
8
|
filterNonNullableKeys,
|
|
9
|
+
actionCardImageSizes,
|
|
9
10
|
} from '@graphcommerce/next-ui'
|
|
10
|
-
import {
|
|
11
|
+
import { Trans } from '@lingui/react'
|
|
12
|
+
import { Box, Button, Link } from '@mui/material'
|
|
11
13
|
import { CartItemFragment } from '../../Api/CartItem.gql'
|
|
12
14
|
import { RemoveItemFromCart } from '../RemoveItemFromCart/RemoveItemFromCart'
|
|
13
15
|
import { UpdateItemQuantity } from '../UpdateItemQuantity/UpdateItemQuantity'
|
|
@@ -17,16 +19,8 @@ export type CartItemActionCardProps = { cartItem: CartItemFragment; readOnly?: b
|
|
|
17
19
|
'value' | 'image' | 'price' | 'title' | 'action'
|
|
18
20
|
>
|
|
19
21
|
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
medium: responsiveVal(60, 80),
|
|
23
|
-
large: responsiveVal(100, 120),
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const typographySizes = {
|
|
27
|
-
small: 'body2',
|
|
28
|
-
medium: 'body1',
|
|
29
|
-
large: 'subtitle1',
|
|
22
|
+
export function productEditLink(link: ProductLinkProps) {
|
|
23
|
+
return `/checkout/item/${link.url_key}`
|
|
30
24
|
}
|
|
31
25
|
|
|
32
26
|
export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
@@ -49,6 +43,11 @@ export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
|
49
43
|
price = prices?.price.value
|
|
50
44
|
}
|
|
51
45
|
|
|
46
|
+
const hasOptions = !(
|
|
47
|
+
(cartItem.__typename === 'SimpleCartItem' || cartItem.__typename === 'VirtualCartItem') &&
|
|
48
|
+
cartItem.customizable_options.length === 0
|
|
49
|
+
)
|
|
50
|
+
|
|
52
51
|
return (
|
|
53
52
|
<ActionCard
|
|
54
53
|
value={uid}
|
|
@@ -58,7 +57,7 @@ export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
|
58
57
|
px: 0,
|
|
59
58
|
py: theme.spacings.xs,
|
|
60
59
|
},
|
|
61
|
-
'& .
|
|
60
|
+
'& .ActionCard-rootInner': {
|
|
62
61
|
justifyContent: 'space-between',
|
|
63
62
|
alignItems: 'stretch',
|
|
64
63
|
},
|
|
@@ -66,9 +65,7 @@ export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
|
66
65
|
px: 0,
|
|
67
66
|
},
|
|
68
67
|
'&.sizeResponsive': {
|
|
69
|
-
[theme.breakpoints.down('md')]: {
|
|
70
|
-
px: 0,
|
|
71
|
-
},
|
|
68
|
+
[theme.breakpoints.down('md')]: { px: 0 },
|
|
72
69
|
},
|
|
73
70
|
'& .ActionCard-end': {
|
|
74
71
|
justifyContent: readOnly ? 'center' : 'space-between',
|
|
@@ -80,30 +77,11 @@ export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
|
80
77
|
alignSelf: 'flex-start',
|
|
81
78
|
},
|
|
82
79
|
'& .ActionCard-secondaryAction': {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
xs: typographySizes.small,
|
|
87
|
-
md: typographySizes.medium,
|
|
88
|
-
lg: typographySizes.large,
|
|
89
|
-
}
|
|
90
|
-
: typographySizes[size],
|
|
91
|
-
display: 'flex',
|
|
92
|
-
alignItems: 'center',
|
|
93
|
-
color: 'text.secondary',
|
|
94
|
-
mt: 1,
|
|
95
|
-
gap: '10px',
|
|
96
|
-
justifyContent: 'start',
|
|
80
|
+
display: 'grid',
|
|
81
|
+
rowGap: theme.spacings.xs,
|
|
82
|
+
justifyItems: 'start',
|
|
97
83
|
},
|
|
98
84
|
'& .ActionCard-price': {
|
|
99
|
-
typography:
|
|
100
|
-
size === 'responsive'
|
|
101
|
-
? {
|
|
102
|
-
xs: typographySizes.small,
|
|
103
|
-
md: typographySizes.medium,
|
|
104
|
-
lg: typographySizes.large,
|
|
105
|
-
}
|
|
106
|
-
: typographySizes[size],
|
|
107
85
|
pr: readOnly ? 0 : theme.spacings.xs,
|
|
108
86
|
mb: { xs: 0.5, sm: 0 },
|
|
109
87
|
},
|
|
@@ -116,27 +94,13 @@ export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
|
116
94
|
layout='fill'
|
|
117
95
|
src={thumbnail?.url}
|
|
118
96
|
sx={{
|
|
119
|
-
width:
|
|
120
|
-
|
|
121
|
-
? {
|
|
122
|
-
xs: productImageSizes.small,
|
|
123
|
-
md: productImageSizes.medium,
|
|
124
|
-
lg: productImageSizes.large,
|
|
125
|
-
}
|
|
126
|
-
: productImageSizes[size],
|
|
127
|
-
height:
|
|
128
|
-
size === 'responsive'
|
|
129
|
-
? {
|
|
130
|
-
xs: productImageSizes.small,
|
|
131
|
-
md: productImageSizes.medium,
|
|
132
|
-
lg: productImageSizes.large,
|
|
133
|
-
}
|
|
134
|
-
: productImageSizes[size],
|
|
97
|
+
width: actionCardImageSizes[size],
|
|
98
|
+
height: actionCardImageSizes[size],
|
|
135
99
|
display: 'block',
|
|
136
100
|
borderRadius: 1,
|
|
137
101
|
objectFit: 'contain',
|
|
138
102
|
}}
|
|
139
|
-
sizes={
|
|
103
|
+
sizes={actionCardImageSizes[size]}
|
|
140
104
|
/>
|
|
141
105
|
)
|
|
142
106
|
}
|
|
@@ -159,10 +123,30 @@ export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
|
159
123
|
}
|
|
160
124
|
secondaryAction={
|
|
161
125
|
<>
|
|
162
|
-
|
|
163
|
-
|
|
126
|
+
<Box
|
|
127
|
+
sx={{
|
|
128
|
+
display: 'flex',
|
|
129
|
+
alignItems: 'center',
|
|
130
|
+
color: 'text.secondary',
|
|
131
|
+
mt: 1,
|
|
132
|
+
gap: '10px',
|
|
133
|
+
justifyContent: 'start',
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
{readOnly ? quantity : <UpdateItemQuantity uid={uid} quantity={quantity} />}
|
|
137
|
+
{' ⨉ '}
|
|
164
138
|
|
|
165
|
-
|
|
139
|
+
<Money value={price} currency={prices?.price.currency} />
|
|
140
|
+
</Box>
|
|
141
|
+
{hasOptions && (
|
|
142
|
+
<Button
|
|
143
|
+
variant='inline'
|
|
144
|
+
color='secondary'
|
|
145
|
+
href={`${productEditLink(product)}?cartItemId=${uid}`}
|
|
146
|
+
>
|
|
147
|
+
<Trans id='Edit options' />
|
|
148
|
+
</Button>
|
|
149
|
+
)}
|
|
166
150
|
</>
|
|
167
151
|
}
|
|
168
152
|
price={<Money {...(inclTaxes ? prices?.row_total_including_tax : prices?.row_total)} />}
|
|
@@ -181,6 +165,7 @@ export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
|
181
165
|
</Box>
|
|
182
166
|
))}
|
|
183
167
|
{...rest}
|
|
168
|
+
details={<>{rest.details}</>}
|
|
184
169
|
/>
|
|
185
170
|
)
|
|
186
171
|
}
|
|
@@ -7,7 +7,13 @@ import {
|
|
|
7
7
|
|
|
8
8
|
export type CartProps = Omit<ActionCardLayoutProps, 'className'> & {
|
|
9
9
|
cart?: CartItemsFragment | null
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated Not used anymore, please use the size prop
|
|
12
|
+
*/
|
|
10
13
|
sizeSm?: CartItemActionCardProps['size']
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated Not used anymore, please use the size prop
|
|
16
|
+
*/
|
|
11
17
|
sizeMd?: CartItemActionCardProps['size']
|
|
12
18
|
variant?: CartItemActionCardProps['variant']
|
|
13
19
|
itemProps?: Omit<
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AddProductsToCartButton,
|
|
3
|
+
AddProductsToCartButtonProps,
|
|
4
|
+
} from '@graphcommerce/magento-product'
|
|
5
|
+
import { Trans } from '@lingui/react'
|
|
6
|
+
|
|
7
|
+
export function EditCartItemButton(props: AddProductsToCartButtonProps) {
|
|
8
|
+
const { children, ...rest } = props
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<AddProductsToCartButton color='secondary' {...rest}>
|
|
12
|
+
<Trans id='Save changes' />
|
|
13
|
+
</AddProductsToCartButton>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { UseHistoryLink, useHistoryGo } from '@graphcommerce/framer-next-pages'
|
|
2
|
+
import {
|
|
3
|
+
useFormAddProductsToCart,
|
|
4
|
+
AddProductsToCartFormProps,
|
|
5
|
+
AddToCartItemSelector,
|
|
6
|
+
AddProductsToCartForm,
|
|
7
|
+
} from '@graphcommerce/magento-product'
|
|
8
|
+
import { useEffect } from 'react'
|
|
9
|
+
import {
|
|
10
|
+
UseRemoveItemFromCartProps,
|
|
11
|
+
useRemoveItemFromCart,
|
|
12
|
+
} from '../../../hooks/useRemoveItemFromCart'
|
|
13
|
+
import {
|
|
14
|
+
CartItemToCartItemInputProps,
|
|
15
|
+
cartItemToCartItemInput,
|
|
16
|
+
} from '../../../utils/cartItemToCartItemInput'
|
|
17
|
+
|
|
18
|
+
type EditInitProps = CartItemToCartItemInputProps & AddToCartItemSelector
|
|
19
|
+
|
|
20
|
+
function EditInit(props: EditInitProps) {
|
|
21
|
+
const { product, selectors, cartItem, index = 0 } = props
|
|
22
|
+
const { setValue } = useFormAddProductsToCart()
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const cartItemInput = cartItemToCartItemInput({ product, cartItem, selectors })
|
|
26
|
+
if (cartItemInput) setValue(`cartItems.${index}`, cartItemInput)
|
|
27
|
+
}, [cartItem, index, product, selectors, setValue])
|
|
28
|
+
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type EditCartItemFormProps = CartItemToCartItemInputProps &
|
|
33
|
+
AddToCartItemSelector &
|
|
34
|
+
UseHistoryLink &
|
|
35
|
+
AddProductsToCartFormProps
|
|
36
|
+
|
|
37
|
+
export function EditCartItemForm(props: EditCartItemFormProps) {
|
|
38
|
+
const { product, cartItem, onBeforeSubmit, onComplete, index = 0, children, href } = props
|
|
39
|
+
|
|
40
|
+
const remove = useRemoveItemFromCart(cartItem as UseRemoveItemFromCartProps)
|
|
41
|
+
const goToCart = useHistoryGo({ href })
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<AddProductsToCartForm
|
|
45
|
+
{...props}
|
|
46
|
+
onBeforeSubmit={async (variables) => {
|
|
47
|
+
await remove.submit()
|
|
48
|
+
return onBeforeSubmit?.(variables) ?? variables
|
|
49
|
+
}}
|
|
50
|
+
onComplete={async (result, variables) => {
|
|
51
|
+
await goToCart()
|
|
52
|
+
return onComplete?.(result, variables)
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
{children}
|
|
56
|
+
<EditInit product={product} cartItem={cartItem} index={index} />
|
|
57
|
+
</AddProductsToCartForm>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { ApolloCartErrorSnackbar
|
|
1
|
+
import { ApolloCartErrorSnackbar } from '@graphcommerce/magento-cart'
|
|
2
2
|
import { Button, ButtonProps } from '@graphcommerce/next-ui'
|
|
3
3
|
import { Trans } from '@lingui/react'
|
|
4
4
|
import { SxProps, Theme, styled } from '@mui/material'
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import {
|
|
6
|
+
UseRemoveItemFromCartProps,
|
|
7
|
+
useRemoveItemFromCart,
|
|
8
|
+
} from '../../hooks/useRemoveItemFromCart'
|
|
8
9
|
|
|
9
|
-
export type RemoveItemFromCartProps =
|
|
10
|
+
export type RemoveItemFromCartProps = UseRemoveItemFromCartProps & {
|
|
10
11
|
sx?: SxProps<Theme>
|
|
11
12
|
buttonProps?: Omit<ButtonProps, 'type' | 'loading'>
|
|
12
13
|
}
|
|
@@ -15,12 +16,10 @@ const Form = styled('form')({})
|
|
|
15
16
|
|
|
16
17
|
export function RemoveItemFromCart(props: RemoveItemFromCartProps) {
|
|
17
18
|
const { uid, quantity, prices, buttonProps, ...formProps } = props
|
|
18
|
-
const form = useFormGqlMutationCart(RemoveItemFromCartDocument, { defaultValues: { uid } })
|
|
19
|
-
const { handleSubmit, formState, error } = form
|
|
20
|
-
const submitHandler = handleSubmit(() => {})
|
|
21
19
|
|
|
20
|
+
const { submit, formState, error } = useRemoveItemFromCart(props)
|
|
22
21
|
return (
|
|
23
|
-
<Form noValidate onSubmit={
|
|
22
|
+
<Form noValidate onSubmit={submit} {...formProps}>
|
|
24
23
|
<Button
|
|
25
24
|
variant='inline'
|
|
26
25
|
color='secondary'
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { ApolloCartErrorSnackbar
|
|
1
|
+
import { ApolloCartErrorSnackbar } from '@graphcommerce/magento-cart'
|
|
2
2
|
import { Fab, FabProps, iconClose } from '@graphcommerce/next-ui'
|
|
3
3
|
import { i18n } from '@lingui/core'
|
|
4
4
|
import { SxProps, Theme, styled } from '@mui/material'
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
UseRemoveItemFromCartProps,
|
|
7
|
+
useRemoveItemFromCart,
|
|
8
|
+
} from '../../hooks/useRemoveItemFromCart'
|
|
7
9
|
|
|
8
|
-
export type RemoveItemFromCartFabProps =
|
|
10
|
+
export type RemoveItemFromCartFabProps = UseRemoveItemFromCartProps & {
|
|
9
11
|
sx?: SxProps<Theme>
|
|
10
12
|
fabProps?: Omit<FabProps, 'type' | 'icon' | 'loading'>
|
|
11
13
|
}
|
|
@@ -14,12 +16,11 @@ const Form = styled('form')({})
|
|
|
14
16
|
|
|
15
17
|
export function RemoveItemFromCartFab(props: RemoveItemFromCartFabProps) {
|
|
16
18
|
const { uid, quantity, prices, product, ...formProps } = props
|
|
17
|
-
|
|
18
|
-
const {
|
|
19
|
-
const submitHandler = handleSubmit(() => {})
|
|
19
|
+
|
|
20
|
+
const { submit, formState, error } = useRemoveItemFromCart(props)
|
|
20
21
|
|
|
21
22
|
return (
|
|
22
|
-
<Form noValidate onSubmit={
|
|
23
|
+
<Form noValidate onSubmit={submit} {...formProps}>
|
|
23
24
|
<Fab
|
|
24
25
|
aria-label={i18n._(/* i18n */ 'Remove Product')}
|
|
25
26
|
size='small'
|
|
@@ -22,29 +22,20 @@ export function SelectedCustomizableOptions(props: SelectedCustomizableOptionPro
|
|
|
22
22
|
return (
|
|
23
23
|
<>
|
|
24
24
|
{options.map((option) => (
|
|
25
|
-
<Box>
|
|
26
|
-
<Box
|
|
27
|
-
{option.label}
|
|
28
|
-
</Box>
|
|
25
|
+
<Box key={option.customizable_option_uid}>
|
|
26
|
+
<Box sx={{ color: 'text.primary' }}>{option.label}</Box>
|
|
29
27
|
{option.values.filter(nonNullable).map((value) => (
|
|
30
28
|
<Box
|
|
31
|
-
key={
|
|
29
|
+
key={value.customizable_option_value_uid}
|
|
32
30
|
sx={(theme) => ({
|
|
33
31
|
display: 'flex',
|
|
34
32
|
gap: theme.spacings.xxs,
|
|
35
33
|
flexDirection: 'row',
|
|
36
34
|
})}
|
|
37
35
|
>
|
|
38
|
-
{value.label &&
|
|
39
|
-
<span key={`${value.customizable_option_value_uid}_${value.label}`}>
|
|
40
|
-
{value.label}
|
|
41
|
-
</span>
|
|
42
|
-
)}
|
|
36
|
+
{value.label && <span>{value.label}</span>}
|
|
43
37
|
{value.price.value > 0 && productPrice && (
|
|
44
|
-
<Box
|
|
45
|
-
sx={(theme) => ({ position: 'absolute', right: theme.spacings.xs })}
|
|
46
|
-
key={`${value.customizable_option_value_uid}_${value.price.value}`}
|
|
47
|
-
>
|
|
38
|
+
<Box sx={(theme) => ({ position: 'absolute', right: theme.spacings.xs })}>
|
|
48
39
|
<Money
|
|
49
40
|
value={
|
|
50
41
|
value.price.type === 'PERCENT'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useFormGqlMutationCart } from '@graphcommerce/magento-cart/hooks'
|
|
2
|
+
import { UseFormGraphQlOptions } from '@graphcommerce/react-hook-form'
|
|
3
|
+
import type { DistributedOmit } from 'type-fest'
|
|
4
|
+
import { CartItemFragment } from '../Api/CartItem.gql'
|
|
5
|
+
import {
|
|
6
|
+
RemoveItemFromCartMutation,
|
|
7
|
+
RemoveItemFromCartMutationVariables,
|
|
8
|
+
RemoveItemFromCartDocument,
|
|
9
|
+
} from '../components/RemoveItemFromCart/RemoveItemFromCart.gql'
|
|
10
|
+
|
|
11
|
+
export type UseRemoveItemFromCartProps = DistributedOmit<CartItemFragment, '__typename'> &
|
|
12
|
+
Omit<
|
|
13
|
+
UseFormGraphQlOptions<RemoveItemFromCartMutation, RemoveItemFromCartMutationVariables>,
|
|
14
|
+
'errors'
|
|
15
|
+
>
|
|
16
|
+
|
|
17
|
+
export function useRemoveItemFromCart(props: UseRemoveItemFromCartProps) {
|
|
18
|
+
const { uid, errors, ...options } = props
|
|
19
|
+
|
|
20
|
+
const form = useFormGqlMutationCart(RemoveItemFromCartDocument, {
|
|
21
|
+
defaultValues: { uid },
|
|
22
|
+
...options,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const { handleSubmit } = form
|
|
26
|
+
const submit = handleSubmit(() => {})
|
|
27
|
+
return { ...form, submit }
|
|
28
|
+
}
|
package/index.ts
CHANGED
|
@@ -9,3 +9,5 @@ export * from './components/RemoveItemFromCart/RemoveItemFromCart.gql'
|
|
|
9
9
|
export * from './components/RemoveItemFromCart/RemoveItemFromCartFab'
|
|
10
10
|
export * from './components/SelectedCustomizableOptions/SelectedCustomizableOptions'
|
|
11
11
|
export * from './components/UpdateItemQuantity/UpdateItemQuantity'
|
|
12
|
+
export * from './components/EditCartItem'
|
|
13
|
+
export * from './hooks/useRemoveItemFromCart'
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/magento-cart-items",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "8.1.0-canary.
|
|
5
|
+
"version": "8.1.0-canary.20",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
8
|
"eslintConfig": {
|
|
@@ -12,17 +12,18 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"@graphcommerce/eslint-config-pwa": "^8.1.0-canary.
|
|
16
|
-
"@graphcommerce/
|
|
17
|
-
"@graphcommerce/
|
|
18
|
-
"@graphcommerce/
|
|
19
|
-
"@graphcommerce/magento-
|
|
20
|
-
"@graphcommerce/magento-
|
|
21
|
-
"@graphcommerce/magento-
|
|
22
|
-
"@graphcommerce/
|
|
23
|
-
"@graphcommerce/
|
|
24
|
-
"@graphcommerce/
|
|
25
|
-
"@graphcommerce/
|
|
15
|
+
"@graphcommerce/eslint-config-pwa": "^8.1.0-canary.20",
|
|
16
|
+
"@graphcommerce/framer-next-pages": "^8.1.0-canary.20",
|
|
17
|
+
"@graphcommerce/graphql": "^8.1.0-canary.20",
|
|
18
|
+
"@graphcommerce/image": "^8.1.0-canary.20",
|
|
19
|
+
"@graphcommerce/magento-cart": "^8.1.0-canary.20",
|
|
20
|
+
"@graphcommerce/magento-customer": "^8.1.0-canary.20",
|
|
21
|
+
"@graphcommerce/magento-product": "^8.1.0-canary.20",
|
|
22
|
+
"@graphcommerce/magento-store": "^8.1.0-canary.20",
|
|
23
|
+
"@graphcommerce/next-ui": "^8.1.0-canary.20",
|
|
24
|
+
"@graphcommerce/prettier-config-pwa": "^8.1.0-canary.20",
|
|
25
|
+
"@graphcommerce/react-hook-form": "^8.1.0-canary.20",
|
|
26
|
+
"@graphcommerce/typescript-config-pwa": "^8.1.0-canary.20",
|
|
26
27
|
"@lingui/core": "^4.2.1",
|
|
27
28
|
"@lingui/macro": "^4.2.1",
|
|
28
29
|
"@lingui/react": "^4.2.1",
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AddProductsToCartFields,
|
|
3
|
+
AnyOption,
|
|
4
|
+
CustomizableProductOptionBase,
|
|
5
|
+
OptionValueSelector,
|
|
6
|
+
SelectorsProp,
|
|
7
|
+
productCustomizableSelectors,
|
|
8
|
+
} from '@graphcommerce/magento-product'
|
|
9
|
+
import { isTypename, filterNonNullableKeys, nonNullable } from '@graphcommerce/next-ui'
|
|
10
|
+
import { CartItemFragment } from '../Api/CartItem.gql'
|
|
11
|
+
import { EditCartItemFormFragment } from '../components/EditCartItem/EditCartItemForm/EditCartItemForm.gql'
|
|
12
|
+
|
|
13
|
+
type CartItemInput = AddProductsToCartFields['cartItems'][number]
|
|
14
|
+
|
|
15
|
+
export type CartItemToCartItemInputProps = {
|
|
16
|
+
product: EditCartItemFormFragment
|
|
17
|
+
cartItem: CartItemFragment
|
|
18
|
+
} & SelectorsProp
|
|
19
|
+
|
|
20
|
+
export function cartItemToCartItemInput(
|
|
21
|
+
props: CartItemToCartItemInputProps,
|
|
22
|
+
): CartItemInput | undefined {
|
|
23
|
+
const { product, cartItem, selectors } = props
|
|
24
|
+
|
|
25
|
+
if (isTypename(product, ['GroupedProduct']) || !product.sku || !cartItem) return undefined
|
|
26
|
+
|
|
27
|
+
const allSelectors: OptionValueSelector = { ...productCustomizableSelectors, ...selectors }
|
|
28
|
+
|
|
29
|
+
const cartItemInput: CartItemInput = {
|
|
30
|
+
sku: product.sku,
|
|
31
|
+
quantity: cartItem.quantity,
|
|
32
|
+
customizable_options: {},
|
|
33
|
+
selected_options: [],
|
|
34
|
+
entered_options: [],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const cartItemCustomizableOptions = filterNonNullableKeys(
|
|
38
|
+
isTypename(cartItem, ['ConfigurableCartItem'])
|
|
39
|
+
? cartItem.configurable_customizable
|
|
40
|
+
: cartItem.customizable_options,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if (cartItemCustomizableOptions.length > 0) {
|
|
44
|
+
product.options?.filter(nonNullable).forEach((productOption) => {
|
|
45
|
+
// @todo Date option: Magento's backend does not provide an ISO date string that can be used, only localized strings are available which can not be parsed.
|
|
46
|
+
// @todo File option: We do not support file options yet.
|
|
47
|
+
if (isTypename(productOption, ['CustomizableDateOption', 'CustomizableFileOption'])) return
|
|
48
|
+
|
|
49
|
+
const selector = allSelectors[productOption.__typename] as
|
|
50
|
+
| undefined
|
|
51
|
+
| ((option: AnyOption) => CustomizableProductOptionBase | CustomizableProductOptionBase[])
|
|
52
|
+
const possibleProductValues = selector ? selector(productOption) : null
|
|
53
|
+
|
|
54
|
+
const cartItemCustomizableOption = cartItemCustomizableOptions.find(
|
|
55
|
+
(option) => option?.customizable_option_uid === productOption.uid,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
const cartItemCustomizableOptionValue = filterNonNullableKeys(
|
|
59
|
+
cartItemCustomizableOption?.values,
|
|
60
|
+
)
|
|
61
|
+
if (cartItemCustomizableOptionValue.length === 0) return
|
|
62
|
+
|
|
63
|
+
if (Array.isArray(possibleProductValues)) {
|
|
64
|
+
const value = cartItemCustomizableOptionValue.map((v) => v.customizable_option_value_uid)
|
|
65
|
+
if (!cartItemInput.customizable_options) cartItemInput.customizable_options = {}
|
|
66
|
+
cartItemInput.customizable_options[productOption.uid] = isTypename(productOption, [
|
|
67
|
+
'CustomizableRadioOption',
|
|
68
|
+
'CustomizableDropDownOption',
|
|
69
|
+
])
|
|
70
|
+
? value[0]
|
|
71
|
+
: value
|
|
72
|
+
} else {
|
|
73
|
+
const idx = (productOption.sort_order ?? 0) + 100
|
|
74
|
+
|
|
75
|
+
if (!cartItemInput.entered_options) cartItemInput.entered_options = []
|
|
76
|
+
cartItemInput.entered_options[idx] = {
|
|
77
|
+
uid: productOption.uid,
|
|
78
|
+
value: cartItemCustomizableOptionValue[0].value,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (isTypename(cartItem, ['ConfigurableCartItem']) && cartItem.configurable_options) {
|
|
85
|
+
cartItemInput.selected_options = filterNonNullableKeys(cartItem.configurable_options).map(
|
|
86
|
+
(option) => option.configurable_product_option_value_uid,
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (isTypename(cartItem, ['BundleCartItem']) && isTypename(product, ['BundleProduct'])) {
|
|
91
|
+
filterNonNullableKeys(product.items).forEach((productBundleItem, i) => {
|
|
92
|
+
const cartItemBundleOption = cartItem.bundle_options.find(
|
|
93
|
+
(option) => option?.uid === productBundleItem?.uid,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
if (!cartItemBundleOption) return
|
|
97
|
+
|
|
98
|
+
// todo multi select..
|
|
99
|
+
const idx = productBundleItem.position ?? 0 + 1000
|
|
100
|
+
const value = cartItemBundleOption.values[0]
|
|
101
|
+
|
|
102
|
+
if (!value) return
|
|
103
|
+
if (productBundleItem.options?.some((o) => o?.can_change_quantity)) {
|
|
104
|
+
if (!cartItemInput.entered_options) cartItemInput.entered_options = []
|
|
105
|
+
cartItemInput.entered_options[idx] = {
|
|
106
|
+
uid: value.uid,
|
|
107
|
+
value: `${value.quantity}`,
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
if (!cartItemInput.selected_options) cartItemInput.selected_options = []
|
|
111
|
+
cartItemInput.selected_options[idx] = value.uid
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return cartItemInput
|
|
117
|
+
}
|