@graphcommerce/magento-cart-items 8.1.0-canary.9 → 9.0.0-canary.55
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/Api/CartItem.graphql +1 -1
- package/Api/CartItems.graphql +1 -1
- package/CHANGELOG.md +117 -0
- package/components/CartCrosssellsScroller/CartCrosssellsScroller.tsx +2 -2
- package/components/CartItem/CartItem.tsx +1 -4
- package/components/CartItemActionCard/CartItemActionCard.tsx +54 -29
- package/components/CartItemsActionCards/CartItemsActionCards.tsx +17 -19
- 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 +7 -19
- package/components/UpdateItemQuantity/UpdateItemQuantity.tsx +1 -1
- package/hooks/useRemoveItemFromCart.ts +28 -0
- package/index.ts +2 -0
- package/package.json +13 -12
- package/utils/cartItemToCartItemInput.ts +113 -0
package/Api/CartItem.graphql
CHANGED
package/Api/CartItems.graphql
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,122 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 9.0.0-canary.55
|
|
4
|
+
|
|
5
|
+
## 9.0.0-canary.54
|
|
6
|
+
|
|
7
|
+
## 8.1.0-canary.53
|
|
8
|
+
|
|
9
|
+
## 8.1.0-canary.52
|
|
10
|
+
|
|
11
|
+
## 8.1.0-canary.51
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [#2322](https://github.com/graphcommerce-org/graphcommerce/pull/2322) [`4d08a2b`](https://github.com/graphcommerce-org/graphcommerce/commit/4d08a2bd8c1919bd85b18e8e856775d18fb67e00) - Rename configurable_customizable back to customizable_options
|
|
16
|
+
([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
|
|
17
|
+
|
|
18
|
+
## 8.1.0-canary.50
|
|
19
|
+
|
|
20
|
+
## 8.1.0-canary.49
|
|
21
|
+
|
|
22
|
+
## 8.1.0-canary.48
|
|
23
|
+
|
|
24
|
+
## 8.1.0-canary.47
|
|
25
|
+
|
|
26
|
+
## 8.1.0-canary.46
|
|
27
|
+
|
|
28
|
+
## 8.1.0-canary.45
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- [#2216](https://github.com/graphcommerce-org/graphcommerce/pull/2216) [`c30ac07`](https://github.com/graphcommerce-org/graphcommerce/commit/c30ac07d1e9aee5fb3df483404d8a8ca853c93ce) - Make sure the edit cart item isn't shown for an order
|
|
33
|
+
([@paales](https://github.com/paales))
|
|
34
|
+
|
|
35
|
+
## 8.1.0-canary.44
|
|
36
|
+
|
|
37
|
+
## 8.1.0-canary.43
|
|
38
|
+
|
|
39
|
+
## 8.1.0-canary.42
|
|
40
|
+
|
|
41
|
+
## 8.1.0-canary.41
|
|
42
|
+
|
|
43
|
+
## 8.1.0-canary.40
|
|
44
|
+
|
|
45
|
+
## 8.1.0-canary.39
|
|
46
|
+
|
|
47
|
+
## 8.1.0-canary.38
|
|
48
|
+
|
|
49
|
+
## 8.1.0-canary.37
|
|
50
|
+
|
|
51
|
+
## 8.1.0-canary.36
|
|
52
|
+
|
|
53
|
+
## 8.1.0-canary.35
|
|
54
|
+
|
|
55
|
+
## 8.1.0-canary.34
|
|
56
|
+
|
|
57
|
+
## 8.1.0-canary.33
|
|
58
|
+
|
|
59
|
+
## 8.1.0-canary.32
|
|
60
|
+
|
|
61
|
+
## 8.1.0-canary.31
|
|
62
|
+
|
|
63
|
+
## 8.1.0-canary.30
|
|
64
|
+
|
|
65
|
+
## 8.1.0-canary.29
|
|
66
|
+
|
|
67
|
+
## 8.1.0-canary.28
|
|
68
|
+
|
|
69
|
+
## 8.1.0-canary.27
|
|
70
|
+
|
|
71
|
+
## 8.1.0-canary.26
|
|
72
|
+
|
|
73
|
+
## 8.1.0-canary.25
|
|
74
|
+
|
|
75
|
+
## 8.1.0-canary.24
|
|
76
|
+
|
|
77
|
+
## 8.1.0-canary.23
|
|
78
|
+
|
|
79
|
+
## 8.1.0-canary.22
|
|
80
|
+
|
|
81
|
+
## 8.1.0-canary.21
|
|
82
|
+
|
|
83
|
+
## 8.1.0-canary.20
|
|
84
|
+
|
|
85
|
+
### Minor Changes
|
|
86
|
+
|
|
87
|
+
- [#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
|
|
88
|
+
([@Jessevdpoel](https://github.com/Jessevdpoel))
|
|
89
|
+
|
|
90
|
+
### Patch Changes
|
|
91
|
+
|
|
92
|
+
- [#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.
|
|
93
|
+
([@Jessevdpoel](https://github.com/Jessevdpoel))
|
|
94
|
+
|
|
95
|
+
## 8.1.0-canary.19
|
|
96
|
+
|
|
97
|
+
## 8.1.0-canary.18
|
|
98
|
+
|
|
99
|
+
## 8.1.0-canary.17
|
|
100
|
+
|
|
101
|
+
### Minor Changes
|
|
102
|
+
|
|
103
|
+
- [#2209](https://github.com/graphcommerce-org/graphcommerce/pull/2209) [`2872cab`](https://github.com/graphcommerce-org/graphcommerce/commit/2872cabdca9ee4f0378fd411c6a633f71bb92f1f) - Removed useMediaQuery from the wishlist and cart ItemActionCard and replaced it with a new responsive size prop.
|
|
104
|
+
([@Jessevdpoel](https://github.com/Jessevdpoel))
|
|
105
|
+
|
|
106
|
+
## 8.1.0-canary.16
|
|
107
|
+
|
|
108
|
+
## 8.1.0-canary.15
|
|
109
|
+
|
|
110
|
+
## 8.1.0-canary.14
|
|
111
|
+
|
|
112
|
+
## 8.1.0-canary.13
|
|
113
|
+
|
|
114
|
+
## 8.1.0-canary.12
|
|
115
|
+
|
|
116
|
+
## 8.1.0-canary.11
|
|
117
|
+
|
|
118
|
+
## 8.1.0-canary.10
|
|
119
|
+
|
|
3
120
|
## 8.1.0-canary.9
|
|
4
121
|
|
|
5
122
|
## 8.1.0-canary.8
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
ProductScroller,
|
|
6
6
|
ProductScrollerProps,
|
|
7
7
|
} from '@graphcommerce/magento-product'
|
|
8
|
-
import { Trans } from '@lingui/
|
|
8
|
+
import { Trans } from '@lingui/macro'
|
|
9
9
|
|
|
10
10
|
export type CartItemCrosssellsProps = {
|
|
11
11
|
renderer: ProductListItemRenderer
|
|
@@ -24,7 +24,7 @@ export function CartCrosssellsScroller(props: CartItemCrosssellsProps) {
|
|
|
24
24
|
productListRenderer={renderer}
|
|
25
25
|
items={crossSellItems}
|
|
26
26
|
sx={sx}
|
|
27
|
-
title={title ?? <Trans
|
|
27
|
+
title={title ?? <Trans>Complete your purchase</Trans>}
|
|
28
28
|
titleProps={{ variant: 'h6', ...titleProps }}
|
|
29
29
|
/>
|
|
30
30
|
</AddProductsToCartForm>
|
|
@@ -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,20 +19,12 @@ 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) {
|
|
33
|
-
const { cartItem, sx = [], size = '
|
|
27
|
+
const { cartItem, sx = [], size = 'responsive', readOnly = false, ...rest } = props
|
|
34
28
|
const { uid, quantity, prices, errors, product } = cartItem
|
|
35
29
|
const { name, thumbnail, url_key } = product
|
|
36
30
|
|
|
@@ -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,13 +57,16 @@ 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
|
},
|
|
65
64
|
'&.sizeSmall': {
|
|
66
65
|
px: 0,
|
|
67
66
|
},
|
|
67
|
+
'&.sizeResponsive': {
|
|
68
|
+
[theme.breakpoints.down('md')]: { px: 0 },
|
|
69
|
+
},
|
|
68
70
|
'& .ActionCard-end': {
|
|
69
71
|
justifyContent: readOnly ? 'center' : 'space-between',
|
|
70
72
|
},
|
|
@@ -75,16 +77,11 @@ export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
|
75
77
|
alignSelf: 'flex-start',
|
|
76
78
|
},
|
|
77
79
|
'& .ActionCard-secondaryAction': {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
color: 'text.secondary',
|
|
82
|
-
mt: 1,
|
|
83
|
-
gap: '10px',
|
|
84
|
-
justifyContent: 'start',
|
|
80
|
+
display: 'grid',
|
|
81
|
+
rowGap: theme.spacings.xs,
|
|
82
|
+
justifyItems: 'start',
|
|
85
83
|
},
|
|
86
84
|
'& .ActionCard-price': {
|
|
87
|
-
typography: typographySizes[size],
|
|
88
85
|
pr: readOnly ? 0 : theme.spacings.xs,
|
|
89
86
|
mb: { xs: 0.5, sm: 0 },
|
|
90
87
|
},
|
|
@@ -97,13 +94,13 @@ export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
|
97
94
|
layout='fill'
|
|
98
95
|
src={thumbnail?.url}
|
|
99
96
|
sx={{
|
|
100
|
-
width:
|
|
101
|
-
height:
|
|
97
|
+
width: actionCardImageSizes[size],
|
|
98
|
+
height: actionCardImageSizes[size],
|
|
102
99
|
display: 'block',
|
|
103
100
|
borderRadius: 1,
|
|
104
101
|
objectFit: 'contain',
|
|
105
102
|
}}
|
|
106
|
-
sizes={
|
|
103
|
+
sizes={actionCardImageSizes[size]}
|
|
107
104
|
/>
|
|
108
105
|
)
|
|
109
106
|
}
|
|
@@ -126,14 +123,41 @@ export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
|
126
123
|
}
|
|
127
124
|
secondaryAction={
|
|
128
125
|
<>
|
|
129
|
-
|
|
130
|
-
|
|
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
|
+
{' ⨉ '}
|
|
131
138
|
|
|
132
|
-
|
|
139
|
+
<Money value={price} currency={prices?.price.currency} />
|
|
140
|
+
</Box>
|
|
141
|
+
{hasOptions && !readOnly && (
|
|
142
|
+
<Button
|
|
143
|
+
variant='inline'
|
|
144
|
+
color='secondary'
|
|
145
|
+
href={`${productEditLink(product)}?cartItemId=${uid}`}
|
|
146
|
+
>
|
|
147
|
+
<Trans id='Edit options' />
|
|
148
|
+
</Button>
|
|
149
|
+
)}
|
|
133
150
|
</>
|
|
134
151
|
}
|
|
135
152
|
price={<Money {...(inclTaxes ? prices?.row_total_including_tax : prices?.row_total)} />}
|
|
136
|
-
action={
|
|
153
|
+
action={
|
|
154
|
+
!readOnly && (
|
|
155
|
+
<RemoveItemFromCart
|
|
156
|
+
{...cartItem}
|
|
157
|
+
buttonProps={{ size: size === 'responsive' ? 'large' : size }}
|
|
158
|
+
/>
|
|
159
|
+
)
|
|
160
|
+
}
|
|
137
161
|
size={size}
|
|
138
162
|
after={filterNonNullableKeys(errors).map((error) => (
|
|
139
163
|
<Box sx={{ color: 'error.main', typography: 'caption' }} key={error.message}>
|
|
@@ -141,6 +165,7 @@ export function CartItemActionCard(props: CartItemActionCardProps) {
|
|
|
141
165
|
</Box>
|
|
142
166
|
))}
|
|
143
167
|
{...rest}
|
|
168
|
+
details={<>{rest.details}</>}
|
|
144
169
|
/>
|
|
145
170
|
)
|
|
146
171
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ActionCardLayout, ActionCardLayoutProps, nonNullable } from '@graphcommerce/next-ui'
|
|
2
|
-
import { Theme, useMediaQuery } from '@mui/material'
|
|
3
2
|
import { CartItemsFragment } from '../../Api/CartItems.gql'
|
|
4
3
|
import {
|
|
5
4
|
CartItemActionCard,
|
|
@@ -8,7 +7,13 @@ import {
|
|
|
8
7
|
|
|
9
8
|
export type CartProps = Omit<ActionCardLayoutProps, 'className'> & {
|
|
10
9
|
cart?: CartItemsFragment | null
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated Not used anymore, please use the size prop
|
|
12
|
+
*/
|
|
11
13
|
sizeSm?: CartItemActionCardProps['size']
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated Not used anymore, please use the size prop
|
|
16
|
+
*/
|
|
12
17
|
sizeMd?: CartItemActionCardProps['size']
|
|
13
18
|
variant?: CartItemActionCardProps['variant']
|
|
14
19
|
itemProps?: Omit<
|
|
@@ -31,32 +36,25 @@ export function CartItemsActionCards(props: CartProps) {
|
|
|
31
36
|
children,
|
|
32
37
|
layout = 'list',
|
|
33
38
|
itemProps = {},
|
|
34
|
-
sizeSm = 'medium',
|
|
35
|
-
sizeMd = 'large',
|
|
36
39
|
variant = 'default',
|
|
37
40
|
...remainingProps
|
|
38
41
|
} = props
|
|
39
42
|
|
|
40
|
-
const isMobile = useMediaQuery<Theme>((theme) => theme.breakpoints.down('md'), {
|
|
41
|
-
defaultMatches: false,
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
const size = isMobile ? sizeSm : sizeMd
|
|
45
|
-
|
|
46
43
|
if (!cart?.items?.length) return null
|
|
47
44
|
|
|
48
45
|
return (
|
|
49
46
|
<ActionCardLayout layout={layout} {...remainingProps}>
|
|
50
|
-
{cart.items
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
{cart.items
|
|
48
|
+
?.filter(nonNullable)
|
|
49
|
+
.map((item) => (
|
|
50
|
+
<CartItemActionCard
|
|
51
|
+
key={item.uid}
|
|
52
|
+
cartItem={item}
|
|
53
|
+
layout={layout}
|
|
54
|
+
variant={variant}
|
|
55
|
+
{...itemProps}
|
|
56
|
+
/>
|
|
57
|
+
))}
|
|
60
58
|
{children}
|
|
61
59
|
</ActionCardLayout>
|
|
62
60
|
)
|
|
@@ -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'
|
|
@@ -6,14 +6,11 @@ import { SelectedCustomizableOptionFragment } from './SelectedCustomizableOption
|
|
|
6
6
|
|
|
7
7
|
type SelectedCustomizableOptionProps = CartItemFragment & {
|
|
8
8
|
customizable_options?: (SelectedCustomizableOptionFragment | null | undefined)[] | null
|
|
9
|
-
configurable_customizable?: (SelectedCustomizableOptionFragment | null | undefined)[] | null
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
export function SelectedCustomizableOptions(props: SelectedCustomizableOptionProps) {
|
|
13
|
-
const { customizable_options, product
|
|
14
|
-
const options = customizable_options
|
|
15
|
-
? filterNonNullableKeys(customizable_options, [])
|
|
16
|
-
: filterNonNullableKeys(configurable_customizable, [])
|
|
12
|
+
const { customizable_options, product } = props
|
|
13
|
+
const options = filterNonNullableKeys(customizable_options, [])
|
|
17
14
|
|
|
18
15
|
if (!options.length) return null
|
|
19
16
|
|
|
@@ -22,29 +19,20 @@ export function SelectedCustomizableOptions(props: SelectedCustomizableOptionPro
|
|
|
22
19
|
return (
|
|
23
20
|
<>
|
|
24
21
|
{options.map((option) => (
|
|
25
|
-
<Box>
|
|
26
|
-
<Box
|
|
27
|
-
{option.label}
|
|
28
|
-
</Box>
|
|
22
|
+
<Box key={option.customizable_option_uid}>
|
|
23
|
+
<Box sx={{ color: 'text.primary' }}>{option.label}</Box>
|
|
29
24
|
{option.values.filter(nonNullable).map((value) => (
|
|
30
25
|
<Box
|
|
31
|
-
key={
|
|
26
|
+
key={value.customizable_option_value_uid}
|
|
32
27
|
sx={(theme) => ({
|
|
33
28
|
display: 'flex',
|
|
34
29
|
gap: theme.spacings.xxs,
|
|
35
30
|
flexDirection: 'row',
|
|
36
31
|
})}
|
|
37
32
|
>
|
|
38
|
-
{value.label &&
|
|
39
|
-
<span key={`${value.customizable_option_value_uid}_${value.label}`}>
|
|
40
|
-
{value.label}
|
|
41
|
-
</span>
|
|
42
|
-
)}
|
|
33
|
+
{value.label && <span>{value.label}</span>}
|
|
43
34
|
{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
|
-
>
|
|
35
|
+
<Box sx={(theme) => ({ position: 'absolute', right: theme.spacings.xs })}>
|
|
48
36
|
<Money
|
|
49
37
|
value={
|
|
50
38
|
value.price.type === 'PERCENT'
|
|
@@ -38,7 +38,7 @@ export function UpdateItemQuantity(props: UpdateItemQuantityProps) {
|
|
|
38
38
|
|
|
39
39
|
return (
|
|
40
40
|
<form noValidate onSubmit={submit}>
|
|
41
|
-
<FormAutoSubmit control={control} submit={submit}
|
|
41
|
+
<FormAutoSubmit control={control} submit={submit} leading />
|
|
42
42
|
<NumberFieldElement
|
|
43
43
|
control={control}
|
|
44
44
|
name='quantity'
|
|
@@ -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": "
|
|
5
|
+
"version": "9.0.0-canary.55",
|
|
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": "^
|
|
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": "^9.0.0-canary.55",
|
|
16
|
+
"@graphcommerce/framer-next-pages": "^9.0.0-canary.55",
|
|
17
|
+
"@graphcommerce/graphql": "^9.0.0-canary.55",
|
|
18
|
+
"@graphcommerce/image": "^9.0.0-canary.55",
|
|
19
|
+
"@graphcommerce/magento-cart": "^9.0.0-canary.55",
|
|
20
|
+
"@graphcommerce/magento-customer": "^9.0.0-canary.55",
|
|
21
|
+
"@graphcommerce/magento-product": "^9.0.0-canary.55",
|
|
22
|
+
"@graphcommerce/magento-store": "^9.0.0-canary.55",
|
|
23
|
+
"@graphcommerce/next-ui": "^9.0.0-canary.55",
|
|
24
|
+
"@graphcommerce/prettier-config-pwa": "^9.0.0-canary.55",
|
|
25
|
+
"@graphcommerce/react-hook-form": "^9.0.0-canary.55",
|
|
26
|
+
"@graphcommerce/typescript-config-pwa": "^9.0.0-canary.55",
|
|
26
27
|
"@lingui/core": "^4.2.1",
|
|
27
28
|
"@lingui/macro": "^4.2.1",
|
|
28
29
|
"@lingui/react": "^4.2.1",
|
|
@@ -0,0 +1,113 @@
|
|
|
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(cartItem.customizable_options ?? {})
|
|
38
|
+
|
|
39
|
+
if (cartItemCustomizableOptions.length > 0) {
|
|
40
|
+
product.options?.filter(nonNullable).forEach((productOption) => {
|
|
41
|
+
// @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.
|
|
42
|
+
// @todo File option: We do not support file options yet.
|
|
43
|
+
if (isTypename(productOption, ['CustomizableDateOption', 'CustomizableFileOption'])) return
|
|
44
|
+
|
|
45
|
+
const selector = allSelectors[productOption.__typename] as
|
|
46
|
+
| undefined
|
|
47
|
+
| ((option: AnyOption) => CustomizableProductOptionBase | CustomizableProductOptionBase[])
|
|
48
|
+
const possibleProductValues = selector ? selector(productOption) : null
|
|
49
|
+
|
|
50
|
+
const cartItemCustomizableOption = cartItemCustomizableOptions.find(
|
|
51
|
+
(option) => option?.customizable_option_uid === productOption.uid,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
const cartItemCustomizableOptionValue = filterNonNullableKeys(
|
|
55
|
+
cartItemCustomizableOption?.values,
|
|
56
|
+
)
|
|
57
|
+
if (cartItemCustomizableOptionValue.length === 0) return
|
|
58
|
+
|
|
59
|
+
if (Array.isArray(possibleProductValues)) {
|
|
60
|
+
const value = cartItemCustomizableOptionValue.map((v) => v.customizable_option_value_uid)
|
|
61
|
+
if (!cartItemInput.customizable_options) cartItemInput.customizable_options = {}
|
|
62
|
+
cartItemInput.customizable_options[productOption.uid] = isTypename(productOption, [
|
|
63
|
+
'CustomizableRadioOption',
|
|
64
|
+
'CustomizableDropDownOption',
|
|
65
|
+
])
|
|
66
|
+
? value[0]
|
|
67
|
+
: value
|
|
68
|
+
} else {
|
|
69
|
+
const idx = (productOption.sort_order ?? 0) + 100
|
|
70
|
+
|
|
71
|
+
if (!cartItemInput.entered_options) cartItemInput.entered_options = []
|
|
72
|
+
cartItemInput.entered_options[idx] = {
|
|
73
|
+
uid: productOption.uid,
|
|
74
|
+
value: cartItemCustomizableOptionValue[0].value,
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (isTypename(cartItem, ['ConfigurableCartItem']) && cartItem.configurable_options) {
|
|
81
|
+
cartItemInput.selected_options = filterNonNullableKeys(cartItem.configurable_options).map(
|
|
82
|
+
(option) => option.configurable_product_option_value_uid,
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (isTypename(cartItem, ['BundleCartItem']) && isTypename(product, ['BundleProduct'])) {
|
|
87
|
+
filterNonNullableKeys(product.items).forEach((productBundleItem) => {
|
|
88
|
+
const cartItemBundleOption = cartItem.bundle_options.find(
|
|
89
|
+
(option) => option?.uid === productBundleItem?.uid,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if (!cartItemBundleOption) return
|
|
93
|
+
|
|
94
|
+
// todo multi select..
|
|
95
|
+
const idx = productBundleItem.position ?? 0 + 1000
|
|
96
|
+
const value = cartItemBundleOption.values[0]
|
|
97
|
+
|
|
98
|
+
if (!value) return
|
|
99
|
+
if (productBundleItem.options?.some((o) => o?.can_change_quantity)) {
|
|
100
|
+
if (!cartItemInput.entered_options) cartItemInput.entered_options = []
|
|
101
|
+
cartItemInput.entered_options[idx] = {
|
|
102
|
+
uid: value.uid,
|
|
103
|
+
value: `${value.quantity}`,
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
if (!cartItemInput.selected_options) cartItemInput.selected_options = []
|
|
107
|
+
cartItemInput.selected_options[idx] = value.uid
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return cartItemInput
|
|
113
|
+
}
|