@graphcommerce/google-datalayer 8.0.4-canary.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.
- package/CHANGELOG.md +8 -0
- package/Config.graphqls +21 -0
- package/README.md +6 -0
- package/components/AnalyticsItemList.tsx +63 -0
- package/events/add_payment_info/AddPaymentInfoFragment.graphql +36 -0
- package/events/add_payment_info/addPaymentInfo.ts +23 -0
- package/events/add_payment_info/index.ts +2 -0
- package/events/add_shipping_info/AddSchippingInfoFragment.graphql +38 -0
- package/events/add_shipping_info/addShippingInfo.ts +22 -0
- package/events/add_shipping_info/index.ts +2 -0
- package/events/add_to_cart/AddToCartFragment.graphql +30 -0
- package/events/add_to_cart/addToCart.ts +58 -0
- package/events/add_to_cart/index.ts +2 -0
- package/events/begin_checkout/BeginCheckoutFragment.graphql +38 -0
- package/events/begin_checkout/beginCheckout.ts +22 -0
- package/events/begin_checkout/index.ts +2 -0
- package/events/purchase/index.ts +1 -0
- package/events/purchase/purchase.ts +27 -0
- package/events/remove_from_cart/RemoveFromCartFragment.graphql +36 -0
- package/events/remove_from_cart/index.ts +2 -0
- package/events/remove_from_cart/removeFromCart.ts +21 -0
- package/events/select_item/index.ts +1 -0
- package/events/select_item/select_item.ts +8 -0
- package/events/view_cart/ViewCartFragment.graphql +35 -0
- package/events/view_cart/index.ts +2 -0
- package/events/view_cart/viewCart.ts +21 -0
- package/events/view_item/index.ts +1 -0
- package/events/view_item/view_item.ts +8 -0
- package/events/view_item_list/index.ts +1 -0
- package/events/view_item_list/view_item_list.ts +8 -0
- package/lib/event.ts +39 -0
- package/lib/index.ts +2 -0
- package/lib/productToItem/ProductToItem.graphql +20 -0
- package/lib/productToItem/productToItem.ts +38 -0
- package/package.json +44 -0
- package/plugins/GoogleDatalayerAddProductsToCartForm.tsx +23 -0
- package/plugins/GoogleDatalayerAllPagesPageview.tsx +37 -0
- package/plugins/GoogleDatalayerCartStartCheckout.tsx +21 -0
- package/plugins/GoogleDatalayerCartStartCheckoutLinkOrButton.tsx +35 -0
- package/plugins/GoogleDatalayerPaymentMethodButton.tsx +26 -0
- package/plugins/GoogleDatalayerPaymentMethodContextProvider.tsx +22 -0
- package/plugins/GoogleDatalayerProductListItem.tsx +28 -0
- package/plugins/GoogleDatalayerProductListItemsBase.tsx +17 -0
- package/plugins/GoogleDatalayerRemoveItemFromCart.tsx +29 -0
- package/plugins/GoogleDatalayerRemoveItemFromCartFab.tsx +29 -0
- package/plugins/GoogleDatalayerShippingMethodForm.tsx +22 -0
- package/plugins/GoogleDatalayerUpdateItemQuantity.tsx +65 -0
- package/plugins/GoogleDatalayerViewItem.tsx +29 -0
- package/tsconfig.json +5 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# @graphcommerce/google-datalayer
|
|
2
|
+
|
|
3
|
+
## 8.0.4-canary.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#2158](https://github.com/graphcommerce-org/graphcommerce/pull/2158) [`34de808`](https://github.com/graphcommerce-org/graphcommerce/commit/34de8085e9352d1f3b20b26746685370ea10ab90) - Extracted the datalayer from the googleanalytics package and moved to google-datalayer package. Make sure Google Analytics and Google Tagmanager both can send events individually. Be able to configure the datalayer will send as GA4 or legacy GA3 events.
|
|
8
|
+
([@mikekeehnen](https://github.com/mikekeehnen))
|
package/Config.graphqls
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
extend input GraphCommerceConfig {
|
|
2
|
+
analytics: AnalyticsConfig
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
EventFormat is an enumatation of different event formats. This decides what the format of the event data will be.
|
|
7
|
+
"""
|
|
8
|
+
enum EventFormat {
|
|
9
|
+
GA3
|
|
10
|
+
GA4
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
"""
|
|
14
|
+
AnalyticsConfig will contain all configuration values for the analytics in GraphCommerce.
|
|
15
|
+
"""
|
|
16
|
+
input AnalyticsConfig {
|
|
17
|
+
"""
|
|
18
|
+
eventFormat contains the list of fired and formatted events
|
|
19
|
+
"""
|
|
20
|
+
eventFormat: [EventFormat!]
|
|
21
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { nonNullable, useMemoObject } from '@graphcommerce/next-ui'
|
|
2
|
+
import { useEventCallback } from '@mui/material'
|
|
3
|
+
import React, { useContext, useEffect, useMemo } from 'react'
|
|
4
|
+
import { Item, ProductToItemFragment, productToItem } from '../lib'
|
|
5
|
+
import { event } from '../lib/event'
|
|
6
|
+
|
|
7
|
+
export type UseViewItemListProps<P extends ProductToItemFragment = ProductToItemFragment> = {
|
|
8
|
+
title: string
|
|
9
|
+
items?: (P | null | undefined)[] | null
|
|
10
|
+
listId?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type ViewItemList = {
|
|
14
|
+
item_list_id: string
|
|
15
|
+
item_list_name: string
|
|
16
|
+
items: Item[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const GoogleTagManagerItemListContext = React.createContext<{
|
|
20
|
+
item_list_id: string
|
|
21
|
+
item_list_name: string
|
|
22
|
+
}>({ item_list_id: '', item_list_name: '' })
|
|
23
|
+
|
|
24
|
+
export function useListItemHandler(item: ProductToItemFragment) {
|
|
25
|
+
const { item_list_id, item_list_name } = useContext(GoogleTagManagerItemListContext)
|
|
26
|
+
return useEventCallback(() =>
|
|
27
|
+
event('select_item', {
|
|
28
|
+
item_list_id,
|
|
29
|
+
item_list_name,
|
|
30
|
+
items: productToItem(item),
|
|
31
|
+
}),
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function ItemList<P extends ProductToItemFragment = ProductToItemFragment>(
|
|
36
|
+
props: UseViewItemListProps<P> & { children: React.ReactNode },
|
|
37
|
+
) {
|
|
38
|
+
const { title, items, listId, children } = props
|
|
39
|
+
|
|
40
|
+
const eventData: ViewItemList = useMemoObject({
|
|
41
|
+
item_list_id: listId ?? title?.toLowerCase().replace(/\s/g, '_'),
|
|
42
|
+
item_list_name: title,
|
|
43
|
+
items: items?.map((item) => (item ? productToItem(item) : null)).filter(nonNullable) ?? [],
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
event('view_item_list', eventData)
|
|
48
|
+
}, [eventData])
|
|
49
|
+
|
|
50
|
+
const value = useMemo(
|
|
51
|
+
() => ({
|
|
52
|
+
item_list_id: listId ?? title?.toLowerCase().replace(/\s/g, '_'),
|
|
53
|
+
item_list_name: title ?? listId,
|
|
54
|
+
}),
|
|
55
|
+
[listId, title],
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<GoogleTagManagerItemListContext.Provider value={value}>
|
|
60
|
+
{children}
|
|
61
|
+
</GoogleTagManagerItemListContext.Provider>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
fragment AddPaymentInfoFragment on Cart
|
|
2
|
+
@inject(into: ["PaymentMethodContext", "PaymentMethodUpdated"]) {
|
|
3
|
+
items {
|
|
4
|
+
uid
|
|
5
|
+
prices {
|
|
6
|
+
price {
|
|
7
|
+
currency
|
|
8
|
+
value
|
|
9
|
+
}
|
|
10
|
+
discounts {
|
|
11
|
+
amount {
|
|
12
|
+
currency
|
|
13
|
+
value
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
quantity
|
|
18
|
+
product {
|
|
19
|
+
uid
|
|
20
|
+
name
|
|
21
|
+
sku
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
applied_coupons {
|
|
25
|
+
code
|
|
26
|
+
}
|
|
27
|
+
prices {
|
|
28
|
+
grand_total {
|
|
29
|
+
currency
|
|
30
|
+
value
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
selected_payment_method {
|
|
34
|
+
code
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { event } from '../../lib/event'
|
|
2
|
+
import { AddPaymentInfoFragment } from './AddPaymentInfoFragment.gql'
|
|
3
|
+
|
|
4
|
+
export const addPaymentInfo = <C extends AddPaymentInfoFragment>(cart?: C | null) =>
|
|
5
|
+
event('add_payment_info', {
|
|
6
|
+
...cart,
|
|
7
|
+
currency: cart?.prices?.grand_total?.currency,
|
|
8
|
+
value: cart?.prices?.grand_total?.value,
|
|
9
|
+
coupon: cart?.applied_coupons?.map((coupon) => coupon?.code),
|
|
10
|
+
payment_type: cart?.selected_payment_method?.code,
|
|
11
|
+
items: cart?.items?.map((item) => ({
|
|
12
|
+
item_id: item?.product.sku,
|
|
13
|
+
item_name: item?.product.name,
|
|
14
|
+
currency: item?.prices?.price.currency,
|
|
15
|
+
discount: item?.prices?.discounts?.reduce(
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
|
17
|
+
(sum, discount) => sum + (discount?.amount?.value ?? 0),
|
|
18
|
+
0,
|
|
19
|
+
),
|
|
20
|
+
price: item?.prices?.price.value,
|
|
21
|
+
quantity: item?.quantity,
|
|
22
|
+
})),
|
|
23
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
fragment AddShippingInfoFragment on Cart @inject(into: ["ShippingMethodSelected"]) {
|
|
2
|
+
prices {
|
|
3
|
+
grand_total {
|
|
4
|
+
currency
|
|
5
|
+
value
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
applied_coupons {
|
|
9
|
+
code
|
|
10
|
+
}
|
|
11
|
+
items {
|
|
12
|
+
__typename
|
|
13
|
+
uid
|
|
14
|
+
product {
|
|
15
|
+
uid
|
|
16
|
+
sku
|
|
17
|
+
name
|
|
18
|
+
}
|
|
19
|
+
prices {
|
|
20
|
+
price {
|
|
21
|
+
value
|
|
22
|
+
currency
|
|
23
|
+
}
|
|
24
|
+
discounts {
|
|
25
|
+
amount {
|
|
26
|
+
value
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
quantity
|
|
31
|
+
... on ConfigurableCartItem {
|
|
32
|
+
configured_variant {
|
|
33
|
+
uid
|
|
34
|
+
sku
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { event } from '../../lib/event'
|
|
2
|
+
import { AddShippingInfoFragment } from './AddSchippingInfoFragment.gql'
|
|
3
|
+
|
|
4
|
+
export const addShippingInfo = <C extends AddShippingInfoFragment>(cart?: C) =>
|
|
5
|
+
event('add_shipping_info', {
|
|
6
|
+
...cart,
|
|
7
|
+
currency: cart?.prices?.grand_total?.currency,
|
|
8
|
+
value: cart?.prices?.grand_total?.value,
|
|
9
|
+
coupon: cart?.applied_coupons?.map((coupon) => coupon?.code),
|
|
10
|
+
items: cart?.items?.map((item) => ({
|
|
11
|
+
item_id: item?.product.sku,
|
|
12
|
+
item_name: item?.product.name,
|
|
13
|
+
currency: item?.prices?.price.currency,
|
|
14
|
+
discount: item?.prices?.discounts?.reduce(
|
|
15
|
+
(sum, discount) => sum + (discount?.amount?.value ?? 0),
|
|
16
|
+
0,
|
|
17
|
+
),
|
|
18
|
+
item_variant: item && 'configured_variant' in item ? item.configured_variant.sku : '',
|
|
19
|
+
price: item?.prices?.price.value,
|
|
20
|
+
quantity: item?.quantity,
|
|
21
|
+
})),
|
|
22
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
fragment AddToCartFragment on Cart @inject(into: ["CartItemCountChanged"]) {
|
|
2
|
+
items {
|
|
3
|
+
quantity
|
|
4
|
+
product {
|
|
5
|
+
sku
|
|
6
|
+
name
|
|
7
|
+
}
|
|
8
|
+
prices {
|
|
9
|
+
discounts {
|
|
10
|
+
amount {
|
|
11
|
+
currency
|
|
12
|
+
value
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
row_total_including_tax {
|
|
16
|
+
currency
|
|
17
|
+
value
|
|
18
|
+
}
|
|
19
|
+
price {
|
|
20
|
+
currency
|
|
21
|
+
value
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
prices {
|
|
26
|
+
grand_total {
|
|
27
|
+
currency
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { FetchResult } from '@graphcommerce/graphql'
|
|
2
|
+
import {
|
|
3
|
+
AddProductsToCartFields,
|
|
4
|
+
AddProductsToCartMutation,
|
|
5
|
+
findAddedItems,
|
|
6
|
+
toUserErrors,
|
|
7
|
+
} from '@graphcommerce/magento-product'
|
|
8
|
+
import { nonNullable } from '@graphcommerce/next-ui'
|
|
9
|
+
import { event } from '../../lib/event'
|
|
10
|
+
|
|
11
|
+
export const addToCart = (
|
|
12
|
+
result: FetchResult<AddProductsToCartMutation>,
|
|
13
|
+
variables: AddProductsToCartFields,
|
|
14
|
+
) => {
|
|
15
|
+
const { data, errors } = result
|
|
16
|
+
const cart = data?.addProductsToCart?.cart
|
|
17
|
+
|
|
18
|
+
const addedItems = findAddedItems(data, variables)
|
|
19
|
+
|
|
20
|
+
const items = addedItems
|
|
21
|
+
.map(({ itemVariable, itemInCart }) => {
|
|
22
|
+
if (!itemInCart) return null
|
|
23
|
+
const { product, prices } = itemInCart
|
|
24
|
+
return {
|
|
25
|
+
item_id: product.sku,
|
|
26
|
+
item_name: product.name,
|
|
27
|
+
currency: prices?.price.currency,
|
|
28
|
+
price: (prices?.row_total_including_tax.value ?? 1) / itemInCart.quantity,
|
|
29
|
+
quantity: itemVariable.quantity,
|
|
30
|
+
discount: prices?.discounts?.reduce(
|
|
31
|
+
(sum, discount) => sum + (discount?.amount?.value ?? 0) / itemVariable.quantity,
|
|
32
|
+
0,
|
|
33
|
+
),
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
.filter(nonNullable)
|
|
37
|
+
|
|
38
|
+
const userErrors = toUserErrors(result.data)
|
|
39
|
+
if ((errors && errors.length > 0) || userErrors.length > 0) {
|
|
40
|
+
event('add_to_cart_error', {
|
|
41
|
+
userErrors: userErrors?.map((e) => e.message),
|
|
42
|
+
errors: errors?.map((e) => e.message),
|
|
43
|
+
variables,
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!items.length) return
|
|
48
|
+
|
|
49
|
+
event('add_to_cart', {
|
|
50
|
+
currency: cart?.prices?.grand_total?.currency,
|
|
51
|
+
value: addedItems.reduce(
|
|
52
|
+
(sum, { itemVariable, itemInCart }) =>
|
|
53
|
+
sum + (itemInCart?.prices?.row_total_including_tax.value ?? 1) / itemVariable.quantity,
|
|
54
|
+
0,
|
|
55
|
+
),
|
|
56
|
+
items,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
fragment BeginCheckoutFragment on Cart @inject(into: ["CartStartCheckout"]) {
|
|
2
|
+
prices {
|
|
3
|
+
grand_total {
|
|
4
|
+
currency
|
|
5
|
+
value
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
applied_coupons {
|
|
9
|
+
code
|
|
10
|
+
}
|
|
11
|
+
items {
|
|
12
|
+
__typename
|
|
13
|
+
uid
|
|
14
|
+
product {
|
|
15
|
+
uid
|
|
16
|
+
sku
|
|
17
|
+
name
|
|
18
|
+
}
|
|
19
|
+
prices {
|
|
20
|
+
price {
|
|
21
|
+
value
|
|
22
|
+
currency
|
|
23
|
+
}
|
|
24
|
+
discounts {
|
|
25
|
+
amount {
|
|
26
|
+
value
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
quantity
|
|
31
|
+
... on ConfigurableCartItem {
|
|
32
|
+
configured_variant {
|
|
33
|
+
uid
|
|
34
|
+
sku
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { event } from '../../lib/event'
|
|
2
|
+
import { BeginCheckoutFragment } from './BeginCheckoutFragment.gql'
|
|
3
|
+
|
|
4
|
+
export const beginCheckout = <C extends BeginCheckoutFragment>(cart?: C | null) =>
|
|
5
|
+
event('begin_checkout', {
|
|
6
|
+
...cart,
|
|
7
|
+
currency: cart?.prices?.grand_total?.currency,
|
|
8
|
+
value: cart?.prices?.grand_total?.value,
|
|
9
|
+
coupon: cart?.applied_coupons?.map((coupon) => coupon?.code),
|
|
10
|
+
items: cart?.items?.map((item) => ({
|
|
11
|
+
item_id: item?.product.sku,
|
|
12
|
+
item_name: item?.product.name,
|
|
13
|
+
currency: item?.prices?.price.currency,
|
|
14
|
+
discount: item?.prices?.discounts?.reduce(
|
|
15
|
+
(sum, discount) => sum + (discount?.amount?.value ?? 0),
|
|
16
|
+
0,
|
|
17
|
+
),
|
|
18
|
+
item_variant: item?.__typename === 'ConfigurableCartItem' ? item.configured_variant.sku : '',
|
|
19
|
+
price: item?.prices?.price.value,
|
|
20
|
+
quantity: item?.quantity,
|
|
21
|
+
})),
|
|
22
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './purchase'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PaymentMethodContextFragment } from '@graphcommerce/magento-cart-payment-method/Api/PaymentMethodContext.gql'
|
|
2
|
+
import { event } from '../../lib/event'
|
|
3
|
+
|
|
4
|
+
export const purchase = <C extends PaymentMethodContextFragment>(
|
|
5
|
+
orderNumber: string,
|
|
6
|
+
cart: C | null | undefined,
|
|
7
|
+
) =>
|
|
8
|
+
event('purchase', {
|
|
9
|
+
...cart,
|
|
10
|
+
transaction_id: orderNumber,
|
|
11
|
+
currency: cart?.prices?.grand_total?.currency,
|
|
12
|
+
value: cart?.prices?.grand_total?.value,
|
|
13
|
+
coupon: cart?.applied_coupons?.map((coupon) => coupon?.code).join(' '),
|
|
14
|
+
payment_type: cart?.selected_payment_method?.code,
|
|
15
|
+
tax: cart?.prices?.applied_taxes?.reduce((sum, tax) => sum + (tax?.amount?.value ?? 0), 0),
|
|
16
|
+
items: cart?.items?.map((item) => ({
|
|
17
|
+
item_id: item?.product.sku,
|
|
18
|
+
item_name: item?.product.name,
|
|
19
|
+
currency: item?.prices?.price.currency,
|
|
20
|
+
discount: item?.prices?.discounts?.reduce(
|
|
21
|
+
(sum, discount) => sum + (discount?.amount?.value ?? 0),
|
|
22
|
+
0,
|
|
23
|
+
),
|
|
24
|
+
price: item?.prices?.price.value,
|
|
25
|
+
quantity: item?.quantity,
|
|
26
|
+
})),
|
|
27
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
fragment RemoveFromCartFragment on Cart @inject(into: ["CartItemCountChanged"]) {
|
|
2
|
+
__typename
|
|
3
|
+
prices {
|
|
4
|
+
grand_total {
|
|
5
|
+
currency
|
|
6
|
+
value
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
items {
|
|
10
|
+
__typename
|
|
11
|
+
uid
|
|
12
|
+
product {
|
|
13
|
+
uid
|
|
14
|
+
sku
|
|
15
|
+
name
|
|
16
|
+
}
|
|
17
|
+
prices {
|
|
18
|
+
price {
|
|
19
|
+
value
|
|
20
|
+
currency
|
|
21
|
+
}
|
|
22
|
+
discounts {
|
|
23
|
+
amount {
|
|
24
|
+
value
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
quantity
|
|
29
|
+
... on ConfigurableCartItem {
|
|
30
|
+
configured_variant {
|
|
31
|
+
uid
|
|
32
|
+
sku
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { event } from '../../lib/event'
|
|
2
|
+
import { RemoveFromCartFragment } from './RemoveFromCartFragment.gql'
|
|
3
|
+
|
|
4
|
+
export const removeFromCart = <C extends RemoveFromCartFragment>(cart: C | null | undefined) =>
|
|
5
|
+
event('remove_from_cart', {
|
|
6
|
+
...cart,
|
|
7
|
+
currency: cart?.prices?.grand_total?.currency,
|
|
8
|
+
value: cart?.prices?.grand_total?.value,
|
|
9
|
+
items: cart?.items?.map((item) => ({
|
|
10
|
+
item_id: item?.product.sku,
|
|
11
|
+
item_name: item?.product.name,
|
|
12
|
+
currency: item?.prices?.price.currency,
|
|
13
|
+
discount: item?.prices?.discounts?.reduce(
|
|
14
|
+
(sum, discount) => sum + (discount?.amount?.value ?? 0),
|
|
15
|
+
0,
|
|
16
|
+
),
|
|
17
|
+
item_variant: item?.__typename === 'ConfigurableCartItem' ? item.configured_variant.sku : '',
|
|
18
|
+
price: item?.prices?.price.value,
|
|
19
|
+
quantity: item?.quantity,
|
|
20
|
+
})),
|
|
21
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './select_item'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
fragment ViewCartFragment on Cart @inject(into: ["CartItemCountChanged"]) {
|
|
2
|
+
prices {
|
|
3
|
+
grand_total {
|
|
4
|
+
currency
|
|
5
|
+
value
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
items {
|
|
9
|
+
__typename
|
|
10
|
+
uid
|
|
11
|
+
product {
|
|
12
|
+
uid
|
|
13
|
+
sku
|
|
14
|
+
name
|
|
15
|
+
}
|
|
16
|
+
prices {
|
|
17
|
+
price {
|
|
18
|
+
value
|
|
19
|
+
currency
|
|
20
|
+
}
|
|
21
|
+
discounts {
|
|
22
|
+
amount {
|
|
23
|
+
value
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
quantity
|
|
28
|
+
... on ConfigurableCartItem {
|
|
29
|
+
configured_variant {
|
|
30
|
+
uid
|
|
31
|
+
sku
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { event } from '../../lib/event'
|
|
2
|
+
import { ViewCartFragment } from './ViewCartFragment.gql'
|
|
3
|
+
|
|
4
|
+
export const viewCart = <C extends ViewCartFragment>(cart: C | null | undefined) =>
|
|
5
|
+
event('view_cart', {
|
|
6
|
+
...cart,
|
|
7
|
+
currency: cart?.prices?.grand_total?.currency,
|
|
8
|
+
value: cart?.prices?.grand_total?.value,
|
|
9
|
+
items: cart?.items?.map((item) => ({
|
|
10
|
+
item_id: item?.product.sku,
|
|
11
|
+
item_name: item?.product.name,
|
|
12
|
+
currency: item?.prices?.price.currency,
|
|
13
|
+
discount: item?.prices?.discounts?.reduce(
|
|
14
|
+
(sum, discount) => sum + (discount?.amount?.value ?? 0),
|
|
15
|
+
0,
|
|
16
|
+
),
|
|
17
|
+
item_variant: item?.__typename === 'ConfigurableCartItem' ? item.configured_variant.sku : '',
|
|
18
|
+
price: item?.prices?.price.value,
|
|
19
|
+
quantity: item?.quantity,
|
|
20
|
+
})),
|
|
21
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './view_item'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './view_item_list'
|
package/lib/event.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { EventFormatSchema } from '@graphcommerce/next-config'
|
|
2
|
+
|
|
3
|
+
type EventMapFunctionType = (
|
|
4
|
+
eventName: Gtag.EventNames | (string & Record<never, never>),
|
|
5
|
+
eventData: {
|
|
6
|
+
[key: string]: unknown
|
|
7
|
+
},
|
|
8
|
+
) => void
|
|
9
|
+
|
|
10
|
+
type EventType = keyof (typeof EventFormatSchema)['Enum']
|
|
11
|
+
|
|
12
|
+
const eventMap: { [key in EventType]: EventMapFunctionType } = {
|
|
13
|
+
GA4: (eventName, eventData) => {
|
|
14
|
+
if (import.meta.graphCommerce.googleAnalyticsId) {
|
|
15
|
+
globalThis.gtag('event', eventName, eventData)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (import.meta.graphCommerce.googleTagmanagerId) {
|
|
19
|
+
globalThis.dataLayer.push({ event: eventName, ...eventData })
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
GA3: (eventName, eventData) => {
|
|
23
|
+
if (import.meta.graphCommerce.googleAnalyticsId) {
|
|
24
|
+
console.warn(
|
|
25
|
+
"Google Analytics 3 format is not supported for Google Analytics 4. Please update your event format to 'GA4'.",
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (import.meta.graphCommerce.googleTagmanagerId) {
|
|
30
|
+
globalThis.dataLayer.push({ event: eventName, ecommerce: eventData })
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const eventsToBeFired = import.meta.graphCommerce.analytics?.eventFormat ?? ['GA4']
|
|
36
|
+
|
|
37
|
+
export const event: EventMapFunctionType = (eventName, eventData) => {
|
|
38
|
+
eventsToBeFired.map((e) => eventMap[e](eventName, eventData))
|
|
39
|
+
}
|
package/lib/index.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ProductToItemFragment } from './ProductToItem.gql'
|
|
2
|
+
|
|
3
|
+
export type Item = {
|
|
4
|
+
item_id: string
|
|
5
|
+
item_name: string
|
|
6
|
+
affiliation?: string
|
|
7
|
+
coupon?: string
|
|
8
|
+
currency?: string
|
|
9
|
+
discount?: number
|
|
10
|
+
index?: number
|
|
11
|
+
item_brand?: string
|
|
12
|
+
item_category?: string
|
|
13
|
+
item_category2?: string
|
|
14
|
+
item_category3?: string
|
|
15
|
+
item_category4?: string
|
|
16
|
+
item_category5?: string
|
|
17
|
+
item_list_id?: string
|
|
18
|
+
item_list_name?: string
|
|
19
|
+
item_variant?: string
|
|
20
|
+
location_id?: string
|
|
21
|
+
price?: number
|
|
22
|
+
quantity?: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function productToItem<P extends ProductToItemFragment>(item: P): Item {
|
|
26
|
+
return {
|
|
27
|
+
item_id: item.sku ?? '',
|
|
28
|
+
item_name: item.name ?? '',
|
|
29
|
+
price: item.price_range?.minimum_price.final_price.value ?? undefined,
|
|
30
|
+
currency: item.price_range?.minimum_price.final_price.currency ?? undefined,
|
|
31
|
+
discount: item.price_range?.minimum_price.discount?.amount_off ?? undefined,
|
|
32
|
+
item_category: item.categories?.[0]?.name ?? undefined,
|
|
33
|
+
item_category2: item.categories?.[1]?.name ?? undefined,
|
|
34
|
+
item_category3: item.categories?.[2]?.name ?? undefined,
|
|
35
|
+
item_category4: item.categories?.[3]?.name ?? undefined,
|
|
36
|
+
item_category5: item.categories?.[4]?.name ?? undefined,
|
|
37
|
+
}
|
|
38
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphcommerce/google-datalayer",
|
|
3
|
+
"homepage": "https://www.graphcommerce.org/",
|
|
4
|
+
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
+
"version": "8.0.4-canary.1",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
|
+
"eslintConfig": {
|
|
9
|
+
"extends": "@graphcommerce/eslint-config-pwa",
|
|
10
|
+
"parserOptions": {
|
|
11
|
+
"project": "./tsconfig.json"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"@graphcommerce/eslint-config-pwa": "^8.0.4-canary.1",
|
|
16
|
+
"@graphcommerce/graphql-mesh": "^8.0.4-canary.1",
|
|
17
|
+
"@graphcommerce/magento-cart": "^8.0.4-canary.1",
|
|
18
|
+
"@graphcommerce/magento-cart-payment-method": "^8.0.4-canary.1",
|
|
19
|
+
"@graphcommerce/magento-cart-shipping-method": "^8.0.4-canary.1",
|
|
20
|
+
"@graphcommerce/magento-product": "^8.0.4-canary.1",
|
|
21
|
+
"@graphcommerce/next-config": "^8.0.4-canary.1",
|
|
22
|
+
"@graphcommerce/next-ui": "^8.0.4-canary.1",
|
|
23
|
+
"@graphcommerce/prettier-config-pwa": "^8.0.4-canary.1",
|
|
24
|
+
"@graphcommerce/typescript-config-pwa": "^8.0.4-canary.1",
|
|
25
|
+
"@mui/material": "^5.14.20",
|
|
26
|
+
"next": "^14",
|
|
27
|
+
"react": "^18.2.0",
|
|
28
|
+
"react-dom": "^18.2.0"
|
|
29
|
+
},
|
|
30
|
+
"peerDependenciesMeta": {
|
|
31
|
+
"@graphcommerce/magento-cart": {
|
|
32
|
+
"optional": true
|
|
33
|
+
},
|
|
34
|
+
"@graphcommerce/magento-cart-payment-method": {
|
|
35
|
+
"optional": true
|
|
36
|
+
},
|
|
37
|
+
"@graphcommerce/magento-cart-shipping-method": {
|
|
38
|
+
"optional": true
|
|
39
|
+
},
|
|
40
|
+
"@graphcommerce/magento-product": {
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AddProductsToCartFormProps } from '@graphcommerce/magento-product'
|
|
2
|
+
import { PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { addToCart } from '../events/add_to_cart'
|
|
4
|
+
|
|
5
|
+
export const component = 'AddProductsToCartForm'
|
|
6
|
+
export const exported = '@graphcommerce/magento-product'
|
|
7
|
+
|
|
8
|
+
/** When a product is added to the Cart, send a Google Analytics event */
|
|
9
|
+
function GoogleDatalayerAddProductsToCartForm(props: PluginProps<AddProductsToCartFormProps>) {
|
|
10
|
+
const { Prev, onComplete, ...rest } = props
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<Prev
|
|
14
|
+
{...rest}
|
|
15
|
+
onComplete={(result, variables) => {
|
|
16
|
+
addToCart(result, variables)
|
|
17
|
+
return onComplete?.(result, variables)
|
|
18
|
+
}}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const Plugin = GoogleDatalayerAddProductsToCartForm
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PagesProps } from '@graphcommerce/framer-next-pages'
|
|
2
|
+
import type { PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { useRouter } from 'next/router'
|
|
4
|
+
import { useEffect } from 'react'
|
|
5
|
+
import { event } from '../lib/event'
|
|
6
|
+
|
|
7
|
+
export const component = 'FramerNextPages'
|
|
8
|
+
export const exported = '@graphcommerce/framer-next-pages'
|
|
9
|
+
|
|
10
|
+
function GoogleDatalayerAllPagesPageview(props: PluginProps<PagesProps>) {
|
|
11
|
+
const { Prev, ...rest } = props
|
|
12
|
+
|
|
13
|
+
const { events } = useRouter()
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const onRouteChangeComplete = (url: string) => {
|
|
17
|
+
/**
|
|
18
|
+
* Todo: the actual page_view event is currently disabled, because we run the risk of double counting page views.
|
|
19
|
+
* https://developers.google.com/analytics/devguides/collection/ga4/views?client_type=gtag#manually_send_page_view_events
|
|
20
|
+
*
|
|
21
|
+
* https://developers.google.com/analytics/devguides/collection/ga4/single-page-applications?implementation=event
|
|
22
|
+
*/
|
|
23
|
+
// event('page_view', {
|
|
24
|
+
// page_title: '<Page Title>',
|
|
25
|
+
// page_location: '<Page Location>',
|
|
26
|
+
// })
|
|
27
|
+
// event('pageview', { page: url })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
events.on('routeChangeComplete', onRouteChangeComplete)
|
|
31
|
+
return () => events.off('routeChangeComplete', onRouteChangeComplete)
|
|
32
|
+
}, [events])
|
|
33
|
+
|
|
34
|
+
return <Prev {...rest} />
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const Plugin = GoogleDatalayerAllPagesPageview
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CartStartCheckoutProps } from '@graphcommerce/magento-cart'
|
|
2
|
+
import { PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { beginCheckout } from '../events/begin_checkout'
|
|
4
|
+
|
|
5
|
+
export const component = 'CartStartCheckout'
|
|
6
|
+
export const exported = '@graphcommerce/magento-cart'
|
|
7
|
+
|
|
8
|
+
export function GoogleDatalayerCartStartCheckout(props: PluginProps<CartStartCheckoutProps>) {
|
|
9
|
+
const { Prev, onStart, ...rest } = props
|
|
10
|
+
return (
|
|
11
|
+
<Prev
|
|
12
|
+
{...rest}
|
|
13
|
+
onStart={(e, cart) => {
|
|
14
|
+
beginCheckout(cart)
|
|
15
|
+
return onStart?.(e, cart)
|
|
16
|
+
}}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const Plugin = GoogleDatalayerCartStartCheckout
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CartStartCheckoutLinkOrButtonProps } from '@graphcommerce/magento-cart'
|
|
2
|
+
import { PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { useMemoObject } from '@graphcommerce/next-ui'
|
|
4
|
+
import { useEffect } from 'react'
|
|
5
|
+
import { beginCheckout } from '../events/begin_checkout'
|
|
6
|
+
import { viewCart } from '../events/view_cart'
|
|
7
|
+
|
|
8
|
+
export const component = 'CartStartCheckoutLinkOrButton'
|
|
9
|
+
export const exported = '@graphcommerce/magento-cart'
|
|
10
|
+
|
|
11
|
+
export function GoogleDatalayerCartStartCheckoutLinkOrButton(
|
|
12
|
+
props: PluginProps<CartStartCheckoutLinkOrButtonProps>,
|
|
13
|
+
) {
|
|
14
|
+
const { Prev, onStart, ...rest } = props
|
|
15
|
+
|
|
16
|
+
const cartObject = useMemoObject({ items: rest.items, prices: rest.prices })
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (cartObject.items) {
|
|
20
|
+
viewCart(cartObject)
|
|
21
|
+
}
|
|
22
|
+
}, [cartObject])
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Prev
|
|
26
|
+
{...rest}
|
|
27
|
+
onStart={(e, cart) => {
|
|
28
|
+
beginCheckout(cart)
|
|
29
|
+
return onStart?.(e, cart)
|
|
30
|
+
}}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Plugin = GoogleDatalayerCartStartCheckoutLinkOrButton
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useCartQuery } from '@graphcommerce/magento-cart'
|
|
2
|
+
import { PaymentMethodButtonProps } from '@graphcommerce/magento-cart-payment-method'
|
|
3
|
+
import { GetPaymentMethodContextDocument } from '@graphcommerce/magento-cart-payment-method/PaymentMethodContext/GetPaymentMethodContext.gql'
|
|
4
|
+
import { PluginProps } from '@graphcommerce/next-config'
|
|
5
|
+
import { addPaymentInfo } from '../events/add_payment_info'
|
|
6
|
+
|
|
7
|
+
export const component = 'PaymentMethodButton'
|
|
8
|
+
export const exported = '@graphcommerce/magento-cart-payment-method'
|
|
9
|
+
|
|
10
|
+
// @todo This plugin can probably be migrated to the actual form that is submitted.
|
|
11
|
+
function GoogleDatalayerPaymentMethodButton(props: PluginProps<PaymentMethodButtonProps>) {
|
|
12
|
+
const { Prev, onSubmitSuccessful, ...rest } = props
|
|
13
|
+
const methodContext = useCartQuery(GetPaymentMethodContextDocument)
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Prev
|
|
17
|
+
{...rest}
|
|
18
|
+
onSubmitSuccessful={() => {
|
|
19
|
+
addPaymentInfo(methodContext.data?.cart)
|
|
20
|
+
return onSubmitSuccessful?.()
|
|
21
|
+
}}
|
|
22
|
+
/>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const Plugin = GoogleDatalayerPaymentMethodButton
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PaymentMethodContextProviderProps } from '@graphcommerce/magento-cart-payment-method'
|
|
2
|
+
import type { PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { purchase } from '../events/purchase'
|
|
4
|
+
|
|
5
|
+
export const component = 'PaymentMethodContextProvider'
|
|
6
|
+
export const exported = '@graphcommerce/magento-cart-payment-method'
|
|
7
|
+
|
|
8
|
+
function GoogleDatalayerPaymentMethodContextProvider(
|
|
9
|
+
props: PluginProps<PaymentMethodContextProviderProps>,
|
|
10
|
+
) {
|
|
11
|
+
const { Prev, onSuccess, ...rest } = props
|
|
12
|
+
return (
|
|
13
|
+
<Prev
|
|
14
|
+
{...rest}
|
|
15
|
+
onSuccess={(orderNumber, cart) => {
|
|
16
|
+
purchase(orderNumber, cart)
|
|
17
|
+
return onSuccess?.(orderNumber, cart)
|
|
18
|
+
}}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
export const Plugin = GoogleDatalayerPaymentMethodContextProvider
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ProductListItemReal } from '@graphcommerce/magento-product'
|
|
2
|
+
import { PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { useEventCallback } from '@mui/material'
|
|
4
|
+
import { ComponentProps } from 'react'
|
|
5
|
+
import { useListItemHandler } from '../components/AnalyticsItemList'
|
|
6
|
+
|
|
7
|
+
export const component = 'ProductListItemReal'
|
|
8
|
+
export const exported = '@graphcommerce/magento-product'
|
|
9
|
+
|
|
10
|
+
function GoogleDatalayerProductListItem(
|
|
11
|
+
props: PluginProps<ComponentProps<typeof ProductListItemReal>>,
|
|
12
|
+
) {
|
|
13
|
+
const { Prev, onClick, ...rest } = props
|
|
14
|
+
const { sku, price_range, name } = rest
|
|
15
|
+
const handle = useListItemHandler({ sku, price_range, name })
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Prev
|
|
19
|
+
{...rest}
|
|
20
|
+
onClick={useEventCallback<NonNullable<typeof onClick>>((e, item) => {
|
|
21
|
+
handle()
|
|
22
|
+
return onClick?.(e, item)
|
|
23
|
+
})}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const Plugin = GoogleDatalayerProductListItem
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProductItemsGridProps } from '@graphcommerce/magento-product'
|
|
2
|
+
import { PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { ItemList } from '../components/AnalyticsItemList'
|
|
4
|
+
|
|
5
|
+
export const component = 'ProductListItemsBase'
|
|
6
|
+
export const exported = '@graphcommerce/magento-product'
|
|
7
|
+
|
|
8
|
+
export function GoogleDatalayerProductListItemsBase(props: PluginProps<ProductItemsGridProps>) {
|
|
9
|
+
const { Prev, ...rest } = props
|
|
10
|
+
return (
|
|
11
|
+
<ItemList {...rest}>
|
|
12
|
+
<Prev {...rest} />
|
|
13
|
+
</ItemList>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Plugin = GoogleDatalayerProductListItemsBase
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RemoveItemFromCart as Original } from '@graphcommerce/magento-cart-items'
|
|
2
|
+
import { ReactPlugin } from '@graphcommerce/next-config'
|
|
3
|
+
import { removeFromCart } from '../events/remove_from_cart'
|
|
4
|
+
|
|
5
|
+
export const component = 'RemoveItemFromCart'
|
|
6
|
+
export const exported =
|
|
7
|
+
'@graphcommerce/magento-cart-items/components/RemoveItemFromCart/RemoveItemFromCart'
|
|
8
|
+
|
|
9
|
+
export const GoogleDatalayerRemoveItemFromCart: ReactPlugin<typeof Original> = (props) => {
|
|
10
|
+
const { Prev, uid, quantity, prices, product, buttonProps } = props
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<Prev
|
|
14
|
+
{...props}
|
|
15
|
+
product={product}
|
|
16
|
+
buttonProps={{
|
|
17
|
+
onClick: (e) => {
|
|
18
|
+
removeFromCart({
|
|
19
|
+
__typename: 'Cart',
|
|
20
|
+
items: [{ uid, __typename: 'SimpleCartItem', product, quantity, prices }],
|
|
21
|
+
})
|
|
22
|
+
buttonProps?.onClick?.(e)
|
|
23
|
+
},
|
|
24
|
+
}}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const Plugin = GoogleDatalayerRemoveItemFromCart
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RemoveItemFromCartFab as Original } from '@graphcommerce/magento-cart-items'
|
|
2
|
+
import { ReactPlugin } from '@graphcommerce/next-config'
|
|
3
|
+
import { removeFromCart } from '../events/remove_from_cart'
|
|
4
|
+
|
|
5
|
+
export const component = 'RemoveItemFromCartFab'
|
|
6
|
+
export const exported =
|
|
7
|
+
'@graphcommerce/magento-cart-items/components/RemoveItemFromCart/RemoveItemFromCartFab'
|
|
8
|
+
|
|
9
|
+
export const GoogleDatalayerRemoveItemFromCartFab: ReactPlugin<typeof Original> = (props) => {
|
|
10
|
+
const { Prev, uid, quantity, prices, product, fabProps } = props
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<Prev
|
|
14
|
+
{...props}
|
|
15
|
+
product={product}
|
|
16
|
+
fabProps={{
|
|
17
|
+
onClick: (e) => {
|
|
18
|
+
removeFromCart({
|
|
19
|
+
__typename: 'Cart',
|
|
20
|
+
items: [{ uid, __typename: 'SimpleCartItem', product, quantity, prices }],
|
|
21
|
+
})
|
|
22
|
+
fabProps?.onClick?.(e)
|
|
23
|
+
},
|
|
24
|
+
}}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const Plugin = GoogleDatalayerRemoveItemFromCartFab
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ShippingMethodFormProps } from '@graphcommerce/magento-cart-shipping-method'
|
|
2
|
+
import { PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { addShippingInfo } from '../events/add_shipping_info'
|
|
4
|
+
|
|
5
|
+
export const component = 'ShippingMethodForm'
|
|
6
|
+
export const exported = '@graphcommerce/magento-cart-shipping-method'
|
|
7
|
+
|
|
8
|
+
/** When the ShippingMethod is submitted the result is sent to Google Analytics */
|
|
9
|
+
export function GoogleDatalayerShippingMethodForm(props: PluginProps<ShippingMethodFormProps>) {
|
|
10
|
+
const { Prev, onComplete, ...rest } = props
|
|
11
|
+
return (
|
|
12
|
+
<Prev
|
|
13
|
+
{...rest}
|
|
14
|
+
onComplete={(result, variables) => {
|
|
15
|
+
addShippingInfo(result.data?.setShippingMethodsOnCart?.cart)
|
|
16
|
+
return onComplete?.(result, variables)
|
|
17
|
+
}}
|
|
18
|
+
/>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const Plugin = GoogleDatalayerShippingMethodForm
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { UpdateItemQuantityProps } from '@graphcommerce/magento-cart-items'
|
|
2
|
+
import { PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { event } from '../lib/event'
|
|
4
|
+
|
|
5
|
+
export const component = 'UpdateItemQuantity'
|
|
6
|
+
export const exported =
|
|
7
|
+
'@graphcommerce/magento-cart-items/components/UpdateItemQuantity/UpdateItemQuantity'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* When a product is added to the Cart, by using the + button on cart page, send a Google Analytics
|
|
11
|
+
* event
|
|
12
|
+
*/
|
|
13
|
+
function GoogleDatalayerUpdateItemQuantity(props: PluginProps<UpdateItemQuantityProps>) {
|
|
14
|
+
const { Prev, formOptions, quantity, ...rest } = props
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Prev
|
|
18
|
+
{...rest}
|
|
19
|
+
quantity={quantity}
|
|
20
|
+
formOptions={{
|
|
21
|
+
...formOptions,
|
|
22
|
+
onComplete: (data, variables) => {
|
|
23
|
+
const original = formOptions?.onComplete?.(data, variables)
|
|
24
|
+
const diffQuantity = variables.quantity - quantity
|
|
25
|
+
if (diffQuantity === 0) return original
|
|
26
|
+
|
|
27
|
+
const itemId = variables.uid
|
|
28
|
+
const addedItem = data.data?.updateCartItems?.cart.items?.find(
|
|
29
|
+
(item) => item?.uid === itemId,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if (addedItem && addedItem.prices && addedItem.prices.row_total_including_tax.value) {
|
|
33
|
+
// we need to manually calculate pricePerItemInclTax (https://github.com/magento/magento2/issues/33848)
|
|
34
|
+
const pricePerItemInclTax =
|
|
35
|
+
addedItem.prices.row_total_including_tax.value / addedItem.quantity
|
|
36
|
+
const addToCartValue = pricePerItemInclTax * diffQuantity
|
|
37
|
+
|
|
38
|
+
event('add_to_cart', {
|
|
39
|
+
currency: addedItem?.prices?.price.currency,
|
|
40
|
+
value: addToCartValue,
|
|
41
|
+
items: [
|
|
42
|
+
{
|
|
43
|
+
item_id: addedItem?.product.sku,
|
|
44
|
+
item_name: addedItem?.product.name,
|
|
45
|
+
currency: addedItem?.prices?.price.currency,
|
|
46
|
+
price: pricePerItemInclTax,
|
|
47
|
+
quantity: variables.quantity,
|
|
48
|
+
discount: addedItem?.prices?.discounts?.reduce(
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
|
50
|
+
(sum, discount) => sum + (discount?.amount?.value ?? 0),
|
|
51
|
+
0,
|
|
52
|
+
),
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return original
|
|
59
|
+
},
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const Plugin = GoogleDatalayerUpdateItemQuantity
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ProductPageMeta } from '@graphcommerce/magento-product'
|
|
2
|
+
import { PluginProps } from '@graphcommerce/next-config'
|
|
3
|
+
import { useMemoObject } from '@graphcommerce/next-ui'
|
|
4
|
+
import React, { useEffect } from 'react'
|
|
5
|
+
import { productToItem } from '../lib'
|
|
6
|
+
import { event } from '../lib/event'
|
|
7
|
+
|
|
8
|
+
export const component = 'ProductPageMeta'
|
|
9
|
+
export const exported = '@graphcommerce/magento-product'
|
|
10
|
+
|
|
11
|
+
/** When a product is added to the Cart, send a Google Analytics event */
|
|
12
|
+
function GoogleDatalayerViewItem(props: PluginProps<React.ComponentProps<typeof ProductPageMeta>>) {
|
|
13
|
+
const { Prev, product } = props
|
|
14
|
+
const { price_range } = product
|
|
15
|
+
|
|
16
|
+
const viewItem = useMemoObject({
|
|
17
|
+
currency: price_range.minimum_price.final_price.currency,
|
|
18
|
+
value: price_range.minimum_price.final_price.value,
|
|
19
|
+
items: [productToItem(product)],
|
|
20
|
+
})
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
event('view_item', viewItem)
|
|
24
|
+
}, [viewItem])
|
|
25
|
+
|
|
26
|
+
return <Prev {...props} />
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const Plugin = GoogleDatalayerViewItem
|