@graphcommerce/magento-cart 3.0.1

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.
Files changed (55) hide show
  1. package/Api/CartItemCountChanged.gql.ts +4 -0
  2. package/Api/CartItemCountChanged.graphql +9 -0
  3. package/CHANGELOG.md +324 -0
  4. package/README.md +102 -0
  5. package/components/AddToCartButton/AddToCartButton.tsx +127 -0
  6. package/components/ApolloCartError/ApolloCartErrorAlert.tsx +36 -0
  7. package/components/ApolloCartError/ApolloCartErrorFullPage.tsx +1 -0
  8. package/components/CartFab/CartFab.gql.ts +12 -0
  9. package/components/CartFab/CartFab.graphql +6 -0
  10. package/components/CartFab/CartFab.tsx +67 -0
  11. package/components/CartFab/CartTotalQuantity.gql.ts +4 -0
  12. package/components/CartFab/CartTotalQuantity.graphql +3 -0
  13. package/components/CartItemSummary/GetCartItemSummary.gql.ts +12 -0
  14. package/components/CartItemSummary/GetCartItemSummary.graphql +6 -0
  15. package/components/CartItemSummary/index.tsx +133 -0
  16. package/components/CartStartCheckout/CartStartCheckout.gql.ts +4 -0
  17. package/components/CartStartCheckout/CartStartCheckout.graphql +7 -0
  18. package/components/CartStartCheckout/CartStartCheckout.tsx +63 -0
  19. package/components/CartSummary/CartSummary.gql.ts +4 -0
  20. package/components/CartSummary/CartSummary.graphql +15 -0
  21. package/components/CartSummary/GetCartSummary.gql.ts +12 -0
  22. package/components/CartSummary/GetCartSummary.graphql +5 -0
  23. package/components/CartSummary/index.tsx +139 -0
  24. package/components/CartTotals/CartTotals.gql.ts +4 -0
  25. package/components/CartTotals/CartTotals.graphql +51 -0
  26. package/components/CartTotals/CartTotals.tsx +187 -0
  27. package/components/CartTotals/GetCartTotals.gql.ts +12 -0
  28. package/components/CartTotals/GetCartTotals.graphql +5 -0
  29. package/components/EmptyCart/EmptyCart.tsx +56 -0
  30. package/components/index.ts +24 -0
  31. package/hooks/CreateEmptyCart.gql.ts +10 -0
  32. package/hooks/CreateEmptyCart.graphql +3 -0
  33. package/hooks/CurrentCartId.gql.ts +10 -0
  34. package/hooks/CurrentCartId.graphql +6 -0
  35. package/hooks/CurrentCartId.graphqls +11 -0
  36. package/hooks/CustomerCart.gql.ts +10 -0
  37. package/hooks/CustomerCart.graphql +7 -0
  38. package/hooks/UseCartRedirect.gql.ts +12 -0
  39. package/hooks/UseCartRedirect.graphql +5 -0
  40. package/hooks/UseMergeCustomerCart.gql.ts +13 -0
  41. package/hooks/UseMergeCustomerCart.graphql +6 -0
  42. package/hooks/index.ts +8 -0
  43. package/hooks/useAssignCurrentCartId.ts +18 -0
  44. package/hooks/useCartIdCreate.ts +22 -0
  45. package/hooks/useCartQuery.ts +37 -0
  46. package/hooks/useClearCurrentCartId.ts +18 -0
  47. package/hooks/useCurrentCartId.ts +6 -0
  48. package/hooks/useDisplayInclTax.ts +7 -0
  49. package/hooks/useFormGqlMutationCart.ts +28 -0
  50. package/hooks/useMergeCustomerCart.ts +49 -0
  51. package/index.ts +4 -0
  52. package/next-env.d.ts +4 -0
  53. package/package.json +42 -0
  54. package/tsconfig.json +5 -0
  55. package/typePolicies.ts +56 -0
@@ -0,0 +1,187 @@
1
+ import { Divider, makeStyles, Theme } from '@material-ui/core'
2
+ import { Money, MoneyProps } from '@graphcommerce/magento-store'
3
+ import { AnimatedRow, UseStyles } from '@graphcommerce/next-ui'
4
+ import clsx from 'clsx'
5
+ import { AnimatePresence } from 'framer-motion'
6
+ import React from 'react'
7
+ import { useCartQuery, useDisplayInclTax } from '../../hooks'
8
+ import { GetCartTotalsDocument } from './GetCartTotals.gql'
9
+
10
+ const useStyles = makeStyles(
11
+ (theme: Theme) => ({
12
+ costsContainer: {
13
+ borderRadius: 4,
14
+ background: '#FFFADD',
15
+ padding: `${theme.spacings.xs} ${theme.spacings.sm}`,
16
+ },
17
+ containerMarginTop: {
18
+ marginTop: theme.spacings.md,
19
+ },
20
+ costsDivider: {
21
+ margin: `1em 0`,
22
+ },
23
+ costsRow: {
24
+ display: 'flex',
25
+ justifyContent: 'space-between',
26
+ ...theme.typography.subtitle1,
27
+ },
28
+ costsGrandTotal: {
29
+ fontWeight: theme.typography.fontWeightBold,
30
+ color: theme.palette.primary.main,
31
+ },
32
+ costsDiscount: {
33
+ fontWeight: theme.typography.fontWeightBold,
34
+ },
35
+ costsDiscountSub: {
36
+ fontWeight: theme.typography.fontWeightBold,
37
+ },
38
+ costsTax: {
39
+ color: theme.palette.primary.mutedText,
40
+ paddingTop: 0,
41
+ },
42
+ money: {
43
+ whiteSpace: 'nowrap',
44
+ },
45
+ }),
46
+ { name: 'CartTotals' },
47
+ )
48
+
49
+ export type CartTotalsProps = { containerMargin?: boolean } & UseStyles<typeof useStyles>
50
+
51
+ /**
52
+ * ⚠️ WARNING: The current CartTotals rely heavily on how Magento is configured. It kinda works for
53
+ * the demo, but we need additional fields from the API to get this working as expected:
54
+ *
55
+ * @see https://github.com/magento/magento2/issues/33848
56
+ * @see https://github.com/magento/magento2/issues?q=is%3Aopen+is%3Aissue+label%3A%22Project%3A+GraphQL%22+tax
57
+ */
58
+ export default function CartTotals(props: CartTotalsProps) {
59
+ const { data } = useCartQuery(GetCartTotalsDocument, { allowUrl: true })
60
+ const classes = useStyles(props)
61
+ const inclTax = useDisplayInclTax()
62
+
63
+ if (!data?.cart) return null
64
+
65
+ const { containerMargin } = props
66
+ const { shipping_addresses, prices } = data.cart
67
+ const shippingMethod = shipping_addresses?.[0]?.selected_shipping_method
68
+ const shippingMethodPrices = shipping_addresses?.[0]?.available_shipping_methods?.find(
69
+ (avail) =>
70
+ (shippingMethod?.amount.value ?? 0) > 0 &&
71
+ avail?.carrier_code === shippingMethod?.carrier_code &&
72
+ avail?.method_code === shippingMethod?.method_code,
73
+ )
74
+
75
+ return (
76
+ <AnimatedRow
77
+ className={clsx(
78
+ containerMargin ? classes.containerMarginTop : undefined,
79
+ classes.costsContainer,
80
+ )}
81
+ key='total-costs'
82
+ >
83
+ <AnimatePresence initial={false}>
84
+ {prices?.subtotal_including_tax && (
85
+ <AnimatedRow className={classes.costsRow} key='subtotal'>
86
+ <div>Products</div>
87
+ <div className={classes.money}>
88
+ <Money
89
+ {...(inclTax ? prices.subtotal_including_tax : prices.subtotal_excluding_tax)}
90
+ />
91
+ </div>
92
+ </AnimatedRow>
93
+ )}
94
+
95
+ {/* {prices?.discounts && prices.discounts.length > 1 && (
96
+ <AnimatedRow className={clsx(classes.costsRow, classes.costsDiscount)} key='discount'>
97
+ <div>Product discount</div>
98
+ <div className={classes.money}>
99
+ <Money
100
+ currency={prices.subtotal_with_discount_excluding_tax?.currency}
101
+ value={
102
+ (prices.subtotal_excluding_tax?.value ?? 0) * -1 -
103
+ (prices.subtotal_with_discount_excluding_tax?.value ?? 0) * -1
104
+ }
105
+ />
106
+ </div>
107
+ </AnimatedRow>
108
+ )} */}
109
+
110
+ {prices?.discounts?.map((discount) => {
111
+ const value = inclTax
112
+ ? (discount?.amount.value ?? 0) * -1
113
+ : (discount?.amount.value ?? 0) *
114
+ ((prices.subtotal_excluding_tax?.value ?? 1) /
115
+ (prices.subtotal_including_tax?.value ?? 1)) *
116
+ -1
117
+
118
+ return (
119
+ <AnimatedRow
120
+ className={clsx(classes.costsRow, classes.costsDiscountSub)}
121
+ key={discount?.label}
122
+ >
123
+ <div>{discount?.label}</div>
124
+ <div className={classes.money}>
125
+ {discount?.amount && <Money {...discount} value={value} />}
126
+ </div>
127
+ </AnimatedRow>
128
+ )
129
+ })}
130
+
131
+ {shippingMethod && (
132
+ <AnimatedRow className={classes.costsRow} key='shippingMethod'>
133
+ <div>
134
+ Shipping ({shippingMethod.carrier_title} {shippingMethod.method_title})
135
+ </div>
136
+ <div className={classes.money}>
137
+ <Money
138
+ {...(inclTax
139
+ ? shippingMethodPrices?.price_incl_tax
140
+ : shippingMethodPrices?.price_excl_tax)}
141
+ />
142
+ </div>
143
+ </AnimatedRow>
144
+ )}
145
+
146
+ {!inclTax &&
147
+ prices?.applied_taxes?.map((tax) => (
148
+ <AnimatedRow className={clsx(classes.costsRow)} key={`excl${tax?.label}`}>
149
+ <div>{tax?.label}</div>
150
+ <div className={classes.money}>
151
+ <Money {...tax?.amount} />
152
+ </div>
153
+ </AnimatedRow>
154
+ ))}
155
+
156
+ <AnimatedRow key='divider'>
157
+ <Divider className={classes.costsDivider} />
158
+ </AnimatedRow>
159
+
160
+ {prices?.grand_total && (
161
+ <AnimatedRow
162
+ className={clsx(classes.costsRow, classes.costsGrandTotal)}
163
+ key='grand_total'
164
+ >
165
+ <div>Grand total</div>
166
+ <div className={classes.money}>
167
+ <Money {...prices.grand_total} />
168
+ </div>
169
+ </AnimatedRow>
170
+ )}
171
+
172
+ {inclTax &&
173
+ prices?.applied_taxes?.map((tax) => (
174
+ <AnimatedRow
175
+ className={clsx(classes.costsRow, classes.costsTax)}
176
+ key={`incl${tax?.label}`}
177
+ >
178
+ <div>Including {tax?.label}</div>
179
+ <div className={classes.money}>
180
+ <Money {...tax?.amount} />
181
+ </div>
182
+ </AnimatedRow>
183
+ ))}
184
+ </AnimatePresence>
185
+ </AnimatedRow>
186
+ )
187
+ }
@@ -0,0 +1,12 @@
1
+ /* eslint-disable */
2
+ import * as Types from '@graphcommerce/graphql';
3
+
4
+ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
5
+
6
+ export const GetCartTotalsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCartTotals"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cartId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cart"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cart_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cartId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"shipping_addresses"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"selected_shipping_method"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"carrier_code"}},{"kind":"Field","name":{"kind":"Name","value":"method_code"}},{"kind":"Field","name":{"kind":"Name","value":"method_title"}},{"kind":"Field","name":{"kind":"Name","value":"carrier_title"}},{"kind":"Field","name":{"kind":"Name","value":"amount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"available_shipping_methods"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"carrier_code"}},{"kind":"Field","name":{"kind":"Name","value":"method_code"}},{"kind":"Field","name":{"kind":"Name","value":"price_incl_tax"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"price_excl_tax"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"prices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"applied_taxes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"amount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}},{"kind":"Field","name":{"kind":"Name","value":"discounts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"amount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}},{"kind":"Field","name":{"kind":"Name","value":"grand_total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subtotal_excluding_tax"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subtotal_including_tax"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subtotal_with_discount_excluding_tax"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}}]}}]}}]} as unknown as DocumentNode<GetCartTotalsQuery, GetCartTotalsQueryVariables>;
7
+ export type GetCartTotalsQueryVariables = Types.Exact<{
8
+ cartId: Types.Scalars['String'];
9
+ }>;
10
+
11
+
12
+ export type GetCartTotalsQuery = { cart?: Types.Maybe<{ shipping_addresses: Array<Types.Maybe<{ selected_shipping_method?: Types.Maybe<{ carrier_code: string, method_code: string, method_title: string, carrier_title: string, amount: { currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> } }>, available_shipping_methods?: Types.Maybe<Array<Types.Maybe<{ carrier_code: string, method_code?: Types.Maybe<string>, price_incl_tax: { currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> }, price_excl_tax: { currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> } }>>> }>>, prices?: Types.Maybe<{ __typename: 'CartPrices', applied_taxes?: Types.Maybe<Array<Types.Maybe<{ label: string, amount: { currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> } }>>>, discounts?: Types.Maybe<Array<Types.Maybe<{ label: string, amount: { currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> } }>>>, grand_total?: Types.Maybe<{ currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> }>, subtotal_excluding_tax?: Types.Maybe<{ currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> }>, subtotal_including_tax?: Types.Maybe<{ currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> }>, subtotal_with_discount_excluding_tax?: Types.Maybe<{ currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> }> }> }> };
@@ -0,0 +1,5 @@
1
+ query GetCartTotals($cartId: String!) {
2
+ cart(cart_id: $cartId) {
3
+ ...CartTotals
4
+ }
5
+ }
@@ -0,0 +1,56 @@
1
+ import { makeStyles, Theme, Typography } from '@material-ui/core'
2
+ import { responsiveVal, SvgImage, iconSadShoppingBag } from '@graphcommerce/next-ui'
3
+ import React from 'react'
4
+
5
+ const useStyles = makeStyles(
6
+ (theme: Theme) => ({
7
+ root: {
8
+ textAlign: 'center',
9
+ display: 'flex',
10
+ justifyContent: 'center',
11
+ alignItems: 'center',
12
+ [theme.breakpoints.down('sm')]: {
13
+ minHeight: '70vh',
14
+ },
15
+ [theme.breakpoints.up('md')]: {
16
+ minHeight: '60vh',
17
+ },
18
+ },
19
+ img: {
20
+ display: 'block',
21
+ margin: `0 auto ${theme.spacings.xxs} auto`,
22
+ width: responsiveVal(120, 180),
23
+ height: responsiveVal(120, 180),
24
+ },
25
+ }),
26
+ { name: 'EmptyCart' },
27
+ )
28
+
29
+ type EmptyCartProps = { children?: React.ReactNode }
30
+ export default function EmptyCart(props: EmptyCartProps) {
31
+ const { children } = props
32
+ const classes = useStyles()
33
+
34
+ return (
35
+ <div className={classes.root}>
36
+ <div>
37
+ <SvgImage
38
+ src={iconSadShoppingBag}
39
+ alt='Empty Cart'
40
+ className={classes.img}
41
+ loading='eager'
42
+ size='large'
43
+ />
44
+
45
+ {children ?? (
46
+ <>
47
+ <Typography variant='h3' gutterBottom component='h1'>
48
+ Your cart is empty
49
+ </Typography>
50
+ <Typography>Discover our collection and add items to your basket!</Typography>
51
+ </>
52
+ )}
53
+ </div>
54
+ </div>
55
+ )
56
+ }
@@ -0,0 +1,24 @@
1
+ export * from './AddToCartButton/AddToCartButton'
2
+ export { default as AddToCartButton } from './AddToCartButton/AddToCartButton'
3
+
4
+ export * from './CartFab/CartFab'
5
+ export { default as CartFab } from './CartFab/CartFab'
6
+
7
+ export * from './CartStartCheckout/CartStartCheckout'
8
+ export { default as CartStartCheckout } from './CartStartCheckout/CartStartCheckout'
9
+
10
+ export * from './CartTotals/CartTotals'
11
+ export { default as CartTotals } from './CartTotals/CartTotals'
12
+
13
+ export { default as EmptyCart } from './EmptyCart/EmptyCart'
14
+
15
+ export { default as ApolloCartErrorAlert } from './ApolloCartError/ApolloCartErrorAlert'
16
+ export * from './ApolloCartError/ApolloCartErrorAlert'
17
+
18
+ // export { default as ApolloCartErrorFullPage } from './ApolloCartError/ApolloCartErrorFullPage'
19
+ // export * from './ApolloCartError/ApolloCartErrorFullPage'
20
+
21
+ export * from './CartSummary'
22
+ export { default as CartSummary } from './CartSummary'
23
+
24
+ export { default as CartItemSummary } from './CartItemSummary'
@@ -0,0 +1,10 @@
1
+ /* eslint-disable */
2
+ import * as Types from '@graphcommerce/graphql';
3
+
4
+ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
5
+
6
+ export const CreateEmptyCartDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateEmptyCart"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createEmptyCart"}}]}}]} as unknown as DocumentNode<CreateEmptyCartMutation, CreateEmptyCartMutationVariables>;
7
+ export type CreateEmptyCartMutationVariables = Types.Exact<{ [key: string]: never; }>;
8
+
9
+
10
+ export type CreateEmptyCartMutation = { createEmptyCart?: Types.Maybe<string> };
@@ -0,0 +1,3 @@
1
+ mutation CreateEmptyCart {
2
+ createEmptyCart
3
+ }
@@ -0,0 +1,10 @@
1
+ /* eslint-disable */
2
+ import * as Types from '@graphcommerce/graphql';
3
+
4
+ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
5
+
6
+ export const CurrentCartIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CurrentCartId"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentCartId"},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"client"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<CurrentCartIdQuery, CurrentCartIdQueryVariables>;
7
+ export type CurrentCartIdQueryVariables = Types.Exact<{ [key: string]: never; }>;
8
+
9
+
10
+ export type CurrentCartIdQuery = { currentCartId?: Types.Maybe<{ __typename: 'CurrentCartId', id?: Types.Maybe<string> }> };
@@ -0,0 +1,6 @@
1
+ query CurrentCartId {
2
+ currentCartId @client {
3
+ __typename
4
+ id
5
+ }
6
+ }
@@ -0,0 +1,11 @@
1
+ extend type Query {
2
+ currentCartId: CurrentCartId
3
+ }
4
+
5
+ type CurrentCartId {
6
+ id: String
7
+ }
8
+
9
+ input RegisterCartIdInput {
10
+ cart_id: String!
11
+ }
@@ -0,0 +1,10 @@
1
+ /* eslint-disable */
2
+ import * as Types from '@graphcommerce/graphql';
3
+
4
+ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
5
+
6
+ export const CustomerCartDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CustomerCart"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customerCart"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"uid"}},{"kind":"Field","name":{"kind":"Name","value":"quantity"}}]}},{"kind":"Field","name":{"kind":"Name","value":"total_quantity"}},{"kind":"Field","name":{"kind":"Name","value":"prices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"grand_total"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"applied_taxes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"amount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}},{"kind":"Field","name":{"kind":"Name","value":"discounts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"amount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subtotal_excluding_tax"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subtotal_including_tax"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subtotal_with_discount_excluding_tax"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"shipping_addresses"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"selected_shipping_method"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"carrier_code"}},{"kind":"Field","name":{"kind":"Name","value":"method_code"}},{"kind":"Field","name":{"kind":"Name","value":"method_title"}},{"kind":"Field","name":{"kind":"Name","value":"carrier_title"}},{"kind":"Field","name":{"kind":"Name","value":"amount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"available_shipping_methods"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"carrier_code"}},{"kind":"Field","name":{"kind":"Name","value":"method_code"}},{"kind":"Field","name":{"kind":"Name","value":"price_incl_tax"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"price_excl_tax"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<CustomerCartQuery, CustomerCartQueryVariables>;
7
+ export type CustomerCartQueryVariables = Types.Exact<{ [key: string]: never; }>;
8
+
9
+
10
+ export type CustomerCartQuery = { customerCart: { __typename: 'Cart', id: string, total_quantity: number, items?: Types.Maybe<Array<Types.Maybe<{ __typename: 'BundleCartItem', uid: string, quantity: number } | { __typename: 'ConfigurableCartItem', uid: string, quantity: number } | { __typename: 'DownloadableCartItem', uid: string, quantity: number } | { __typename: 'SimpleCartItem', uid: string, quantity: number } | { __typename: 'VirtualCartItem', uid: string, quantity: number }>>>, prices?: Types.Maybe<{ __typename: 'CartPrices', grand_total?: Types.Maybe<{ currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> }>, applied_taxes?: Types.Maybe<Array<Types.Maybe<{ label: string, amount: { currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> } }>>>, discounts?: Types.Maybe<Array<Types.Maybe<{ label: string, amount: { currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> } }>>>, subtotal_excluding_tax?: Types.Maybe<{ currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> }>, subtotal_including_tax?: Types.Maybe<{ currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> }>, subtotal_with_discount_excluding_tax?: Types.Maybe<{ currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> }> }>, shipping_addresses: Array<Types.Maybe<{ selected_shipping_method?: Types.Maybe<{ carrier_code: string, method_code: string, method_title: string, carrier_title: string, amount: { currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> } }>, available_shipping_methods?: Types.Maybe<Array<Types.Maybe<{ carrier_code: string, method_code?: Types.Maybe<string>, price_incl_tax: { currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> }, price_excl_tax: { currency?: Types.Maybe<Types.CurrencyEnum>, value?: Types.Maybe<number> } }>>> }>> } };
@@ -0,0 +1,7 @@
1
+ query CustomerCart {
2
+ customerCart {
3
+ id
4
+ __typename
5
+ ...CartItemCountChanged
6
+ }
7
+ }
@@ -0,0 +1,12 @@
1
+ /* eslint-disable */
2
+ import * as Types from '@graphcommerce/graphql';
3
+
4
+ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
5
+
6
+ export const UseCartRedirectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UseCartRedirect"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cartId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cart"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cart_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cartId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<UseCartRedirectQuery, UseCartRedirectQueryVariables>;
7
+ export type UseCartRedirectQueryVariables = Types.Exact<{
8
+ cartId: Types.Scalars['String'];
9
+ }>;
10
+
11
+
12
+ export type UseCartRedirectQuery = { cart?: Types.Maybe<{ id: string }> };
@@ -0,0 +1,5 @@
1
+ query UseCartRedirect($cartId: String!) {
2
+ cart(cart_id: $cartId) {
3
+ id
4
+ }
5
+ }
@@ -0,0 +1,13 @@
1
+ /* eslint-disable */
2
+ import * as Types from '@graphcommerce/graphql';
3
+
4
+ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
5
+
6
+ export const UseMergeCustomerCartDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UseMergeCustomerCart"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sourceCartId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"destinationCartId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mergeCarts"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"source_cart_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sourceCartId"}}},{"kind":"Argument","name":{"kind":"Name","value":"destination_cart_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"destinationCartId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<UseMergeCustomerCartMutation, UseMergeCustomerCartMutationVariables>;
7
+ export type UseMergeCustomerCartMutationVariables = Types.Exact<{
8
+ sourceCartId: Types.Scalars['String'];
9
+ destinationCartId: Types.Scalars['String'];
10
+ }>;
11
+
12
+
13
+ export type UseMergeCustomerCartMutation = { mergeCarts: { __typename: 'Cart', id: string } };
@@ -0,0 +1,6 @@
1
+ mutation UseMergeCustomerCart($sourceCartId: String!, $destinationCartId: String!) {
2
+ mergeCarts(source_cart_id: $sourceCartId, destination_cart_id: $destinationCartId) {
3
+ __typename
4
+ id
5
+ }
6
+ }
package/hooks/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * from './useAssignCurrentCartId'
2
+ export * from './useCurrentCartId'
3
+ export * from './useCartQuery'
4
+ export * from './useClearCurrentCartId'
5
+ export * from './useCartIdCreate'
6
+ export * from './useFormGqlMutationCart'
7
+ export * from './useMergeCustomerCart'
8
+ export * from './useDisplayInclTax'
@@ -0,0 +1,18 @@
1
+ import { useApolloClient } from '@apollo/client'
2
+ import { useCallback } from 'react'
3
+ import { CurrentCartIdDocument } from './CurrentCartId.gql'
4
+
5
+ export function useAssignCurrentCartId() {
6
+ const { cache } = useApolloClient()
7
+
8
+ return useCallback(
9
+ (id: string) => {
10
+ cache.writeQuery({
11
+ query: CurrentCartIdDocument,
12
+ data: { currentCartId: { __typename: 'CurrentCartId', id } },
13
+ broadcast: true,
14
+ })
15
+ },
16
+ [cache],
17
+ )
18
+ }
@@ -0,0 +1,22 @@
1
+ import { useMutation } from '@apollo/client'
2
+ import { CreateEmptyCartDocument } from './CreateEmptyCart.gql'
3
+ import { useAssignCurrentCartId } from './useAssignCurrentCartId'
4
+ import { useCurrentCartId } from './useCurrentCartId'
5
+
6
+ export function useCartIdCreate() {
7
+ const cartId = useCurrentCartId()
8
+ const [create] = useMutation(CreateEmptyCartDocument)
9
+ const assignCurrentCartId = useAssignCurrentCartId()
10
+
11
+ return async (): Promise<string> => {
12
+ if (cartId) return cartId
13
+
14
+ const { data } = await create()
15
+ if (!data?.createEmptyCart) throw Error('Could not create an empty cart')
16
+
17
+ // We store the cartId that is returned as the currentCartId result
18
+ assignCurrentCartId(data.createEmptyCart)
19
+
20
+ return data.createEmptyCart
21
+ }
22
+ }
@@ -0,0 +1,37 @@
1
+ import { useQuery, TypedDocumentNode, QueryHookOptions } from '@apollo/client'
2
+ import { useRouter } from 'next/router'
3
+ import { useCurrentCartId } from './useCurrentCartId'
4
+
5
+ /**
6
+ * Requires the query to have a `$cartId: String!` argument. It will automatically inject the
7
+ * currently active cart_id.
8
+ *
9
+ * Example:
10
+ *
11
+ * ```tsx
12
+ * const { data } = useCartQuery(CartFabQueryDocument)
13
+ * ```
14
+ */
15
+ export function useCartQuery<Q, V extends { cartId: string; [index: string]: unknown }>(
16
+ document: TypedDocumentNode<Q, V>,
17
+ options: QueryHookOptions<Q, Omit<V, 'cartId'>> & { allowUrl?: boolean } = {},
18
+ ) {
19
+ const { allowUrl = false, ...queryOptions } = options
20
+
21
+ const router = useRouter()
22
+ const currentCartId = useCurrentCartId()
23
+ const urlCartId = router.query.cartId
24
+ const usingUrl = allowUrl && typeof urlCartId === 'string'
25
+
26
+ const cartId = usingUrl ? urlCartId : currentCartId
27
+
28
+ if (usingUrl && typeof queryOptions.fetchPolicy === 'undefined')
29
+ queryOptions.fetchPolicy = 'cache-only'
30
+ if (usingUrl && typeof queryOptions.returnPartialData === 'undefined')
31
+ queryOptions.returnPartialData = true
32
+ queryOptions.variables = { cartId, ...options?.variables } as V
33
+ queryOptions.skip = queryOptions?.skip || !cartId
34
+ queryOptions.ssr = false
35
+
36
+ return useQuery(document, queryOptions)
37
+ }
@@ -0,0 +1,18 @@
1
+ import { useApolloClient } from '@apollo/client'
2
+ import { CurrentCartIdDocument } from './CurrentCartId.gql'
3
+ import { useCurrentCartId } from './useCurrentCartId'
4
+
5
+ export function useClearCurrentCartId() {
6
+ const { cache } = useApolloClient()
7
+ const currentCartId = useCurrentCartId()
8
+
9
+ if (!currentCartId) return undefined
10
+
11
+ return () => {
12
+ cache.writeQuery({
13
+ query: CurrentCartIdDocument,
14
+ data: { currentCartId: { __typename: 'CurrentCartId', id: null } },
15
+ broadcast: true,
16
+ })
17
+ }
18
+ }
@@ -0,0 +1,6 @@
1
+ import { useQuery } from '@apollo/client'
2
+ import { CurrentCartIdDocument } from './CurrentCartId.gql'
3
+
4
+ export function useCurrentCartId() {
5
+ return useQuery(CurrentCartIdDocument, { ssr: false }).data?.currentCartId?.id ?? undefined
6
+ }
@@ -0,0 +1,7 @@
1
+ import { useRouter } from 'next/router'
2
+
3
+ export function useDisplayInclTax(): boolean {
4
+ const { locale } = useRouter()
5
+ const locales = (process.env.NEXT_PUBLIC_DISPLAY_INCL_TAX ?? '').split(',').map((i) => i.trim())
6
+ return locale ? locales.includes(locale) : false
7
+ }
@@ -0,0 +1,28 @@
1
+ import { MutationHookOptions, TypedDocumentNode } from '@apollo/client'
2
+ import { graphqlErrorByCategory } from '@graphcommerce/magento-graphql'
3
+ import {
4
+ useFormGqlMutation,
5
+ UseFormGqlMutationReturn,
6
+ UseFormGraphQlOptions,
7
+ } from '@graphcommerce/react-hook-form'
8
+ import { useCartIdCreate } from './useCartIdCreate'
9
+
10
+ export function useFormGqlMutationCart<Q, V extends { cartId: string; [index: string]: unknown }>(
11
+ document: TypedDocumentNode<Q, V>,
12
+ options: UseFormGraphQlOptions<Q, V> = {},
13
+ operationOptions?: MutationHookOptions<Q, V>,
14
+ ): UseFormGqlMutationReturn<Q, V> {
15
+ const cartId = useCartIdCreate()
16
+
17
+ const onBeforeSubmit = async (variables: V) => {
18
+ const vars = { ...variables, cartId: await cartId() }
19
+ return options.onBeforeSubmit ? options.onBeforeSubmit(vars) : vars
20
+ }
21
+ const result = useFormGqlMutation<Q, V>(
22
+ document,
23
+ { ...options, onBeforeSubmit },
24
+ { errorPolicy: 'all', ...operationOptions },
25
+ )
26
+
27
+ return result
28
+ }