@graphcommerce/magento-cart-items 8.1.0-canary.9 → 9.0.0-canary.54

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.
@@ -1,4 +1,4 @@
1
- fragment CartItem on CartItemInterface @injectable {
1
+ fragment CartItem on CartItemInterface {
2
2
  uid
3
3
  quantity
4
4
  __typename
@@ -1,4 +1,4 @@
1
- fragment CartItems on Cart @injectable {
1
+ fragment CartItems on Cart {
2
2
  id
3
3
  items {
4
4
  uid
package/CHANGELOG.md CHANGED
@@ -1,5 +1,120 @@
1
1
  # Change Log
2
2
 
3
+ ## 9.0.0-canary.54
4
+
5
+ ## 8.1.0-canary.53
6
+
7
+ ## 8.1.0-canary.52
8
+
9
+ ## 8.1.0-canary.51
10
+
11
+ ### Minor Changes
12
+
13
+ - [#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
14
+ ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
15
+
16
+ ## 8.1.0-canary.50
17
+
18
+ ## 8.1.0-canary.49
19
+
20
+ ## 8.1.0-canary.48
21
+
22
+ ## 8.1.0-canary.47
23
+
24
+ ## 8.1.0-canary.46
25
+
26
+ ## 8.1.0-canary.45
27
+
28
+ ### Patch Changes
29
+
30
+ - [#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
31
+ ([@paales](https://github.com/paales))
32
+
33
+ ## 8.1.0-canary.44
34
+
35
+ ## 8.1.0-canary.43
36
+
37
+ ## 8.1.0-canary.42
38
+
39
+ ## 8.1.0-canary.41
40
+
41
+ ## 8.1.0-canary.40
42
+
43
+ ## 8.1.0-canary.39
44
+
45
+ ## 8.1.0-canary.38
46
+
47
+ ## 8.1.0-canary.37
48
+
49
+ ## 8.1.0-canary.36
50
+
51
+ ## 8.1.0-canary.35
52
+
53
+ ## 8.1.0-canary.34
54
+
55
+ ## 8.1.0-canary.33
56
+
57
+ ## 8.1.0-canary.32
58
+
59
+ ## 8.1.0-canary.31
60
+
61
+ ## 8.1.0-canary.30
62
+
63
+ ## 8.1.0-canary.29
64
+
65
+ ## 8.1.0-canary.28
66
+
67
+ ## 8.1.0-canary.27
68
+
69
+ ## 8.1.0-canary.26
70
+
71
+ ## 8.1.0-canary.25
72
+
73
+ ## 8.1.0-canary.24
74
+
75
+ ## 8.1.0-canary.23
76
+
77
+ ## 8.1.0-canary.22
78
+
79
+ ## 8.1.0-canary.21
80
+
81
+ ## 8.1.0-canary.20
82
+
83
+ ### Minor Changes
84
+
85
+ - [#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
86
+ ([@Jessevdpoel](https://github.com/Jessevdpoel))
87
+
88
+ ### Patch Changes
89
+
90
+ - [#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.
91
+ ([@Jessevdpoel](https://github.com/Jessevdpoel))
92
+
93
+ ## 8.1.0-canary.19
94
+
95
+ ## 8.1.0-canary.18
96
+
97
+ ## 8.1.0-canary.17
98
+
99
+ ### Minor Changes
100
+
101
+ - [#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.
102
+ ([@Jessevdpoel](https://github.com/Jessevdpoel))
103
+
104
+ ## 8.1.0-canary.16
105
+
106
+ ## 8.1.0-canary.15
107
+
108
+ ## 8.1.0-canary.14
109
+
110
+ ## 8.1.0-canary.13
111
+
112
+ ## 8.1.0-canary.12
113
+
114
+ ## 8.1.0-canary.11
115
+
116
+ ## 8.1.0-canary.10
117
+
3
118
  ## 8.1.0-canary.9
4
119
 
5
120
  ## 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/react'
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 id='Complete your purchase' />}
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
- uid={uid}
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 { Box, Link } from '@mui/material'
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 const productImageSizes = {
21
- small: responsiveVal(60, 80),
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 = 'large', readOnly = false, ...rest } = props
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
- '& .MuiBox-root': {
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
- typography: typographySizes[size],
79
- display: 'flex',
80
- alignItems: 'center',
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: productImageSizes[size],
101
- height: productImageSizes[size],
97
+ width: actionCardImageSizes[size],
98
+ height: actionCardImageSizes[size],
102
99
  display: 'block',
103
100
  borderRadius: 1,
104
101
  objectFit: 'contain',
105
102
  }}
106
- sizes={productImageSizes[size]}
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
- {readOnly ? quantity : <UpdateItemQuantity uid={uid} quantity={quantity} />}
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
- <Money value={price} currency={prices?.price.currency} />
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={!readOnly && <RemoveItemFromCart {...cartItem} buttonProps={{ size }} />}
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?.filter(nonNullable).map((item) => (
51
- <CartItemActionCard
52
- key={item.uid}
53
- cartItem={item}
54
- layout={layout}
55
- size={size}
56
- variant={variant}
57
- {...itemProps}
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,8 @@
1
+ fragment EditCartItemForm on ProductInterface {
2
+ ...ProductCustomizable
3
+ ...ProductWeight
4
+ ...ProductPageItem
5
+ ...ConfigurableOptions
6
+ ...DownloadableProductOptions
7
+ ...BundleProductOptions
8
+ }
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ export * from './EditCartItemButton/EditCartItemButton'
2
+ export * from './EditCartItemForm/EditCartItemForm'
3
+ export * from './EditCartItemForm/EditCartItemForm.gql'
@@ -1,12 +1,13 @@
1
- import { ApolloCartErrorSnackbar, useFormGqlMutationCart } from '@graphcommerce/magento-cart'
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 type { DistributedOmit } from 'type-fest'
6
- import { CartItemFragment } from '../../Api/CartItem.gql'
7
- import { RemoveItemFromCartDocument } from './RemoveItemFromCart.gql'
5
+ import {
6
+ UseRemoveItemFromCartProps,
7
+ useRemoveItemFromCart,
8
+ } from '../../hooks/useRemoveItemFromCart'
8
9
 
9
- export type RemoveItemFromCartProps = DistributedOmit<CartItemFragment, '__typename'> & {
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={submitHandler} {...formProps}>
22
+ <Form noValidate onSubmit={submit} {...formProps}>
24
23
  <Button
25
24
  variant='inline'
26
25
  color='secondary'
@@ -1,11 +1,13 @@
1
- import { ApolloCartErrorSnackbar, useFormGqlMutationCart } from '@graphcommerce/magento-cart'
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 { CartItemFragment } from '../../Api/CartItem.gql'
6
- import { RemoveItemFromCartDocument } from './RemoveItemFromCart.gql'
5
+ import {
6
+ UseRemoveItemFromCartProps,
7
+ useRemoveItemFromCart,
8
+ } from '../../hooks/useRemoveItemFromCart'
7
9
 
8
- export type RemoveItemFromCartFabProps = Omit<CartItemFragment, '__typename'> & {
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
- const form = useFormGqlMutationCart(RemoveItemFromCartDocument, { defaultValues: { uid } })
18
- const { handleSubmit, formState, error } = form
19
- const submitHandler = handleSubmit(() => {})
19
+
20
+ const { submit, formState, error } = useRemoveItemFromCart(props)
20
21
 
21
22
  return (
22
- <Form noValidate onSubmit={submitHandler} {...formProps}>
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, configurable_customizable } = props
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 key={option.customizable_option_uid} sx={{ color: 'text.primary' }}>
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={option.customizable_option_uid}
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} initialWait={0} />
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": "8.1.0-canary.9",
5
+ "version": "9.0.0-canary.54",
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.9",
16
- "@graphcommerce/graphql": "^8.1.0-canary.9",
17
- "@graphcommerce/image": "^8.1.0-canary.9",
18
- "@graphcommerce/magento-cart": "^8.1.0-canary.9",
19
- "@graphcommerce/magento-customer": "^8.1.0-canary.9",
20
- "@graphcommerce/magento-product": "^8.1.0-canary.9",
21
- "@graphcommerce/magento-store": "^8.1.0-canary.9",
22
- "@graphcommerce/next-ui": "^8.1.0-canary.9",
23
- "@graphcommerce/prettier-config-pwa": "^8.1.0-canary.9",
24
- "@graphcommerce/react-hook-form": "^8.1.0-canary.9",
25
- "@graphcommerce/typescript-config-pwa": "^8.1.0-canary.9",
15
+ "@graphcommerce/eslint-config-pwa": "^9.0.0-canary.54",
16
+ "@graphcommerce/framer-next-pages": "^9.0.0-canary.54",
17
+ "@graphcommerce/graphql": "^9.0.0-canary.54",
18
+ "@graphcommerce/image": "^9.0.0-canary.54",
19
+ "@graphcommerce/magento-cart": "^9.0.0-canary.54",
20
+ "@graphcommerce/magento-customer": "^9.0.0-canary.54",
21
+ "@graphcommerce/magento-product": "^9.0.0-canary.54",
22
+ "@graphcommerce/magento-store": "^9.0.0-canary.54",
23
+ "@graphcommerce/next-ui": "^9.0.0-canary.54",
24
+ "@graphcommerce/prettier-config-pwa": "^9.0.0-canary.54",
25
+ "@graphcommerce/react-hook-form": "^9.0.0-canary.54",
26
+ "@graphcommerce/typescript-config-pwa": "^9.0.0-canary.54",
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
+ }