@graphcommerce/google-datalayer 8.1.0-canary.9 → 9.0.0-canary.101

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 +13 -10
  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 +4 -4
  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.101
4
+
5
+ ## 9.0.0-canary.100
6
+
7
+ ## 9.0.0-canary.99
8
+
9
+ ## 9.0.0-canary.98
10
+
11
+ ## 9.0.0-canary.97
12
+
13
+ ## 9.0.0-canary.96
14
+
15
+ ## 9.0.0-canary.95
16
+
17
+ ## 9.0.0-canary.94
18
+
19
+ ## 9.0.0-canary.93
20
+
21
+ ## 9.0.0-canary.92
22
+
23
+ ## 9.0.0-canary.91
24
+
25
+ ## 9.0.0-canary.90
26
+
27
+ ## 9.0.0-canary.89
28
+
29
+ ## 9.0.0-canary.88
30
+
31
+ ## 9.0.0-canary.87
32
+
33
+ ## 9.0.0-canary.86
34
+
35
+ ## 9.0.0-canary.85
36
+
37
+ ## 9.0.0-canary.84
38
+
39
+ ## 9.0.0-canary.83
40
+
41
+ ## 9.0.0-canary.82
42
+
43
+ ## 9.0.0-canary.81
44
+
45
+ ## 9.0.0-canary.80
46
+
47
+ ### Patch Changes
48
+
49
+ - [#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))
50
+
51
+ ## 9.0.0-canary.79
52
+
53
+ ## 9.0.0-canary.78
54
+
55
+ ## 9.0.0-canary.77
56
+
57
+ ## 9.0.0-canary.76
58
+
59
+ ## 9.0.0-canary.75
60
+
61
+ ## 9.0.0-canary.74
62
+
63
+ ## 9.0.0-canary.73
64
+
65
+ ### Minor Changes
66
+
67
+ - [#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))
68
+
69
+ ## 9.0.0-canary.72
70
+
71
+ ## 9.0.0-canary.71
72
+
73
+ ## 9.0.0-canary.70
74
+
75
+ ## 9.0.0-canary.69
76
+
77
+ ## 9.0.0-canary.68
78
+
79
+ ## 9.0.0-canary.67
80
+
81
+ ## 9.0.0-canary.66
82
+
83
+ ## 9.0.0-canary.65
84
+
85
+ ## 9.0.0-canary.64
86
+
87
+ ## 9.0.0-canary.63
88
+
89
+ ## 9.0.0-canary.62
90
+
91
+ ## 9.0.0-canary.61
92
+
93
+ ## 9.0.0-canary.60
94
+
95
+ ### Patch Changes
96
+
97
+ - [#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))
98
+
99
+ ## 9.0.0-canary.59
100
+
101
+ ## 9.0.0-canary.58
102
+
103
+ ## 9.0.0-canary.57
104
+
105
+ ## 9.0.0-canary.56
106
+
107
+ ## 9.0.0-canary.55
108
+
109
+ ## 9.0.0-canary.54
110
+
111
+ ## 8.1.0-canary.53
112
+
113
+ ## 8.1.0-canary.52
114
+
115
+ ## 8.1.0-canary.51
116
+
117
+ ## 8.1.0-canary.50
118
+
119
+ ## 8.1.0-canary.49
120
+
121
+ ## 8.1.0-canary.48
122
+
123
+ ## 8.1.0-canary.47
124
+
125
+ ## 8.1.0-canary.46
126
+
127
+ ## 8.1.0-canary.45
128
+
129
+ ## 8.1.0-canary.44
130
+
131
+ ## 8.1.0-canary.43
132
+
133
+ ## 8.1.0-canary.42
134
+
135
+ ## 8.1.0-canary.41
136
+
137
+ ## 8.1.0-canary.40
138
+
139
+ ## 8.1.0-canary.39
140
+
141
+ ## 8.1.0-canary.38
142
+
143
+ ## 8.1.0-canary.37
144
+
145
+ ## 8.1.0-canary.36
146
+
147
+ ## 8.1.0-canary.35
148
+
149
+ ## 8.1.0-canary.34
150
+
151
+ ## 8.1.0-canary.33
152
+
153
+ ## 8.1.0-canary.32
154
+
155
+ ## 8.1.0-canary.31
156
+
157
+ ## 8.1.0-canary.30
158
+
159
+ ## 8.1.0-canary.29
160
+
161
+ ## 8.1.0-canary.28
162
+
163
+ ## 8.1.0-canary.27
164
+
165
+ ## 8.1.0-canary.26
166
+
167
+ ## 8.1.0-canary.25
168
+
169
+ ## 8.1.0-canary.24
170
+
171
+ ## 8.1.0-canary.23
172
+
173
+ ## 8.1.0-canary.22
174
+
175
+ ## 8.1.0-canary.21
176
+
177
+ ## 8.1.0-canary.20
178
+
179
+ ### Patch Changes
180
+
181
+ - [#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))
182
+
183
+ ## 8.1.0-canary.19
184
+
185
+ ## 8.1.0-canary.18
186
+
187
+ ## 8.1.0-canary.17
188
+
189
+ ## 8.1.0-canary.16
190
+
191
+ ## 8.1.0-canary.15
192
+
193
+ ## 8.1.0-canary.14
194
+
195
+ ## 8.1.0-canary.13
196
+
197
+ ## 8.1.0-canary.12
198
+
199
+ ## 8.1.0-canary.11
200
+
201
+ ## 8.1.0-canary.10
202
+
3
203
  ## 8.1.0-canary.9
4
204
 
5
205
  ## 8.1.0-canary.8
@@ -18,8 +218,7 @@
18
218
 
19
219
  ### Patch Changes
20
220
 
21
- - [#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
22
- ([@FrankHarland](https://github.com/FrankHarland))
221
+ - [#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))
23
222
 
24
223
  ## 8.0.6-canary.1
25
224
 
@@ -34,8 +233,7 @@
34
233
  - 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.
35
234
  - Created cartItemToGoogleDatalayerItem and productToGoogleDatalayerItem for easier modifications. ([@paales](https://github.com/paales))
36
235
 
37
- - [#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.
38
- ([@paales](https://github.com/paales))
236
+ - [#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))
39
237
 
40
238
  ## 8.0.5-canary.10
41
239
 
@@ -64,8 +262,7 @@
64
262
  - 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.
65
263
  - Created cartItemToGoogleDatalayerItem and productToGoogleDatalayerItem for easier modifications. ([@paales](https://github.com/paales))
66
264
 
67
- - [#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.
68
- ([@paales](https://github.com/paales))
265
+ - [#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))
69
266
 
70
267
  ## 8.0.5-canary.0
71
268
 
@@ -73,12 +270,10 @@
73
270
 
74
271
  ### Patch Changes
75
272
 
76
- - [#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.
77
- ([@mikekeehnen](https://github.com/mikekeehnen))
273
+ - [#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))
78
274
 
79
275
  ## 8.0.4-canary.1
80
276
 
81
277
  ### Patch Changes
82
278
 
83
- - [#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.
84
- ([@mikekeehnen](https://github.com/mikekeehnen))
279
+ - [#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
+ }