@graphcommerce/magento-cart 4.6.7 → 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 +57 -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 +117 -117
- package/hooks/useCartQuery.ts +3 -13
- package/index.ts +2 -0
- package/link/createCartErrorLink.ts +71 -0
- package/package.json +12 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
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
|
+
|
|
33
|
+
## 4.6.9
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- Updated dependencies [[`1f7ee6f6c`](https://github.com/graphcommerce-org/graphcommerce/commit/1f7ee6f6cfb28544439ed36e10929ac530d1b2b7), [`707dbc73d`](https://github.com/graphcommerce-org/graphcommerce/commit/707dbc73d181204d88fdbbd2e09340e25b2b5f7b), [`5c5645e6e`](https://github.com/graphcommerce-org/graphcommerce/commit/5c5645e6eaf5314c063f05547707fcd4b34a8717)]:
|
|
38
|
+
- @graphcommerce/next-ui@4.21.0
|
|
39
|
+
- @graphcommerce/framer-utils@3.1.5
|
|
40
|
+
- @graphcommerce/graphql@3.4.6
|
|
41
|
+
- @graphcommerce/ecommerce-ui@1.1.12
|
|
42
|
+
- @graphcommerce/framer-scroller@2.1.32
|
|
43
|
+
- @graphcommerce/magento-customer@4.9.5
|
|
44
|
+
- @graphcommerce/magento-store@4.2.27
|
|
45
|
+
- @graphcommerce/framer-next-pages@3.2.5
|
|
46
|
+
- @graphcommerce/image@3.1.8
|
|
47
|
+
- @graphcommerce/magento-graphql@3.1.6
|
|
48
|
+
|
|
49
|
+
## 4.6.8
|
|
50
|
+
|
|
51
|
+
### Patch Changes
|
|
52
|
+
|
|
53
|
+
- Updated dependencies [[`43822fd61`](https://github.com/graphcommerce-org/graphcommerce/commit/43822fd61c949215b8ddce9fb37d09f29b638426), [`3a619b70d`](https://github.com/graphcommerce-org/graphcommerce/commit/3a619b70d082804b8de46a8e8232f9431479a8b7), [`3a619b70d`](https://github.com/graphcommerce-org/graphcommerce/commit/3a619b70d082804b8de46a8e8232f9431479a8b7)]:
|
|
54
|
+
- @graphcommerce/next-ui@4.20.0
|
|
55
|
+
- @graphcommerce/magento-customer@4.9.4
|
|
56
|
+
- @graphcommerce/ecommerce-ui@1.1.11
|
|
57
|
+
- @graphcommerce/framer-scroller@2.1.31
|
|
58
|
+
- @graphcommerce/magento-store@4.2.26
|
|
59
|
+
|
|
3
60
|
## 4.6.7
|
|
4
61
|
|
|
5
62
|
### 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,8 @@
|
|
|
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 {
|
|
5
|
+
import { useRouter } from 'next/router'
|
|
6
6
|
import { useCartQuery, useDisplayInclTax } from '../../hooks'
|
|
7
7
|
import { GetCartTotalsDocument } from './GetCartTotals.gql'
|
|
8
8
|
|
|
@@ -30,6 +30,8 @@ const { withState } = extendableComponent<OwnerProps, typeof name, typeof parts>
|
|
|
30
30
|
export function CartTotals(props: CartTotalsProps) {
|
|
31
31
|
const { data } = useCartQuery(GetCartTotalsDocument, { allowUrl: true })
|
|
32
32
|
const { containerMargin, sx = [] } = props
|
|
33
|
+
const { asPath } = useRouter()
|
|
34
|
+
const animateLayout = asPath === '/checkout/payment' ? undefined : true
|
|
33
35
|
|
|
34
36
|
const classes = withState({ containerMargin })
|
|
35
37
|
const inclTax = useDisplayInclTax()
|
|
@@ -47,14 +49,19 @@ export function CartTotals(props: CartTotalsProps) {
|
|
|
47
49
|
)
|
|
48
50
|
|
|
49
51
|
return (
|
|
50
|
-
<
|
|
52
|
+
<Box
|
|
51
53
|
className={classes.root}
|
|
52
54
|
sx={[
|
|
53
55
|
(theme) => ({
|
|
54
|
-
|
|
56
|
+
...breakpointVal(
|
|
57
|
+
'borderRadius',
|
|
58
|
+
theme.shape.borderRadius * 3,
|
|
59
|
+
theme.shape.borderRadius * 5,
|
|
60
|
+
theme.breakpoints.values,
|
|
61
|
+
),
|
|
55
62
|
background:
|
|
56
63
|
theme.palette.mode === 'light'
|
|
57
|
-
?
|
|
64
|
+
? theme.palette.background.default
|
|
58
65
|
: lighten(theme.palette.background.default, 0.15),
|
|
59
66
|
padding: `${theme.spacings.xs} ${theme.spacings.sm}`,
|
|
60
67
|
|
|
@@ -66,131 +73,124 @@ export function CartTotals(props: CartTotalsProps) {
|
|
|
66
73
|
]}
|
|
67
74
|
key='total-costs'
|
|
68
75
|
>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
+
}}
|
|
75
106
|
>
|
|
76
|
-
<Box>
|
|
77
|
-
<Trans id='Products' />
|
|
78
|
-
</Box>
|
|
107
|
+
<Box>{discount?.label}</Box>
|
|
79
108
|
<Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
|
|
80
|
-
<Money
|
|
81
|
-
{...(inclTax ? prices.subtotal_including_tax : prices.subtotal_excluding_tax)}
|
|
82
|
-
/>
|
|
109
|
+
{discount?.amount && <Money {...discount.amount} value={value} />}
|
|
83
110
|
</Box>
|
|
84
|
-
</
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
<AnimatedRow
|
|
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
|
|
114
140
|
className={classes.costsRow}
|
|
115
|
-
key=
|
|
116
|
-
sx={{ display: 'flex', justifyContent: 'space-between'
|
|
141
|
+
key={`excl${tax?.label}`}
|
|
142
|
+
sx={{ display: 'flex', justifyContent: 'space-between' }}
|
|
117
143
|
>
|
|
118
|
-
<Box>
|
|
119
|
-
<Trans
|
|
120
|
-
id='Shipping ({0} {1})'
|
|
121
|
-
values={{ 0: shippingMethod.carrier_title, 1: shippingMethod.method_title }}
|
|
122
|
-
/>
|
|
123
|
-
</Box>
|
|
144
|
+
<Box>{tax?.label}</Box>
|
|
124
145
|
<Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
|
|
125
|
-
<Money
|
|
126
|
-
{...(inclTax
|
|
127
|
-
? shippingMethodPrices?.price_incl_tax
|
|
128
|
-
: shippingMethodPrices?.price_excl_tax)}
|
|
129
|
-
/>
|
|
146
|
+
<Money {...tax?.amount} />
|
|
130
147
|
</Box>
|
|
131
|
-
</
|
|
132
|
-
)}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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={{
|
|
157
180
|
display: 'flex',
|
|
158
181
|
justifyContent: 'space-between',
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
182
|
+
color: 'text.disabled',
|
|
183
|
+
paddingTop: 0,
|
|
184
|
+
}}
|
|
162
185
|
>
|
|
163
186
|
<Box>
|
|
164
|
-
<Trans id='
|
|
187
|
+
<Trans id='Including {0}' values={{ 0: tax?.label }} />
|
|
165
188
|
</Box>
|
|
166
189
|
<Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
|
|
167
|
-
<Money {...
|
|
190
|
+
<Money {...tax?.amount} />
|
|
168
191
|
</Box>
|
|
169
|
-
</
|
|
170
|
-
)}
|
|
171
|
-
|
|
172
|
-
{inclTax &&
|
|
173
|
-
prices?.applied_taxes?.map((tax) => (
|
|
174
|
-
<AnimatedRow
|
|
175
|
-
className={`${classes.costsRow} ${classes.costsTax}`}
|
|
176
|
-
key={`incl${tax?.label}`}
|
|
177
|
-
sx={{
|
|
178
|
-
display: 'flex',
|
|
179
|
-
justifyContent: 'space-between',
|
|
180
|
-
typography: 'body1',
|
|
181
|
-
color: 'text.disabled',
|
|
182
|
-
paddingTop: 0,
|
|
183
|
-
}}
|
|
184
|
-
>
|
|
185
|
-
<Box>
|
|
186
|
-
<Trans id='Including {0}' values={{ 0: tax?.label }} />
|
|
187
|
-
</Box>
|
|
188
|
-
<Box className={classes.money} sx={{ whiteSpace: 'nowrap' }}>
|
|
189
|
-
<Money {...tax?.amount} />
|
|
190
|
-
</Box>
|
|
191
|
-
</AnimatedRow>
|
|
192
|
-
))}
|
|
193
|
-
</AnimatePresence>
|
|
194
|
-
</AnimatedRow>
|
|
192
|
+
</Box>
|
|
193
|
+
))}
|
|
194
|
+
</Box>
|
|
195
195
|
)
|
|
196
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": {
|
|
@@ -12,22 +12,22 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"@graphcommerce/eslint-config-pwa": "^4.1.
|
|
15
|
+
"@graphcommerce/eslint-config-pwa": "^4.1.10",
|
|
16
16
|
"@graphcommerce/prettier-config-pwa": "^4.0.6",
|
|
17
17
|
"@graphcommerce/typescript-config-pwa": "^4.0.4",
|
|
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": {
|