@graphcommerce/magento-cart 4.6.9 → 4.7.0
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 +30 -0
- package/components/ApolloCartError/ApolloCartErrorAlert.tsx +1 -23
- package/components/ApolloCartError/ApolloCartErrorFullPage.tsx +1 -38
- package/components/ApolloCartError/ApolloCartErrorSnackbar.tsx +1 -19
- package/components/CartDebugger/CartDebugger.tsx +38 -0
- package/components/CartItemSummary/CartItemSummary.tsx +8 -5
- package/components/CartSummary/CartSummary.tsx +32 -6
- package/components/CartTotals/CartTotals.tsx +114 -124
- package/hooks/useCartQuery.ts +3 -13
- package/index.ts +2 -0
- package/link/createCartErrorLink.ts +71 -0
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 4.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1602](https://github.com/graphcommerce-org/graphcommerce/pull/1602) [`5f781a217`](https://github.com/graphcommerce-org/graphcommerce/commit/5f781a217ce63ed56bc1a9983487b04400a8a315) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - Default styles and layout fixes
|
|
8
|
+
|
|
9
|
+
- Scaled icons and fonts down. Size in typography is now more gradual: https://graphcommerce.vercel.app/test/typography
|
|
10
|
+
- Multiple accessibility fixes. Missing button/input labels, and fixed spacing issues resulting in high % appropriately sized tap targets
|
|
11
|
+
- Replaced responsiveVal usage with better performaning breakpointVal where possible
|
|
12
|
+
- All buttons are now Pill by default.
|
|
13
|
+
- Cleaned up checkout styles
|
|
14
|
+
|
|
15
|
+
* [#1608](https://github.com/graphcommerce-org/graphcommerce/pull/1608) [`ac6eedbb1`](https://github.com/graphcommerce-org/graphcommerce/commit/ac6eedbb14d3abd8cf1231a98dc2a8b7f4659f1f) Thanks [@mikekeehnen](https://github.com/mikekeehnen)! - Added global errorhandling on cart errors. When a cart query return an error, the currentCartId wil be renewed with the actual cartId when the user is authenticated. When there is an error in a guest cart, a new cartId will be generated and the cart will be empty
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [#1609](https://github.com/graphcommerce-org/graphcommerce/pull/1609) [`e573278e4`](https://github.com/graphcommerce-org/graphcommerce/commit/e573278e43506a6b17a2981e61d0e9fad41eb2eb) Thanks [@ErwinOtten](https://github.com/ErwinOtten)! - Remove Cart error message when there isn’t a cart present yet
|
|
20
|
+
|
|
21
|
+
- Updated dependencies [[`04708dacc`](https://github.com/graphcommerce-org/graphcommerce/commit/04708daccc213c6ea927bc67fa3bd0d5b1fad619), [`bb94e7045`](https://github.com/graphcommerce-org/graphcommerce/commit/bb94e7045460cb671c45d612a0833731d7c20c30), [`b0dc4e2e1`](https://github.com/graphcommerce-org/graphcommerce/commit/b0dc4e2e1982d502d38dd50a0f493396360a7a15), [`4a5286dfe`](https://github.com/graphcommerce-org/graphcommerce/commit/4a5286dfeaa1719e594a0078f274fbab53969c4e), [`0ad5159eb`](https://github.com/graphcommerce-org/graphcommerce/commit/0ad5159ebef54b4ce7fee6f71b4bf710dba9ef8e), [`d46d5ed0c`](https://github.com/graphcommerce-org/graphcommerce/commit/d46d5ed0cc5794391b7527fc17bbb68ec2212e33), [`5f781a217`](https://github.com/graphcommerce-org/graphcommerce/commit/5f781a217ce63ed56bc1a9983487b04400a8a315), [`01372b918`](https://github.com/graphcommerce-org/graphcommerce/commit/01372b918a291e01cbf5db40edcb40fb1c2af313)]:
|
|
22
|
+
- @graphcommerce/next-ui@4.22.0
|
|
23
|
+
- @graphcommerce/ecommerce-ui@1.2.0
|
|
24
|
+
- @graphcommerce/magento-customer@4.10.0
|
|
25
|
+
- @graphcommerce/framer-next-pages@3.3.0
|
|
26
|
+
- @graphcommerce/framer-utils@3.2.0
|
|
27
|
+
- @graphcommerce/magento-store@4.2.28
|
|
28
|
+
- @graphcommerce/framer-scroller@2.1.33
|
|
29
|
+
- @graphcommerce/graphql@3.4.7
|
|
30
|
+
- @graphcommerce/image@3.1.9
|
|
31
|
+
- @graphcommerce/magento-graphql@3.1.7
|
|
32
|
+
|
|
3
33
|
## 4.6.9
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
|
@@ -2,31 +2,9 @@ import {
|
|
|
2
2
|
ApolloCustomerErrorAlert,
|
|
3
3
|
ApolloCustomerErrorAlertProps,
|
|
4
4
|
} from '@graphcommerce/magento-customer'
|
|
5
|
-
import { graphqlErrorByCategory } from '@graphcommerce/magento-graphql'
|
|
6
|
-
import { Trans } from '@lingui/react'
|
|
7
|
-
import { Button } from '@mui/material'
|
|
8
|
-
import { useClearCurrentCartId } from '../../hooks/useClearCurrentCartId'
|
|
9
5
|
|
|
10
6
|
export type ApolloCartErrorAlertProps = ApolloCustomerErrorAlertProps
|
|
11
7
|
|
|
12
8
|
export function ApolloCartErrorAlert(props: ApolloCartErrorAlertProps) {
|
|
13
|
-
|
|
14
|
-
const clear = useClearCurrentCartId()
|
|
15
|
-
|
|
16
|
-
const [, noSuchEntity] = graphqlErrorByCategory({ category: 'graphql-no-such-entity', error })
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<ApolloCustomerErrorAlert
|
|
20
|
-
{...props}
|
|
21
|
-
graphqlErrorAlertProps={{
|
|
22
|
-
action: noSuchEntity ? (
|
|
23
|
-
<Button onClick={clear}>
|
|
24
|
-
<Trans id='Reset Cart' />
|
|
25
|
-
</Button>
|
|
26
|
-
) : (
|
|
27
|
-
graphqlErrorAlertProps?.action
|
|
28
|
-
),
|
|
29
|
-
}}
|
|
30
|
-
/>
|
|
31
|
-
)
|
|
9
|
+
return <ApolloCustomerErrorAlert {...props} />
|
|
32
10
|
}
|
|
@@ -2,49 +2,12 @@ import {
|
|
|
2
2
|
ApolloCustomerErrorFullPage,
|
|
3
3
|
ApolloCustomerErrorFullPageProps,
|
|
4
4
|
} from '@graphcommerce/magento-customer'
|
|
5
|
-
import { graphqlErrorByCategory } from '@graphcommerce/magento-graphql'
|
|
6
5
|
import { iconShoppingBag, IconSvg } from '@graphcommerce/next-ui'
|
|
7
|
-
import { Trans } from '@lingui/react'
|
|
8
|
-
import { Button } from '@mui/material'
|
|
9
|
-
import { useCurrentCartId } from '../../hooks'
|
|
10
|
-
import { useClearCurrentCartId } from '../../hooks/useClearCurrentCartId'
|
|
11
|
-
import { EmptyCart } from '../EmptyCart/EmptyCart'
|
|
12
6
|
|
|
13
7
|
export type ApolloCartErrorFullPageProps = Omit<ApolloCustomerErrorFullPageProps, 'icon'>
|
|
14
8
|
|
|
15
9
|
export function ApolloCartErrorFullPage(props: ApolloCartErrorFullPageProps) {
|
|
16
|
-
const { error, button } = props
|
|
17
|
-
const clear = useClearCurrentCartId()
|
|
18
|
-
const { currentCartId } = useCurrentCartId()
|
|
19
|
-
|
|
20
|
-
const [, noSuchEntity] = graphqlErrorByCategory({ category: 'graphql-no-such-entity', error })
|
|
21
|
-
|
|
22
|
-
if (noSuchEntity)
|
|
23
|
-
return (
|
|
24
|
-
<EmptyCart
|
|
25
|
-
button={
|
|
26
|
-
currentCartId ? (
|
|
27
|
-
<Button onClick={clear}>
|
|
28
|
-
<Trans id='Reset Cart' />
|
|
29
|
-
</Button>
|
|
30
|
-
) : undefined
|
|
31
|
-
}
|
|
32
|
-
/>
|
|
33
|
-
)
|
|
34
|
-
|
|
35
10
|
return (
|
|
36
|
-
<ApolloCustomerErrorFullPage
|
|
37
|
-
{...props}
|
|
38
|
-
icon={<IconSvg src={iconShoppingBag} size='xxl' />}
|
|
39
|
-
button={
|
|
40
|
-
noSuchEntity ? (
|
|
41
|
-
<Button onClick={clear}>
|
|
42
|
-
<Trans id='Reset Cart' />
|
|
43
|
-
</Button>
|
|
44
|
-
) : (
|
|
45
|
-
button
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
/>
|
|
11
|
+
<ApolloCustomerErrorFullPage {...props} icon={<IconSvg src={iconShoppingBag} size='xxl' />} />
|
|
49
12
|
)
|
|
50
13
|
}
|
|
@@ -10,23 +10,5 @@ import { useClearCurrentCartId } from '../../hooks/useClearCurrentCartId'
|
|
|
10
10
|
export type ApolloCartErrorSnackbarProps = ApolloCustomerErrorSnackbarProps
|
|
11
11
|
|
|
12
12
|
export function ApolloCartErrorSnackbar(props: ApolloCartErrorSnackbarProps) {
|
|
13
|
-
|
|
14
|
-
const clear = useClearCurrentCartId()
|
|
15
|
-
|
|
16
|
-
const [, noSuchEntity] = graphqlErrorByCategory({ category: 'graphql-no-such-entity', error })
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<ApolloCustomerErrorSnackbar
|
|
20
|
-
{...props}
|
|
21
|
-
action={
|
|
22
|
-
noSuchEntity ? (
|
|
23
|
-
<Button onClick={clear} variant='pill' color='secondary'>
|
|
24
|
-
<Trans id='Reset Cart' />
|
|
25
|
-
</Button>
|
|
26
|
-
) : (
|
|
27
|
-
action
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
/>
|
|
31
|
-
)
|
|
13
|
+
return <ApolloCustomerErrorSnackbar {...props} />
|
|
32
14
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useApolloClient } from '@graphcommerce/graphql'
|
|
2
|
+
import { Button } from '@mui/material'
|
|
3
|
+
import { CurrentCartIdDocument } from '../../hooks/CurrentCartId.gql'
|
|
4
|
+
|
|
5
|
+
export function CartDebugger() {
|
|
6
|
+
const client = useApolloClient()
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div style={{ position: 'fixed', bottom: 0, right: 0, zIndex: 1, opacity: 0.3 }}>
|
|
10
|
+
<Button
|
|
11
|
+
type='button'
|
|
12
|
+
variant='text'
|
|
13
|
+
size='small'
|
|
14
|
+
onClick={() => {
|
|
15
|
+
const currentCardId = client.readQuery({ query: CurrentCartIdDocument })
|
|
16
|
+
if (!currentCardId?.currentCartId) {
|
|
17
|
+
console.log('No customerToken available, nothing to break')
|
|
18
|
+
} else {
|
|
19
|
+
console.log(`Changing current token to a random one)`)
|
|
20
|
+
|
|
21
|
+
client.writeQuery({
|
|
22
|
+
query: CurrentCartIdDocument,
|
|
23
|
+
data: {
|
|
24
|
+
currentCartId: {
|
|
25
|
+
...currentCardId.currentCartId,
|
|
26
|
+
id: `${Math.random().toString(36).slice(2)}random-cardId`,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
broadcast: true,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
}}
|
|
33
|
+
>
|
|
34
|
+
break cart
|
|
35
|
+
</Button>
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
SectionContainer,
|
|
8
8
|
IconSvg,
|
|
9
9
|
extendableComponent,
|
|
10
|
+
breakpointVal,
|
|
10
11
|
} from '@graphcommerce/next-ui'
|
|
11
12
|
import { Trans } from '@lingui/react'
|
|
12
13
|
import { Box, Divider, SxProps, Theme } from '@mui/material'
|
|
@@ -47,7 +48,12 @@ export function CartItemSummary(props: OrderSummaryProps) {
|
|
|
47
48
|
(theme) => ({
|
|
48
49
|
padding: `${theme.spacings.sm} ${theme.spacings.sm}`,
|
|
49
50
|
border: `1px ${theme.palette.divider} solid`,
|
|
50
|
-
|
|
51
|
+
...breakpointVal(
|
|
52
|
+
'borderRadius',
|
|
53
|
+
theme.shape.borderRadius * 2,
|
|
54
|
+
theme.shape.borderRadius * 3,
|
|
55
|
+
theme.breakpoints.values,
|
|
56
|
+
),
|
|
51
57
|
}),
|
|
52
58
|
...(Array.isArray(sx) ? sx : [sx]),
|
|
53
59
|
]}
|
|
@@ -65,9 +71,6 @@ export function CartItemSummary(props: OrderSummaryProps) {
|
|
|
65
71
|
<Box
|
|
66
72
|
className={classes.imageScrollerContainer}
|
|
67
73
|
sx={(theme) => ({
|
|
68
|
-
display: 'flex',
|
|
69
|
-
alignItems: 'center',
|
|
70
|
-
gap: theme.spacings.sm,
|
|
71
74
|
position: 'relative',
|
|
72
75
|
})}
|
|
73
76
|
>
|
|
@@ -137,7 +140,7 @@ export function CartItemSummary(props: OrderSummaryProps) {
|
|
|
137
140
|
/>
|
|
138
141
|
<CartTotals
|
|
139
142
|
sx={(theme) => ({
|
|
140
|
-
background:
|
|
143
|
+
background: 'none',
|
|
141
144
|
padding: 0,
|
|
142
145
|
})}
|
|
143
146
|
/>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useHistoryLink } from '@graphcommerce/framer-next-pages'
|
|
2
|
-
import { SectionContainer, extendableComponent } from '@graphcommerce/next-ui'
|
|
2
|
+
import { SectionContainer, extendableComponent, breakpointVal } from '@graphcommerce/next-ui'
|
|
3
3
|
import { Trans } from '@lingui/react'
|
|
4
|
-
import { Box, Link, SxProps, Theme, Typography } from '@mui/material'
|
|
4
|
+
import { Box, Link, SxProps, Theme, Typography, lighten } from '@mui/material'
|
|
5
5
|
import PageLink from 'next/link'
|
|
6
6
|
import React from 'react'
|
|
7
7
|
import { useCartQuery } from '../../hooks'
|
|
@@ -37,8 +37,20 @@ export function CartSummary(props: CartSummaryProps) {
|
|
|
37
37
|
sx={[
|
|
38
38
|
(theme) => ({
|
|
39
39
|
margin: `${theme.spacings.sm} 0`,
|
|
40
|
-
'& div:last-of-type': {
|
|
41
|
-
borderRadius: '0
|
|
40
|
+
'& > div:last-of-type': {
|
|
41
|
+
borderRadius: '0',
|
|
42
|
+
...breakpointVal(
|
|
43
|
+
'borderBottomLeftRadius',
|
|
44
|
+
theme.shape.borderRadius * 2,
|
|
45
|
+
theme.shape.borderRadius * 3,
|
|
46
|
+
theme.breakpoints.values,
|
|
47
|
+
),
|
|
48
|
+
...breakpointVal(
|
|
49
|
+
'borderBottomRightRadius',
|
|
50
|
+
theme.shape.borderRadius * 2,
|
|
51
|
+
theme.shape.borderRadius * 3,
|
|
52
|
+
theme.breakpoints.values,
|
|
53
|
+
),
|
|
42
54
|
},
|
|
43
55
|
}),
|
|
44
56
|
...(Array.isArray(sx) ? sx : [sx]),
|
|
@@ -47,8 +59,22 @@ export function CartSummary(props: CartSummaryProps) {
|
|
|
47
59
|
<Box
|
|
48
60
|
className={classes.detailsContainer}
|
|
49
61
|
sx={(theme) => ({
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
...breakpointVal(
|
|
63
|
+
'borderTopLeftRadius',
|
|
64
|
+
theme.shape.borderRadius * 2,
|
|
65
|
+
theme.shape.borderRadius * 3,
|
|
66
|
+
theme.breakpoints.values,
|
|
67
|
+
),
|
|
68
|
+
...breakpointVal(
|
|
69
|
+
'borderTopRightRadius',
|
|
70
|
+
theme.shape.borderRadius * 2,
|
|
71
|
+
theme.shape.borderRadius * 3,
|
|
72
|
+
theme.breakpoints.values,
|
|
73
|
+
),
|
|
74
|
+
background:
|
|
75
|
+
theme.palette.mode === 'light'
|
|
76
|
+
? theme.palette.background.default
|
|
77
|
+
: lighten(theme.palette.background.default, 0.15),
|
|
52
78
|
padding: theme.spacings.sm,
|
|
53
79
|
gridColumnGap: theme.spacings.xxl,
|
|
54
80
|
gridRowGap: theme.spacings.sm,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Money } from '@graphcommerce/magento-store'
|
|
2
|
-
import {
|
|
2
|
+
import { extendableComponent, breakpointVal } from '@graphcommerce/next-ui'
|
|
3
3
|
import { Trans } from '@lingui/react'
|
|
4
4
|
import { Box, Divider, lighten, SxProps, Theme } from '@mui/material'
|
|
5
|
-
import { AnimatePresence } from 'framer-motion'
|
|
6
5
|
import { useRouter } from 'next/router'
|
|
7
6
|
import { useCartQuery, useDisplayInclTax } from '../../hooks'
|
|
8
7
|
import { GetCartTotalsDocument } from './GetCartTotals.gql'
|
|
@@ -50,15 +49,19 @@ export function CartTotals(props: CartTotalsProps) {
|
|
|
50
49
|
)
|
|
51
50
|
|
|
52
51
|
return (
|
|
53
|
-
<
|
|
54
|
-
layout={animateLayout}
|
|
52
|
+
<Box
|
|
55
53
|
className={classes.root}
|
|
56
54
|
sx={[
|
|
57
55
|
(theme) => ({
|
|
58
|
-
|
|
56
|
+
...breakpointVal(
|
|
57
|
+
'borderRadius',
|
|
58
|
+
theme.shape.borderRadius * 3,
|
|
59
|
+
theme.shape.borderRadius * 5,
|
|
60
|
+
theme.breakpoints.values,
|
|
61
|
+
),
|
|
59
62
|
background:
|
|
60
63
|
theme.palette.mode === 'light'
|
|
61
|
-
?
|
|
64
|
+
? theme.palette.background.default
|
|
62
65
|
: lighten(theme.palette.background.default, 0.15),
|
|
63
66
|
padding: `${theme.spacings.xs} ${theme.spacings.sm}`,
|
|
64
67
|
|
|
@@ -70,137 +73,124 @@ export function CartTotals(props: CartTotalsProps) {
|
|
|
70
73
|
]}
|
|
71
74
|
key='total-costs'
|
|
72
75
|
>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
{prices?.subtotal_including_tax && (
|
|
77
|
+
<Box
|
|
78
|
+
className={classes.costsRow}
|
|
79
|
+
key='subtotal'
|
|
80
|
+
sx={{ display: 'flex', justifyContent: 'space-between' }}
|
|
81
|
+
>
|
|
82
|
+
<Box>
|
|
83
|
+
<Trans id='Products' />
|
|
84
|
+
</Box>
|
|
85
|
+
<Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
|
|
86
|
+
<Money {...(inclTax ? prices.subtotal_including_tax : prices.subtotal_excluding_tax)} />
|
|
87
|
+
</Box>
|
|
88
|
+
</Box>
|
|
89
|
+
)}
|
|
90
|
+
|
|
91
|
+
{prices?.discounts?.map((discount) => {
|
|
92
|
+
const value = inclTax
|
|
93
|
+
? (discount?.amount.value ?? 0) * -1
|
|
94
|
+
: (discount?.amount.value ?? 0) *
|
|
95
|
+
((prices.subtotal_excluding_tax?.value ?? 1) /
|
|
96
|
+
(prices.subtotal_including_tax?.value ?? 1)) *
|
|
97
|
+
-1
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<Box
|
|
101
|
+
key={discount?.label}
|
|
102
|
+
sx={{
|
|
103
|
+
display: 'flex',
|
|
104
|
+
justifyContent: 'space-between',
|
|
105
|
+
}}
|
|
80
106
|
>
|
|
81
|
-
<Box>
|
|
82
|
-
<Trans id='Products' />
|
|
83
|
-
</Box>
|
|
107
|
+
<Box>{discount?.label}</Box>
|
|
84
108
|
<Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
|
|
85
|
-
<Money
|
|
86
|
-
{...(inclTax ? prices.subtotal_including_tax : prices.subtotal_excluding_tax)}
|
|
87
|
-
/>
|
|
109
|
+
{discount?.amount && <Money {...discount.amount} value={value} />}
|
|
88
110
|
</Box>
|
|
89
|
-
</
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
{shippingMethod && (
|
|
119
|
-
<AnimatedRow
|
|
120
|
-
layout={animateLayout}
|
|
111
|
+
</Box>
|
|
112
|
+
)
|
|
113
|
+
})}
|
|
114
|
+
|
|
115
|
+
{shippingMethod && (
|
|
116
|
+
<Box
|
|
117
|
+
className={classes.costsRow}
|
|
118
|
+
key='shippingMethod'
|
|
119
|
+
sx={{ display: 'flex', justifyContent: 'space-between' }}
|
|
120
|
+
>
|
|
121
|
+
<Box>
|
|
122
|
+
<Trans
|
|
123
|
+
id='Shipping ({0} {1})'
|
|
124
|
+
values={{ 0: shippingMethod.carrier_title, 1: shippingMethod.method_title }}
|
|
125
|
+
/>
|
|
126
|
+
</Box>
|
|
127
|
+
<Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
|
|
128
|
+
<Money
|
|
129
|
+
{...(inclTax
|
|
130
|
+
? shippingMethodPrices?.price_incl_tax
|
|
131
|
+
: shippingMethodPrices?.price_excl_tax)}
|
|
132
|
+
/>
|
|
133
|
+
</Box>
|
|
134
|
+
</Box>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
{!inclTax &&
|
|
138
|
+
prices?.applied_taxes?.map((tax) => (
|
|
139
|
+
<Box
|
|
121
140
|
className={classes.costsRow}
|
|
122
|
-
key=
|
|
123
|
-
sx={{ display: 'flex', justifyContent: 'space-between'
|
|
141
|
+
key={`excl${tax?.label}`}
|
|
142
|
+
sx={{ display: 'flex', justifyContent: 'space-between' }}
|
|
124
143
|
>
|
|
125
|
-
<Box>
|
|
126
|
-
<Trans
|
|
127
|
-
id='Shipping ({0} {1})'
|
|
128
|
-
values={{ 0: shippingMethod.carrier_title, 1: shippingMethod.method_title }}
|
|
129
|
-
/>
|
|
130
|
-
</Box>
|
|
144
|
+
<Box>{tax?.label}</Box>
|
|
131
145
|
<Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
|
|
132
|
-
<Money
|
|
133
|
-
{...(inclTax
|
|
134
|
-
? shippingMethodPrices?.price_incl_tax
|
|
135
|
-
: shippingMethodPrices?.price_excl_tax)}
|
|
136
|
-
/>
|
|
146
|
+
<Money {...tax?.amount} />
|
|
137
147
|
</Box>
|
|
138
|
-
</
|
|
139
|
-
)}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
148
|
+
</Box>
|
|
149
|
+
))}
|
|
150
|
+
|
|
151
|
+
<Box key='divider'>
|
|
152
|
+
<Divider className={classes.costsDivider} sx={{ margin: `1em 0` }} />
|
|
153
|
+
</Box>
|
|
154
|
+
|
|
155
|
+
{prices?.grand_total && (
|
|
156
|
+
<Box
|
|
157
|
+
className={`${classes.costsRow} ${classes.costsGrandTotal}`}
|
|
158
|
+
key='grand_total'
|
|
159
|
+
sx={(theme) => ({
|
|
160
|
+
display: 'flex',
|
|
161
|
+
justifyContent: 'space-between',
|
|
162
|
+
color: theme.palette.primary.main,
|
|
163
|
+
})}
|
|
164
|
+
>
|
|
165
|
+
<Box>
|
|
166
|
+
<Trans id='Grand total' />
|
|
167
|
+
</Box>
|
|
168
|
+
<Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
|
|
169
|
+
<Money {...prices.grand_total} />
|
|
170
|
+
</Box>
|
|
171
|
+
</Box>
|
|
172
|
+
)}
|
|
173
|
+
|
|
174
|
+
{inclTax &&
|
|
175
|
+
prices?.applied_taxes?.map((tax) => (
|
|
176
|
+
<Box
|
|
177
|
+
className={`${classes.costsRow} ${classes.costsTax}`}
|
|
178
|
+
key={`incl${tax?.label}`}
|
|
179
|
+
sx={{
|
|
166
180
|
display: 'flex',
|
|
167
181
|
justifyContent: 'space-between',
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
182
|
+
color: 'text.disabled',
|
|
183
|
+
paddingTop: 0,
|
|
184
|
+
}}
|
|
171
185
|
>
|
|
172
186
|
<Box>
|
|
173
|
-
<Trans id='
|
|
187
|
+
<Trans id='Including {0}' values={{ 0: tax?.label }} />
|
|
174
188
|
</Box>
|
|
175
189
|
<Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
|
|
176
|
-
<Money {...
|
|
190
|
+
<Money {...tax?.amount} />
|
|
177
191
|
</Box>
|
|
178
|
-
</
|
|
179
|
-
)}
|
|
180
|
-
|
|
181
|
-
{inclTax &&
|
|
182
|
-
prices?.applied_taxes?.map((tax) => (
|
|
183
|
-
<AnimatedRow
|
|
184
|
-
layout={animateLayout}
|
|
185
|
-
className={`${classes.costsRow} ${classes.costsTax}`}
|
|
186
|
-
key={`incl${tax?.label}`}
|
|
187
|
-
sx={{
|
|
188
|
-
display: 'flex',
|
|
189
|
-
justifyContent: 'space-between',
|
|
190
|
-
typography: 'body1',
|
|
191
|
-
color: 'text.disabled',
|
|
192
|
-
paddingTop: 0,
|
|
193
|
-
}}
|
|
194
|
-
>
|
|
195
|
-
<Box>
|
|
196
|
-
<Trans id='Including {0}' values={{ 0: tax?.label }} />
|
|
197
|
-
</Box>
|
|
198
|
-
<Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
|
|
199
|
-
<Money {...tax?.amount} />
|
|
200
|
-
</Box>
|
|
201
|
-
</AnimatedRow>
|
|
202
|
-
))}
|
|
203
|
-
</AnimatePresence>
|
|
204
|
-
</AnimatedRow>
|
|
192
|
+
</Box>
|
|
193
|
+
))}
|
|
194
|
+
</Box>
|
|
205
195
|
)
|
|
206
196
|
}
|
package/hooks/useCartQuery.ts
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
import { useQuery, TypedDocumentNode, QueryHookOptions
|
|
2
|
-
import { GraphQLError } from 'graphql'
|
|
1
|
+
import { useQuery, TypedDocumentNode, QueryHookOptions } from '@graphcommerce/graphql'
|
|
3
2
|
import { useRouter } from 'next/router'
|
|
4
3
|
import { useCurrentCartId } from './useCurrentCartId'
|
|
5
4
|
|
|
6
|
-
const noCartError = new ApolloError({
|
|
7
|
-
graphQLErrors: [
|
|
8
|
-
new GraphQLError('No cart found', {
|
|
9
|
-
extensions: { category: 'graphql-no-such-entity' },
|
|
10
|
-
}),
|
|
11
|
-
],
|
|
12
|
-
})
|
|
13
|
-
|
|
14
5
|
/**
|
|
15
6
|
* Requires the query to have a `$cartId: String!` argument. It will automatically inject the
|
|
16
7
|
* currently active cart_id.
|
|
@@ -30,7 +21,7 @@ export function useCartQuery<Q, V extends { cartId: string; [index: string]: unk
|
|
|
30
21
|
) {
|
|
31
22
|
const { allowUrl = true, hydration, ...queryOptions } = options
|
|
32
23
|
const router = useRouter()
|
|
33
|
-
const { currentCartId
|
|
24
|
+
const { currentCartId } = useCurrentCartId({ hydration })
|
|
34
25
|
|
|
35
26
|
const urlCartId = router.query.cart_id
|
|
36
27
|
const usingUrl = allowUrl && typeof urlCartId === 'string'
|
|
@@ -49,8 +40,7 @@ export function useCartQuery<Q, V extends { cartId: string; [index: string]: unk
|
|
|
49
40
|
const result = useQuery(document, queryOptions)
|
|
50
41
|
|
|
51
42
|
return {
|
|
52
|
-
...useQuery(document, queryOptions),
|
|
53
43
|
...result,
|
|
54
|
-
error: called && !currentCartId ? noCartError : result.error,
|
|
44
|
+
// error: called && !currentCartId ? noCartError : result.error,
|
|
55
45
|
}
|
|
56
46
|
}
|
package/index.ts
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ApolloClient,
|
|
3
|
+
FetchResult,
|
|
4
|
+
fromPromise,
|
|
5
|
+
InMemoryCache,
|
|
6
|
+
NormalizedCacheObject,
|
|
7
|
+
onError,
|
|
8
|
+
Operation,
|
|
9
|
+
} from '@graphcommerce/graphql'
|
|
10
|
+
import { RefObject } from 'react'
|
|
11
|
+
import { useAssignCurrentCartId, writeCartId } from '../hooks'
|
|
12
|
+
import { CreateEmptyCartDocument } from '../hooks/CreateEmptyCart.gql'
|
|
13
|
+
import { CurrentCartIdDocument } from '../hooks/CurrentCartId.gql'
|
|
14
|
+
|
|
15
|
+
type CartOperation = Operation & { variables: { cartId: string } }
|
|
16
|
+
function isCartOperation(operation: Operation): operation is CartOperation {
|
|
17
|
+
return typeof operation.variables.cartId === 'string'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function errorIsIncluded(errorPath: readonly (string | number)[] | undefined, keys: string[]) {
|
|
21
|
+
const error = errorPath?.join()
|
|
22
|
+
return keys.some((value) => value === error)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const createCartErrorLink = (clientRef: RefObject<ApolloClient<NormalizedCacheObject>>) =>
|
|
26
|
+
onError(({ graphQLErrors, operation, forward }) => {
|
|
27
|
+
if (!clientRef.current) return undefined
|
|
28
|
+
|
|
29
|
+
const client = clientRef.current
|
|
30
|
+
const { cache } = client
|
|
31
|
+
|
|
32
|
+
if (!isCartOperation(operation) || !graphQLErrors) return undefined
|
|
33
|
+
|
|
34
|
+
const cartErr = graphQLErrors.find(
|
|
35
|
+
(err) =>
|
|
36
|
+
err.extensions?.category === 'graphql-no-such-entity' &&
|
|
37
|
+
errorIsIncluded(err.path, [
|
|
38
|
+
'cart',
|
|
39
|
+
'addProductsToCart',
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* These mutations can also throw the graphql-no-such-entity exception, however, we're not
|
|
43
|
+
* sure if it also throws for other types of entities.
|
|
44
|
+
*/
|
|
45
|
+
// 'removeItemFromCart',
|
|
46
|
+
// 'setBillingAddressOnCart',
|
|
47
|
+
// 'setGuestEmailOnCart',
|
|
48
|
+
// 'setPaymentMethodOnCart',
|
|
49
|
+
// 'setShippingAddressesOnCart',
|
|
50
|
+
// 'setShippingMethodsOnCart',
|
|
51
|
+
// 'updateCartItems',
|
|
52
|
+
// 'applyCouponToCart',
|
|
53
|
+
// 'removeCouponFromCart'
|
|
54
|
+
]),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if (!cartErr) return undefined
|
|
58
|
+
|
|
59
|
+
return fromPromise(client?.mutate({ mutation: CreateEmptyCartDocument }))
|
|
60
|
+
.filter((value) => Boolean(value))
|
|
61
|
+
.flatMap((cartData) => {
|
|
62
|
+
const cartId = cartData.data?.createEmptyCart
|
|
63
|
+
if (!cartId) return forward(operation)
|
|
64
|
+
|
|
65
|
+
writeCartId(cache, cartId)
|
|
66
|
+
operation.variables = { ...operation.variables, cartId }
|
|
67
|
+
|
|
68
|
+
// retry the request, returning the new observable
|
|
69
|
+
return forward(operation)
|
|
70
|
+
})
|
|
71
|
+
})
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/magento-cart",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.7.0",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
8
|
"eslintConfig": {
|
|
@@ -18,16 +18,16 @@
|
|
|
18
18
|
"@playwright/test": "^1.21.1"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@graphcommerce/ecommerce-ui": "1.
|
|
22
|
-
"@graphcommerce/framer-utils": "3.
|
|
23
|
-
"@graphcommerce/framer-next-pages": "3.
|
|
24
|
-
"@graphcommerce/framer-scroller": "2.1.
|
|
25
|
-
"@graphcommerce/graphql": "3.4.
|
|
26
|
-
"@graphcommerce/image": "3.1.
|
|
27
|
-
"@graphcommerce/magento-customer": "4.
|
|
28
|
-
"@graphcommerce/magento-graphql": "3.1.
|
|
29
|
-
"@graphcommerce/magento-store": "4.2.
|
|
30
|
-
"@graphcommerce/next-ui": "4.
|
|
21
|
+
"@graphcommerce/ecommerce-ui": "1.2.0",
|
|
22
|
+
"@graphcommerce/framer-utils": "3.2.0",
|
|
23
|
+
"@graphcommerce/framer-next-pages": "3.3.0",
|
|
24
|
+
"@graphcommerce/framer-scroller": "2.1.33",
|
|
25
|
+
"@graphcommerce/graphql": "3.4.7",
|
|
26
|
+
"@graphcommerce/image": "3.1.9",
|
|
27
|
+
"@graphcommerce/magento-customer": "4.10.0",
|
|
28
|
+
"@graphcommerce/magento-graphql": "3.1.7",
|
|
29
|
+
"@graphcommerce/magento-store": "4.2.28",
|
|
30
|
+
"@graphcommerce/next-ui": "4.22.0",
|
|
31
31
|
"@graphcommerce/react-hook-form": "3.3.2"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|