@graphcommerce/google-datalayer 8.1.0-canary.8 → 9.0.0-canary.100

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 (40) hide show
  1. package/CHANGELOG.md +205 -10
  2. package/Config.graphqls +3 -0
  3. package/api/googleEventNames.ts +116 -0
  4. package/api/sendEvent.ts +15 -7
  5. package/components/DatalayerViewItemList.tsx +55 -6
  6. package/mapping/cartItemToDatalayerItem/cartItemToDatalayerItem.ts +3 -2
  7. package/mapping/cartItemToRemoveFromCart/cartToRemoveFromCart.ts +2 -5
  8. package/mapping/cartToAddPaymentInfo/Cart_AddPaymentInfo.graphql +1 -1
  9. package/mapping/cartToAddPaymentInfo/cartToAddPaymentInfo.ts +10 -3
  10. package/mapping/cartToAddShippingInfo/Cart_AddShippingInfo.graphql +1 -1
  11. package/mapping/cartToAddShippingInfo/cartToAddShippingInfo.ts +14 -6
  12. package/mapping/cartToBeginCheckout/Cart_BeginCheckout.graphql +1 -1
  13. package/mapping/cartToBeginCheckout/cartToBeginCheckout.ts +7 -3
  14. package/mapping/cartToPurchase/Cart_PurchaseEvent.graphql +18 -0
  15. package/mapping/cartToPurchase/cartToPurchase.ts +28 -0
  16. package/mapping/cartToViewCart/Cart_ViewCart.graphql +8 -1
  17. package/mapping/cartToViewCart/cartToViewCart.ts +12 -3
  18. package/mapping/datalayerItemsToCurrencyValue/datalayerItemsToCurrencyValue.ts +9 -2
  19. package/mapping/productItemsToViewItemList/productItemsToViewItemList.ts +28 -6
  20. package/mapping/productToDatalayerItem/Product_DatalayerItem.graphql +1 -6
  21. package/mapping/productToDatalayerItem/productToDatalayerItem.ts +11 -4
  22. package/mapping/productToViewItem/productToViewItem.ts +13 -8
  23. package/package.json +12 -9
  24. package/plugins/GoogleDatalayerAddProductsToCartForm.tsx +13 -9
  25. package/plugins/GoogleDatalayerCartStartCheckout.tsx +9 -8
  26. package/plugins/GoogleDatalayerCartStartCheckoutLinkOrButton.tsx +8 -7
  27. package/plugins/GoogleDatalayerPaymentMethodButton.tsx +8 -7
  28. package/plugins/GoogleDatalayerPaymentMethodContextProvider.tsx +11 -8
  29. package/plugins/GoogleDatalayerProductListItem.tsx +6 -6
  30. package/plugins/GoogleDatalayerProductListItemsBase.tsx +15 -9
  31. package/plugins/GoogleDatalayerRemoveItemFromCart.tsx +21 -22
  32. package/plugins/GoogleDatalayerShippingMethodForm.tsx +10 -8
  33. package/plugins/{GoogleDatalayerUpdateItemQuantity.tsx → GoogleDatalayerUseRemoveItemFromCart.tsx} +9 -9
  34. package/plugins/GoogleDatalayerUseSignInForm.tsx +0 -0
  35. package/plugins/GoogleDatalayerViewItem.tsx +10 -9
  36. package/plugins/GoogleDatalayerWebVitals.tsx +2 -1
  37. package/mapping/cartToDatalayerItems/Cart_DatalayerItems.graphql +0 -11
  38. package/mapping/cartToDatalayerItems/cartToDatalayerItems.ts +0 -10
  39. package/mapping/orderToPurchase/orderToPurchase.ts +0 -17
  40. package/plugins/GoogleDatalayerRemoveItemFromCartFab.tsx +0 -29
package/CHANGELOG.md CHANGED
@@ -1,5 +1,205 @@
1
1
  # @graphcommerce/google-datalayer
2
2
 
3
+ ## 9.0.0-canary.100
4
+
5
+ ## 9.0.0-canary.99
6
+
7
+ ## 9.0.0-canary.98
8
+
9
+ ## 9.0.0-canary.97
10
+
11
+ ## 9.0.0-canary.96
12
+
13
+ ## 9.0.0-canary.95
14
+
15
+ ## 9.0.0-canary.94
16
+
17
+ ## 9.0.0-canary.93
18
+
19
+ ## 9.0.0-canary.92
20
+
21
+ ## 9.0.0-canary.91
22
+
23
+ ## 9.0.0-canary.90
24
+
25
+ ## 9.0.0-canary.89
26
+
27
+ ## 9.0.0-canary.88
28
+
29
+ ## 9.0.0-canary.87
30
+
31
+ ## 9.0.0-canary.86
32
+
33
+ ## 9.0.0-canary.85
34
+
35
+ ## 9.0.0-canary.84
36
+
37
+ ## 9.0.0-canary.83
38
+
39
+ ## 9.0.0-canary.82
40
+
41
+ ## 9.0.0-canary.81
42
+
43
+ ## 9.0.0-canary.80
44
+
45
+ ### Patch Changes
46
+
47
+ - [#2341](https://github.com/graphcommerce-org/graphcommerce/pull/2341) [`e3fe4f7`](https://github.com/graphcommerce-org/graphcommerce/commit/e3fe4f73c8c3e3c6a5ec68cdc7a32820e8f69e07) - Solve an issue where the BillingPage query would be re-queried after setting the payment method. ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
48
+
49
+ ## 9.0.0-canary.79
50
+
51
+ ## 9.0.0-canary.78
52
+
53
+ ## 9.0.0-canary.77
54
+
55
+ ## 9.0.0-canary.76
56
+
57
+ ## 9.0.0-canary.75
58
+
59
+ ## 9.0.0-canary.74
60
+
61
+ ## 9.0.0-canary.73
62
+
63
+ ### Minor Changes
64
+
65
+ - [#2337](https://github.com/graphcommerce-org/graphcommerce/pull/2337) [`18898df`](https://github.com/graphcommerce-org/graphcommerce/commit/18898df44b786dd68d8e6fec538e3db947c157e4) - All sendEvent calls are now the return type of useSendEvent, to allow plugins to use hooks themselves ([@Renzovh](https://github.com/Renzovh))
66
+
67
+ ## 9.0.0-canary.72
68
+
69
+ ## 9.0.0-canary.71
70
+
71
+ ## 9.0.0-canary.70
72
+
73
+ ## 9.0.0-canary.69
74
+
75
+ ## 9.0.0-canary.68
76
+
77
+ ## 9.0.0-canary.67
78
+
79
+ ## 9.0.0-canary.66
80
+
81
+ ## 9.0.0-canary.65
82
+
83
+ ## 9.0.0-canary.64
84
+
85
+ ## 9.0.0-canary.63
86
+
87
+ ## 9.0.0-canary.62
88
+
89
+ ## 9.0.0-canary.61
90
+
91
+ ## 9.0.0-canary.60
92
+
93
+ ### Patch Changes
94
+
95
+ - [#2331](https://github.com/graphcommerce-org/graphcommerce/pull/2331) [`702bfc9`](https://github.com/graphcommerce-org/graphcommerce/commit/702bfc93566c9745546988e57988431d5d4d8cb0) - Moved plugins to new format ([@paales](https://github.com/paales))
96
+
97
+ ## 9.0.0-canary.59
98
+
99
+ ## 9.0.0-canary.58
100
+
101
+ ## 9.0.0-canary.57
102
+
103
+ ## 9.0.0-canary.56
104
+
105
+ ## 9.0.0-canary.55
106
+
107
+ ## 9.0.0-canary.54
108
+
109
+ ## 8.1.0-canary.53
110
+
111
+ ## 8.1.0-canary.52
112
+
113
+ ## 8.1.0-canary.51
114
+
115
+ ## 8.1.0-canary.50
116
+
117
+ ## 8.1.0-canary.49
118
+
119
+ ## 8.1.0-canary.48
120
+
121
+ ## 8.1.0-canary.47
122
+
123
+ ## 8.1.0-canary.46
124
+
125
+ ## 8.1.0-canary.45
126
+
127
+ ## 8.1.0-canary.44
128
+
129
+ ## 8.1.0-canary.43
130
+
131
+ ## 8.1.0-canary.42
132
+
133
+ ## 8.1.0-canary.41
134
+
135
+ ## 8.1.0-canary.40
136
+
137
+ ## 8.1.0-canary.39
138
+
139
+ ## 8.1.0-canary.38
140
+
141
+ ## 8.1.0-canary.37
142
+
143
+ ## 8.1.0-canary.36
144
+
145
+ ## 8.1.0-canary.35
146
+
147
+ ## 8.1.0-canary.34
148
+
149
+ ## 8.1.0-canary.33
150
+
151
+ ## 8.1.0-canary.32
152
+
153
+ ## 8.1.0-canary.31
154
+
155
+ ## 8.1.0-canary.30
156
+
157
+ ## 8.1.0-canary.29
158
+
159
+ ## 8.1.0-canary.28
160
+
161
+ ## 8.1.0-canary.27
162
+
163
+ ## 8.1.0-canary.26
164
+
165
+ ## 8.1.0-canary.25
166
+
167
+ ## 8.1.0-canary.24
168
+
169
+ ## 8.1.0-canary.23
170
+
171
+ ## 8.1.0-canary.22
172
+
173
+ ## 8.1.0-canary.21
174
+
175
+ ## 8.1.0-canary.20
176
+
177
+ ### Patch Changes
178
+
179
+ - [#2246](https://github.com/graphcommerce-org/graphcommerce/pull/2246) [`fc5c04d`](https://github.com/graphcommerce-org/graphcommerce/commit/fc5c04d4a2c0301be7d3cc983d9b31f6fcaf6fe6) - Create useRemoveItemFromCart hook to allow for reuse while keeping compatibility with plugins. ([@Jessevdpoel](https://github.com/Jessevdpoel))
180
+
181
+ ## 8.1.0-canary.19
182
+
183
+ ## 8.1.0-canary.18
184
+
185
+ ## 8.1.0-canary.17
186
+
187
+ ## 8.1.0-canary.16
188
+
189
+ ## 8.1.0-canary.15
190
+
191
+ ## 8.1.0-canary.14
192
+
193
+ ## 8.1.0-canary.13
194
+
195
+ ## 8.1.0-canary.12
196
+
197
+ ## 8.1.0-canary.11
198
+
199
+ ## 8.1.0-canary.10
200
+
201
+ ## 8.1.0-canary.9
202
+
3
203
  ## 8.1.0-canary.8
4
204
 
5
205
  ## 8.1.0-canary.7
@@ -16,8 +216,7 @@
16
216
 
17
217
  ### Patch Changes
18
218
 
19
- - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`43bd04a`](https://github.com/graphcommerce-org/graphcommerce/commit/43bd04a777c5800cc7e01bee1e123a5aad82f194) - Prevent BillingPage query from rerunning on each mutation
20
- ([@FrankHarland](https://github.com/FrankHarland))
219
+ - [#2234](https://github.com/graphcommerce-org/graphcommerce/pull/2234) [`43bd04a`](https://github.com/graphcommerce-org/graphcommerce/commit/43bd04a777c5800cc7e01bee1e123a5aad82f194) - Prevent BillingPage query from rerunning on each mutation ([@FrankHarland](https://github.com/FrankHarland))
21
220
 
22
221
  ## 8.0.6-canary.1
23
222
 
@@ -32,8 +231,7 @@
32
231
  - Removed `eventFormat` as we could automatically detec the correct event format and it is now the responsibility of GTM or the GTAG to handle the event format.
33
232
  - Created cartItemToGoogleDatalayerItem and productToGoogleDatalayerItem for easier modifications. ([@paales](https://github.com/paales))
34
233
 
35
- - [#2233](https://github.com/graphcommerce-org/graphcommerce/pull/2233) [`cabeadc`](https://github.com/graphcommerce-org/graphcommerce/commit/cabeadce2b73ce072a2fa8b8ab1ab49907cda13b) - Added core web vitals measurements to the datalayer.
36
- ([@paales](https://github.com/paales))
234
+ - [#2233](https://github.com/graphcommerce-org/graphcommerce/pull/2233) [`cabeadc`](https://github.com/graphcommerce-org/graphcommerce/commit/cabeadce2b73ce072a2fa8b8ab1ab49907cda13b) - Added core web vitals measurements to the datalayer. ([@paales](https://github.com/paales))
37
235
 
38
236
  ## 8.0.5-canary.10
39
237
 
@@ -62,8 +260,7 @@
62
260
  - Removed `eventFormat` as we could automatically detec the correct event format and it is now the responsibility of GTM or the GTAG to handle the event format.
63
261
  - Created cartItemToGoogleDatalayerItem and productToGoogleDatalayerItem for easier modifications. ([@paales](https://github.com/paales))
64
262
 
65
- - [#2233](https://github.com/graphcommerce-org/graphcommerce/pull/2233) [`cabeadc`](https://github.com/graphcommerce-org/graphcommerce/commit/cabeadce2b73ce072a2fa8b8ab1ab49907cda13b) - Added core web vitals measurements to the datalayer.
66
- ([@paales](https://github.com/paales))
263
+ - [#2233](https://github.com/graphcommerce-org/graphcommerce/pull/2233) [`cabeadc`](https://github.com/graphcommerce-org/graphcommerce/commit/cabeadce2b73ce072a2fa8b8ab1ab49907cda13b) - Added core web vitals measurements to the datalayer. ([@paales](https://github.com/paales))
67
264
 
68
265
  ## 8.0.5-canary.0
69
266
 
@@ -71,12 +268,10 @@
71
268
 
72
269
  ### Patch Changes
73
270
 
74
- - [#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.
75
- ([@mikekeehnen](https://github.com/mikekeehnen))
271
+ - [#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. ([@mikekeehnen](https://github.com/mikekeehnen))
76
272
 
77
273
  ## 8.0.4-canary.1
78
274
 
79
275
  ### Patch Changes
80
276
 
81
- - [#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.
82
- ([@mikekeehnen](https://github.com/mikekeehnen))
277
+ - [#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. ([@mikekeehnen](https://github.com/mikekeehnen))
package/Config.graphqls CHANGED
@@ -1,4 +1,7 @@
1
1
  extend input GraphCommerceConfig {
2
+ """
3
+ Datalayer config
4
+ """
2
5
  dataLayer: DatalayerConfig
3
6
  }
4
7
 
@@ -1,3 +1,17 @@
1
+ import { Metric } from 'web-vitals'
2
+ import type { AddPaymentInfo } from '../mapping/cartToAddPaymentInfo/cartToAddPaymentInfo'
3
+ import type { AddShippingInfo } from '../mapping/cartToAddShippingInfo/cartToAddShippingInfo'
4
+ import type { BeginCheckout } from '../mapping/cartToBeginCheckout/cartToBeginCheckout'
5
+ import type { PurchaseOrRefund } from '../mapping/cartToPurchase/cartToPurchase'
6
+ import type { ViewCart } from '../mapping/cartToViewCart/cartToViewCart'
7
+ import type { DataLayerCurrencyValue } from '../mapping/datalayerItemsToCurrencyValue/datalayerItemsToCurrencyValue'
8
+ import type {
9
+ SelectItem,
10
+ ViewItemList,
11
+ } from '../mapping/productItemsToViewItemList/productItemsToViewItemList'
12
+ import type { GoogleDatalayerItem } from '../mapping/productToDatalayerItem/productToDatalayerItem'
13
+ import type { ViewItem } from '../mapping/productToViewItem/productToViewItem'
14
+
1
15
  export const googleEventNames = [
2
16
  'add_payment_info',
3
17
  'add_shipping_info',
@@ -37,3 +51,105 @@ export const googleEventNames = [
37
51
  'view_promotion',
38
52
  'view_search_results',
39
53
  ] as const
54
+
55
+ /**
56
+ * @see https://developers.google.com/tag-platform/gtagjs/reference/events
57
+ */
58
+ export type GoogleEventTypes = {
59
+ exception: { description?: string; fatal?: boolean }
60
+ share: { method?: string; content_type?: string; item_id?: string }
61
+
62
+ add_payment_info: AddPaymentInfo
63
+ add_shipping_info: AddShippingInfo
64
+ add_to_cart: ViewCart
65
+ add_to_wishlist: DataLayerCurrencyValue & { items: GoogleDatalayerItem[] }
66
+ begin_checkout: BeginCheckout
67
+
68
+ login: { method?: string }
69
+ sign_up: {
70
+ method?: string
71
+ }
72
+
73
+ page_view: {
74
+ page_location?: string
75
+ client_id?: string
76
+ language?: string
77
+ page_encoding?: string
78
+ page_title?: string
79
+ user_agent?: string
80
+ }
81
+
82
+ purchase: PurchaseOrRefund
83
+ refund: PurchaseOrRefund
84
+ remove_from_cart: ViewCart
85
+
86
+ select_content: {
87
+ content_type?: string
88
+ content_id?: string
89
+ data?: object
90
+ }
91
+ select_item: SelectItem
92
+
93
+ view_cart: ViewCart
94
+ view_item: ViewItem
95
+ view_item_list: ViewItemList
96
+
97
+ search: { search_term?: string }
98
+ view_search_results: { search_term?: string }
99
+
100
+ view_promotion: {
101
+ creative_name?: string
102
+ creative_slot?: string
103
+ promotion_id?: string
104
+ promotion_name?: string
105
+ items?: GoogleDatalayerItem[]
106
+ }
107
+ select_promotion: {
108
+ creative_name?: string
109
+ creative_slot?: string
110
+ promotion_id?: string
111
+ promotion_name?: string
112
+ items?: GoogleDatalayerItem[]
113
+ }
114
+
115
+ // Tutorial
116
+ tutorial_begin: object
117
+ tutorial_complete: object
118
+
119
+ // Gaming
120
+ earn_virtual_currency: { value?: string; virtual_currency_name?: string }
121
+ join_group: { group_id?: string }
122
+ level_end: { level_name?: string; success?: boolean }
123
+ level_start: { level_name?: string }
124
+ level_up: { level?: number; character?: string }
125
+ post_score: { score?: number; level?: number; character?: string }
126
+ spend_virtual_currency: {
127
+ value?: string
128
+ virtual_currency_name?: string
129
+ item_name?: string
130
+ }
131
+ unlock_achievement: { achievement_id: string }
132
+
133
+ // Leads
134
+ close_convert_lead: DataLayerCurrencyValue
135
+ close_unconvert_lead: DataLayerCurrencyValue & { unconvert_lead_reason?: string }
136
+ disqualify_lead: DataLayerCurrencyValue & { disqualified_lead_reason?: string }
137
+ generate_lead: DataLayerCurrencyValue & { lead_source?: string }
138
+ qualify_lead: DataLayerCurrencyValue
139
+ working_lead: DataLayerCurrencyValue & { lead_status?: string }
140
+
141
+ // Custom events
142
+ add_to_cart_error: {
143
+ userErrors?: string[]
144
+ errors?: string[]
145
+ variables?: object
146
+ }
147
+
148
+ // Core web vitals tracking.
149
+ [key: `cwv_${string}`]: {
150
+ value: Metric['delta']
151
+ debug_target?: string
152
+ } & {
153
+ [K in keyof Metric as K extends string ? `metric_${K}` : never]?: Metric[K]
154
+ }
155
+ }
package/api/sendEvent.ts CHANGED
@@ -1,12 +1,20 @@
1
- import { googleEventNames } from './googleEventNames'
1
+ import { GoogleEventTypes } from './googleEventNames'
2
2
 
3
- export type EventMapFunctionType = (
4
- eventName: (typeof googleEventNames)[number] | (string & Record<never, never>),
5
- eventData: {
6
- [key: string]: unknown
7
- },
3
+ export type SendEvent = (
4
+ eventName: Event | (string & Record<never, never>),
5
+ eventData: { [key: string]: unknown },
8
6
  ) => void
9
7
 
10
- export const sendEvent: EventMapFunctionType = (eventName, eventData) => {
8
+ export function sendEvent<Event extends keyof GoogleEventTypes>(
9
+ eventName: Event,
10
+ eventData: GoogleEventTypes[Event],
11
+ ) {
11
12
  // This is a generic event handler and is plugins from google-analytics and google datalayer
12
13
  }
14
+
15
+ export function useSendEvent() {
16
+ return <Event extends keyof GoogleEventTypes>(
17
+ eventName: Event,
18
+ eventData: GoogleEventTypes[Event],
19
+ ) => sendEvent<Event>(eventName, eventData)
20
+ }
@@ -1,12 +1,13 @@
1
- import { ProductItemsGridProps } from '@graphcommerce/magento-product'
1
+ import { ProductItemsGridProps, useProductFiltersPro } from '@graphcommerce/magento-product'
2
2
  import { useMemoObject } from '@graphcommerce/next-ui'
3
3
  import { useEventCallback } from '@mui/material'
4
- import React, { useContext, useEffect } from 'react'
5
- import { sendEvent } from '../api/sendEvent'
4
+ import React, { useContext, useEffect, useRef } from 'react'
5
+ import { useSendEvent } from '../api/sendEvent'
6
6
  import {
7
7
  productItemsToViewItemList,
8
8
  viewItemListToSelectItem,
9
9
  } from '../mapping/productItemsToViewItemList/productItemsToViewItemList'
10
+ import { useDebounce } from '@graphcommerce/react-hook-form'
10
11
 
11
12
  const DatalayerSelectItemContext = React.createContext<((itemId: string) => void) | undefined>(
12
13
  undefined,
@@ -25,13 +26,61 @@ export function useViewItemList() {
25
26
  export function DatalayerViewItemList(
26
27
  props: ProductItemsGridProps & { children: React.ReactNode },
27
28
  ) {
28
- const { title: item_list_name, items, children } = props
29
+ const { title: item_list_name, items, children, containerRef } = props
29
30
  const item_list_id = item_list_name.toLowerCase().replace(/\s/g, '_')
30
31
 
32
+ const params = useProductFiltersPro(true)?.params
33
+
34
+ const sendEvent = useSendEvent()
31
35
  const viewItemList = useMemoObject(
32
- productItemsToViewItemList(item_list_id, item_list_name, items),
36
+ productItemsToViewItemList(item_list_id, item_list_name, items, params),
33
37
  )
34
- useEffect(() => sendEvent('view_item_list', viewItemList), [viewItemList])
38
+
39
+ const sendUids = useRef<Map<string, boolean>>(new Map())
40
+
41
+ const send = useDebounce(() => {
42
+ const toSend = [...sendUids.current.entries()]
43
+ .filter(([, value]) => value !== true)
44
+ .map(([key]) => key)
45
+ if (toSend.length === 0) return
46
+
47
+ const viewItems = viewItemList.items.filter((item) => toSend.includes(item.item_uid))
48
+
49
+ sendEvent('view_item_list', { ...viewItemList, items: viewItems })
50
+
51
+ // Mark as send.
52
+ toSend.forEach((item_uid) => sendUids.current.set(item_uid, true))
53
+ }, 1000)
54
+
55
+ useEffect(() => {
56
+ sendUids.current.clear()
57
+ if (typeof containerRef === 'function' || !containerRef?.current) return () => {}
58
+
59
+ const childNodes = [...containerRef.current.childNodes].filter((n) => n instanceof HTMLElement)
60
+
61
+ const io = new IntersectionObserver(
62
+ (entries) => {
63
+ for (const entry of entries) {
64
+ const { target } = entry
65
+ if (entry.isIntersecting && target instanceof HTMLElement) {
66
+ const index = childNodes.indexOf(target)
67
+ const item = viewItemList.items[index]
68
+ if (item && !sendUids.current.has(item.item_uid)) {
69
+ sendUids.current.set(item.item_uid, false)
70
+ }
71
+ }
72
+ }
73
+ send()
74
+ },
75
+ { threshold: 0.5 },
76
+ )
77
+
78
+ childNodes.forEach((node) => {
79
+ io.observe(node)
80
+ })
81
+
82
+ return () => io.disconnect()
83
+ }, [containerRef, send, viewItemList])
35
84
 
36
85
  const selectItem = useEventCallback((itemId: string) => {
37
86
  sendEvent('select_item', viewItemListToSelectItem(viewItemList, itemId))
@@ -6,6 +6,7 @@ import { CartItem_DatalayerItemFragment } from './CartItem_DatalayerItem.gql'
6
6
 
7
7
  export function cartItemToDatalayerItem<P extends CartItem_DatalayerItemFragment>(
8
8
  item: P,
9
+ index: number,
9
10
  ): GoogleDatalayerItem {
10
11
  const discount = item.prices?.total_item_discount?.value
11
12
  ? item.prices.total_item_discount.value / item.quantity
@@ -14,14 +15,14 @@ export function cartItemToDatalayerItem<P extends CartItem_DatalayerItemFragment
14
15
  const price = (item?.prices?.price_including_tax?.value ?? 0) - discount
15
16
 
16
17
  return {
17
- ...productToDatalayerItem(item.product),
18
+ ...productToDatalayerItem(item.product, index),
18
19
  currency: item.prices?.price.currency as string,
19
20
  discount,
20
21
  price,
21
22
  quantity: item.quantity,
22
23
  item_variant:
23
24
  item.__typename === 'ConfigurableCartItem'
24
- ? item.configured_variant.sku ?? undefined
25
+ ? (item.configured_variant.sku ?? undefined)
25
26
  : undefined,
26
27
  }
27
28
  }
@@ -5,9 +5,6 @@ import { CartItem_RemoveFromCartFragment } from './CartItem_RemoveFromCart.gql'
5
5
  export const cartItemToRemoveFromCart = <C extends CartItem_RemoveFromCartFragment>(
6
6
  cartItem: C,
7
7
  ) => {
8
- const items = [cartItemToDatalayerItem(cartItem)]
9
- return {
10
- ...datalayerItemsToCurrencyValue(items),
11
- items,
12
- }
8
+ const items = [cartItemToDatalayerItem(cartItem, 0)]
9
+ return { ...datalayerItemsToCurrencyValue(items), items }
13
10
  }
@@ -1,10 +1,10 @@
1
1
  fragment Cart_AddPaymentInfo on Cart
2
2
  @inject(into: ["PaymentMethodContext", "PaymentMethodUpdated"]) {
3
+ ...Cart_ViewCart
3
4
  applied_coupons {
4
5
  code
5
6
  }
6
7
  selected_payment_method {
7
8
  code
8
9
  }
9
- ...Cart_DatalayerItems
10
10
  }
@@ -1,10 +1,17 @@
1
- import { cartToDatalayerItems } from '../cartToDatalayerItems/cartToDatalayerItems'
1
+ import { cartToViewCart, ViewCart } from '../cartToViewCart/cartToViewCart'
2
2
  import { Cart_AddPaymentInfoFragment } from './Cart_AddPaymentInfo.gql'
3
3
 
4
- export function cartToAddPaymentInfo<C extends Cart_AddPaymentInfoFragment>(cart: C) {
4
+ export type AddPaymentInfo = ViewCart & {
5
+ coupon?: string
6
+ payment_type?: string
7
+ }
8
+
9
+ export function cartToAddPaymentInfo<C extends Cart_AddPaymentInfoFragment>(
10
+ cart: C,
11
+ ): AddPaymentInfo {
5
12
  return {
13
+ ...cartToViewCart(cart),
6
14
  coupon: cart?.applied_coupons?.map((coupon) => coupon?.code).join(' '),
7
15
  payment_type: cart?.selected_payment_method?.code,
8
- ...cartToDatalayerItems(cart),
9
16
  }
10
17
  }
@@ -8,5 +8,5 @@ fragment Cart_AddShippingInfo on Cart @inject(into: ["ShippingMethodSelected"])
8
8
  method_code
9
9
  }
10
10
  }
11
- ...Cart_DatalayerItems
11
+ ...Cart_ViewCart
12
12
  }
@@ -1,15 +1,23 @@
1
- import { cartToDatalayerItems } from '../cartToDatalayerItems/cartToDatalayerItems'
1
+ import { filterNonNullableKeys } from '@graphcommerce/next-ui'
2
+ import { cartToViewCart, ViewCart } from '../cartToViewCart/cartToViewCart'
2
3
  import { Cart_AddShippingInfoFragment } from './Cart_AddShippingInfo.gql'
3
4
 
4
- export function cartToAddShippingInfo<C extends Cart_AddShippingInfoFragment>(cart: C) {
5
+ export type AddShippingInfo = ViewCart & {
6
+ coupon?: string
7
+ shipping_tier?: string
8
+ }
9
+
10
+ export function cartToAddShippingInfo<C extends Cart_AddShippingInfoFragment>(
11
+ cart: C,
12
+ ): AddShippingInfo {
5
13
  return {
6
14
  coupon: cart?.applied_coupons?.map((coupon) => coupon?.code).join(' '),
7
- shipping_tier: cart?.shipping_addresses
15
+ shipping_tier: filterNonNullableKeys(cart?.shipping_addresses, ['selected_shipping_method'])
8
16
  .map(
9
- (address) =>
10
- `${address?.selected_shipping_method?.carrier_code}_${address?.selected_shipping_method?.method_code}`,
17
+ ({ selected_shipping_method: { carrier_code, method_code } }) =>
18
+ `${carrier_code}_${method_code}`,
11
19
  )
12
20
  .join(' '),
13
- ...cartToDatalayerItems(cart),
21
+ ...cartToViewCart(cart),
14
22
  }
15
23
  }
@@ -1,6 +1,6 @@
1
1
  fragment Cart_BeginCheckout on Cart @inject(into: ["CartStartCheckout"]) {
2
+ ...Cart_ViewCart
2
3
  applied_coupons {
3
4
  code
4
5
  }
5
- ...Cart_DatalayerItems
6
6
  }
@@ -1,9 +1,13 @@
1
- import { cartToDatalayerItems } from '../cartToDatalayerItems/cartToDatalayerItems'
1
+ import { cartToViewCart, ViewCart } from '../cartToViewCart/cartToViewCart'
2
2
  import { Cart_BeginCheckoutFragment } from './Cart_BeginCheckout.gql'
3
3
 
4
- export function cartToBeginCheckout<C extends Cart_BeginCheckoutFragment>(cart: C) {
4
+ export type BeginCheckout = ViewCart & {
5
+ coupon?: string
6
+ }
7
+
8
+ export function cartToBeginCheckout<C extends Cart_BeginCheckoutFragment>(cart: C): BeginCheckout {
5
9
  return {
6
10
  coupon: cart?.applied_coupons?.map((coupon) => coupon?.code).join(' '),
7
- ...cartToDatalayerItems(cart),
11
+ ...cartToViewCart(cart),
8
12
  }
9
13
  }
@@ -0,0 +1,18 @@
1
+ fragment Cart_PurchaseEvent on Cart
2
+ @inject(into: ["PaymentMethodContext", "PaymentMethodUpdated"]) {
3
+ ...Cart_AddPaymentInfo
4
+ shipping_addresses {
5
+ selected_shipping_method {
6
+ price_excl_tax {
7
+ ...Money
8
+ }
9
+ }
10
+ }
11
+ prices {
12
+ applied_taxes {
13
+ amount {
14
+ ...Money
15
+ }
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,28 @@
1
+ import { cartToViewCart, ViewCart } from '../cartToViewCart/cartToViewCart'
2
+ import { Cart_PurchaseEventFragment } from './Cart_PurchaseEvent.gql'
3
+
4
+ export type PurchaseOrRefund = ViewCart & {
5
+ transaction_id: string
6
+ coupon?: string
7
+ shipping?: number
8
+ tax?: number
9
+ }
10
+
11
+ export function cartToPurchase<C extends Cart_PurchaseEventFragment>(
12
+ orderNumber: string,
13
+ cart: C | null | undefined,
14
+ ): PurchaseOrRefund {
15
+ // Although the fallback information is wrong, we find it to be more important to register a purchase even in the cart object is missing.
16
+ const base = cart ? cartToViewCart(cart) : { items: [], currency: '', value: 0 }
17
+
18
+ return {
19
+ transaction_id: orderNumber,
20
+ coupon: cart?.applied_coupons?.map((coupon) => coupon?.code).join(' '),
21
+ shipping: cart?.shipping_addresses.reduce(
22
+ (sum, address) => sum + (address?.selected_shipping_method?.price_excl_tax.value ?? 0),
23
+ 0,
24
+ ),
25
+ tax: cart?.prices?.applied_taxes?.reduce((sum, tax) => sum + (tax?.amount?.value ?? 0), 0),
26
+ ...base,
27
+ }
28
+ }