Created
November 10, 2022 17:23
-
-
Save elephantsneverforget/77ffd7ba708719ee8958ac4ddd73ce70 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Google Tag Manager | |
* @returns null | |
*/ | |
import { | |
ClientAnalytics, | |
flattenConnection, | |
loadScript, | |
useCart, | |
useUrl, | |
} from '@shopify/hydrogen'; | |
import Analytics from 'analytics'; | |
import googleTagManager from '@analytics/google-tag-manager'; | |
import {doNotTrackEnabled} from 'analytics-plugin-do-not-track'; | |
import {useEffect} from 'react'; | |
import {v4 as uuidv4} from 'uuid'; | |
import {getIdFromGid} from 'Js/lib/gid'; | |
import Cookies from 'js-cookie'; | |
import {getCookie, getCookiFromPartial} from 'Js/lib/cookies'; | |
import dynamicYield from 'Js/lib/dynamic-yield-tracking.js'; | |
let init = false; | |
let prevUrl: string | null = null; | |
let elevarScriptInit = false; | |
export default function GTM({containerId}: {containerId: string}) { | |
const url = useUrl(); | |
const {cost, lines, attributes} = useCart(); | |
useEffect(() => { | |
if (!elevarScriptInit) { | |
elevarScriptInit = true; | |
loadScript( | |
'https://shopify-gtm-suite.getelevar.com/shops/6c9391c966ab7077151d58ba302717bdf46ebca6/events.js', | |
).then((loaded: boolean) => { | |
if (loaded && window.ElevarGtmSuiteListener) { | |
window.ElevarGtmSuiteListener.handlers.listen({ | |
ssUrl: 'https://4ieigxsa2ffkf7izglms.skims.com', | |
}); | |
} | |
}); | |
} | |
}, []); | |
// Sets session store with utm params | |
useEffect(() => { | |
if ( | |
url.search.includes('utm_') || | |
url.search.includes('fbclid') || | |
url.search.includes('gclid') || | |
url.search.includes('ttclid') || | |
url.search.includes('irclid') | |
) { | |
url.searchParams.forEach((value: string, key: string) => { | |
if (!key || !value) return; | |
const prevItem = window.sessionStorage.getItem(key); | |
if (prevItem && prevItem === value) return; | |
window.sessionStorage.setItem(key, value); | |
}); | |
} | |
if (url.search.includes('clientId')) { | |
const attentiveClientId = url.searchParams.get('clientId'); | |
if (attentiveClientId) { | |
Cookies.set('__attentive_client_user_id', attentiveClientId, { | |
expires: 30, | |
secure: true, | |
}); | |
} | |
} | |
}, [url]); | |
useEffect(() => { | |
if (!init) { | |
init = true; | |
const dontTrack = doNotTrackEnabled(); | |
const analytics = Analytics({ | |
app: 'skims-hydrogen', | |
plugins: [ | |
googleTagManager({ | |
containerId, | |
enabled: !dontTrack, | |
}), | |
], | |
}); | |
// Listen for events from Hydrogen | |
ClientAnalytics.subscribe( | |
ClientAnalytics.eventNames.PAGE_VIEW, | |
(payload) => { | |
const device = parseDeviceInfo(); | |
const {shopify, normalizedRscUrl: payloadUrl} = payload; | |
const { | |
customer, | |
customerCurrencyCode = 'USD', | |
userId, | |
} = shopify || {}; | |
const userProperties = parseCustomerData(customer, userId); | |
const {cartTotal, items} = parseCartData(cost, lines, attributes); | |
const marketing = parseMarketingInfo(customer, userId); | |
// Ignore sub page loads | |
if (prevUrl === payloadUrl) return; | |
prevUrl = payloadUrl; | |
console.log('PAGE_VIEW'); | |
analytics.page({event: 'dl_route_change'}); | |
analytics.page({ | |
event: 'dl_user_data', | |
device, | |
page: { | |
title: '', | |
}, | |
event_id: uuidv4(), | |
event_time: new Date().toISOString(), | |
cart_total: cartTotal, | |
user_properties: userProperties, | |
marketing, | |
ecommerce: { | |
cart_contents: { | |
products: items, | |
}, | |
currencyCode: customerCurrencyCode, | |
}, | |
}); | |
}, | |
); | |
ClientAnalytics.subscribe('VIEW_PLP', (payload) => { | |
const {shopify} = payload; | |
const { | |
customer, | |
productListResults, | |
customerCurrencyCode = 'USD', | |
userId, | |
} = shopify || {}; | |
const userProperties = parseCustomerData(customer, userId); | |
const plpProducts = productListResults.map( | |
(product: any, idx: number) => { | |
const productId = getIdFromGid(product.id, 'Product'); | |
const [variant] = flattenConnection(product.variants) as any; | |
const {priceV2, compareAtPriceV2, image, title, id} = | |
variant || ({} as any); | |
const variantId = getIdFromGid(id, 'ProductVariant'); | |
return { | |
id: variant.sku || productId, | |
name: title, | |
brand: product?.vendor || '', | |
category: product?.productType || '', | |
variant: title, | |
price: priceV2?.amount || '', | |
quantity: 1, | |
product_id: productId, | |
position: idx, | |
variant_id: variantId, | |
compare_at_price: compareAtPriceV2?.amount || '', | |
image: image?.url || '', | |
inventory: '', | |
}; | |
}, | |
); | |
const marketing = parseMarketingInfo(customer, userId); | |
console.log('VIEW_PLP'); | |
analytics.page({ | |
event: 'dl_view_item_list', | |
event_id: uuidv4(), | |
event_time: new Date().toISOString(), | |
user_properties: userProperties, | |
marketing, | |
ecommerce: { | |
currencyCode: customerCurrencyCode, | |
impressions: plpProducts, | |
}, | |
}); | |
}); | |
ClientAnalytics.subscribe('VIEW_SEACH_RESULTS', (payload) => { | |
const {shopify} = payload; | |
const { | |
customer, | |
customerCurrencyCode = 'USD', | |
productListResults, | |
userId, | |
} = shopify || {}; | |
const userProperties = parseCustomerData(customer, userId); | |
const searchProducts = productListResults | |
.filter((product: any) => product) | |
.map((product: any, idx: number) => { | |
const productId = getIdFromGid(product.id, 'Product'); | |
const [variant] = flattenConnection(product.variants) as any; | |
const {priceV2, compareAtPriceV2, image, title, id} = | |
variant || ({} as any); | |
const variantId = getIdFromGid(id, 'ProductVariant'); | |
return { | |
id: variant.sku || productId, | |
name: title, | |
brand: product?.vendor || '', | |
category: product?.productType || '', | |
variant: title, | |
price: priceV2?.amount || '', | |
quantity: 1, | |
product_id: productId, | |
position: idx, | |
variant_id: variantId, | |
compare_at_price: compareAtPriceV2?.amount || '', | |
image: image?.url || '', | |
inventory: '', | |
}; | |
}); | |
const marketing = parseMarketingInfo(customer, userId); | |
console.log('VIEW_SEACH_RESULTS'); | |
analytics.page({ | |
event: 'dl_view_search_results', | |
event_id: uuidv4(), | |
event_time: new Date().toISOString(), | |
user_properties: userProperties, | |
marketing, | |
ecommerce: { | |
currencyCode: customerCurrencyCode, | |
actionField: {list: 'search results'}, | |
impressions: searchProducts, | |
}, | |
}); | |
}); | |
ClientAnalytics.subscribe('SELECT_ITEM', (payload) => { | |
const {shopify} = payload; | |
const { | |
customer, | |
customerCurrencyCode = 'USD', | |
selectedProduct, | |
userId, | |
} = shopify || {}; | |
const userProperties = parseCustomerData(customer, userId); | |
const productId = selectedProduct.id.toString(); | |
const [variant] = selectedProduct.variants as any; | |
const {price, compareAtPrice, title, id} = variant || ({} as any); | |
const variantId = id.toString(); | |
const productData = { | |
id: variant.sku || productId, | |
name: selectedProduct.title, | |
brand: selectedProduct?.vendor || '', | |
category: selectedProduct?.productType || '', | |
variant: title, | |
price: price?.amount || '', | |
quantity: 1, | |
product_id: productId, | |
variant_id: variantId, | |
compare_at_price: compareAtPrice?.amount || price?.amount || '', | |
image: selectedProduct.previewImageUrl || '', | |
inventory: '', | |
}; | |
const marketing = parseMarketingInfo(customer, userId); | |
console.log('SELECT_ITEM'); | |
analytics.page({ | |
event: 'dl_select_item', | |
event_id: uuidv4(), | |
event_time: new Date().toISOString(), | |
user_properties: userProperties, | |
marketing, | |
ecommerce: { | |
currencyCode: customerCurrencyCode, | |
click: { | |
actionField: { | |
list: window?.location?.pathname || '', | |
action: 'click', | |
}, | |
products: [productData], | |
}, | |
}, | |
}); | |
}); | |
ClientAnalytics.subscribe( | |
ClientAnalytics.eventNames.VIEWED_PRODUCT, | |
(payload) => { | |
const device = parseDeviceInfo(); | |
const {shopify, referrer} = payload; | |
const { | |
customer, | |
customerCurrencyCode = 'USD', | |
products: [product], | |
userId, | |
} = shopify || {}; | |
const referrerUrl = referrer ? new URL(referrer) : {pathname: null}; | |
const userProperties = parseCustomerData(customer, userId); | |
const productId = getIdFromGid(product.id, 'Product'); | |
const variantId = getIdFromGid(product.variantId, 'ProductVariant'); | |
const productData = { | |
id: product.sku || productId, | |
name: product?.name || '', | |
brand: product?.brand || '', | |
category: product?.category || '', | |
variant: product.variant, | |
price: product?.price || '', | |
quantity: 1, | |
product_id: productId, | |
variant_id: variantId, | |
compare_at_price: product?.compareAtPrice || '', | |
image: product?.image?.url || '', | |
inventory: '', | |
}; | |
const marketing = parseMarketingInfo(customer, userId); | |
console.log('VIEWED_PRODUCT'); | |
analytics.page({ | |
event: 'dl_view_item', | |
device, | |
event_id: uuidv4(), | |
event_time: new Date().toISOString(), | |
user_properties: userProperties, | |
marketing, | |
ecommerce: { | |
currencyCode: customerCurrencyCode, | |
detail: { | |
actionField: { | |
list: referrerUrl?.pathname || referrer || '', | |
action: 'detail', | |
}, | |
products: [productData], | |
}, | |
}, | |
}); | |
}, | |
); | |
ClientAnalytics.subscribe( | |
ClientAnalytics.eventNames.ADD_TO_CART, | |
(payload) => { | |
const {addedCartLines, cart = {}, shopify, referrer} = payload; | |
const { | |
customer, | |
customerCurrencyCode = 'USD', | |
userId, | |
} = shopify || {}; | |
const referrerUrl = referrer ? new URL(referrer) : {pathname: null}; | |
const userProperties = parseCustomerData(customer, userId); | |
const payloadCartLines = flattenConnection(cart.lines); | |
const {items} = parseCartData( | |
cart.cost, | |
payloadCartLines, | |
cart.attributes, | |
); | |
const merchandiseIds = addedCartLines.map( | |
(item: any) => item.merchandiseId, | |
); | |
const addedProducts = items.filter((item: any) => { | |
const hasJustAdded = merchandiseIds.includes(item.merchandiseId); | |
if (hasJustAdded) { | |
delete item.merchandiseId; | |
delete item.position; | |
return item; | |
} | |
return false; | |
}); | |
const marketing = parseMarketingInfo(customer, userId); | |
dynamicYield.addItem(addedProducts[0], items, customerCurrencyCode); | |
console.log('ADD_TO_CART'); | |
analytics.page({ | |
event: 'dl_add_to_cart', | |
user_properties: userProperties, | |
event_id: uuidv4(), | |
event_time: new Date().toISOString(), | |
marketing, | |
ecommerce: { | |
currencyCode: customerCurrencyCode, | |
add: { | |
actionField: { | |
list: referrerUrl?.pathname || referrer || '', | |
}, | |
products: addedProducts, // Array of added products | |
}, | |
}, | |
}); | |
}, | |
); | |
ClientAnalytics.subscribe('REMOVE_FROM_CART_CUSTOM', (payload) => { | |
const {cart = {}, shopify, referrer, removedCartItems} = payload; | |
const {customer, customerCurrencyCode = 'USD', userId} = shopify || {}; | |
const referrerUrl = referrer ? new URL(referrer) : {pathname: null}; | |
const userProperties = parseCustomerData(customer, userId); | |
const {items} = parseCartData( | |
cart.cost, | |
removedCartItems, | |
cart.attributes, | |
); | |
const removedProducts = items.map((item: any) => { | |
delete item.merchandiseId; | |
delete item.position; | |
return item; | |
}); | |
const marketing = parseMarketingInfo(customer, userId); | |
dynamicYield.removeItem( | |
removedCartItems[0], | |
items, | |
customerCurrencyCode, | |
); | |
console.log('REMOVE_FROM_CART'); | |
analytics.page({ | |
event: 'dl_add_to_cart', | |
user_properties: userProperties, | |
event_id: uuidv4(), | |
event_time: new Date().toISOString(), | |
marketing, | |
ecommerce: { | |
currencyCode: customerCurrencyCode, | |
remove: { | |
actionField: { | |
list: referrerUrl?.pathname || referrer || '', | |
action: 'remove', | |
}, | |
products: removedProducts, // Array of removed products | |
}, | |
}, | |
}); | |
}); | |
ClientAnalytics.subscribe('VIEW_CART_CUSTOM', (payload) => { | |
const {shopify} = payload; | |
const { | |
cart = {}, | |
customer, | |
customerCurrencyCode = 'USD', | |
userId, | |
} = shopify || {}; | |
const userProperties = parseCustomerData(customer, userId); | |
const {cartTotal, items} = parseCartData( | |
cart.cost, | |
cart.lines, | |
cart.attributes, | |
); | |
const sanitizedCartItems = items.map((item: any) => { | |
delete item.merchandiseId; | |
return item; | |
}); | |
const marketing = parseMarketingInfo(customer, userId); | |
console.log('VIEW_CART'); | |
analytics.page({ | |
event: 'dl_view_cart', | |
user_properties: userProperties, | |
event_id: uuidv4(), | |
event_time: new Date().toISOString(), | |
cart_total: cartTotal, | |
marketing, | |
ecommerce: { | |
currencyCode: customerCurrencyCode, | |
actionField: { | |
list: 'Shopping Cart', | |
}, | |
impressions: sanitizedCartItems, // Array of cart products | |
}, | |
}); | |
}); | |
ClientAnalytics.subscribe('ACCOUNT_LOGIN', (payload) => { | |
const {shopify} = payload; | |
const {customer, userId} = shopify || {}; | |
const userProperties = parseCustomerData(customer, userId); | |
const marketing = parseMarketingInfo(customer, userId); | |
dynamicYield.login(customer.email); | |
console.log('ACCOUNT_LOGIN'); | |
analytics.page({ | |
event: 'dl_login', | |
page: {title: 'Login'}, | |
user_properties: userProperties, | |
event_id: uuidv4(), | |
event_time: new Date().toISOString(), | |
marketing, | |
}); | |
}); | |
} // Init wrapper end | |
}, [containerId, attributes, cost, lines]); | |
function parseCustomerData(data: any, uuid: string) { | |
const [address] = flattenConnection(data?.addresses || []) as any; | |
const orders = flattenConnection(data?.orders || []); | |
const customerId = data?.id ? getIdFromGid(data.id, 'Customer') : ''; | |
return { | |
customer_address_1: address?.address1 || '', | |
customer_address_2: address?.address2 || '', | |
customer_city: address?.city || '', | |
customer_country: address?.country || '', | |
customer_email: data?.email || '', | |
customer_first_name: data?.firstName || '', | |
customer_id: customerId || '', | |
customer_last_name: data?.lastName || '', | |
customer_order_count: orders?.length.toString() || '0', | |
customer_phone: data?.phone || '', | |
customer_province: address?.province || '', | |
customer_province_code: address?.provinceCode || '', | |
customer_tags: data?.tags?.join(', '), | |
customer_total_spent: | |
orders | |
?.reduce( | |
(prev: number, curr: any) => | |
prev + parseFloat(curr.currentTotalPrice.amount || 0), | |
0, | |
) | |
.toString() || '0', | |
customer_zip: address?.zip || '', | |
user_consent: '', | |
visitor_type: customerId ? 'logged_in' : 'guest', | |
user_id: customerId || uuid, | |
}; | |
} | |
function parseDeviceInfo() { | |
if (!window) return {}; | |
const deviceInfo = { | |
screen_resolution: `${window.screen.width * window.devicePixelRatio}x${ | |
window.screen.height * window.devicePixelRatio | |
}`, | |
viewport_size: `${window.screen.width}x${window.screen.height}`, | |
encoding: document.characterSet || 'UTF-8', | |
language: window.navigator.language, | |
colors: screen.colorDepth, | |
}; | |
return deviceInfo; | |
} | |
function parseMarketingInfo(customer: any, uuid: string) { | |
if (!window) return {}; | |
const urlParams = new URLSearchParams(window.location.search); | |
const utmParams: any = {}; | |
urlParams.forEach((value: string, key: string) => { | |
if (key.includes('utm_')) { | |
utmParams[key] = value; | |
} | |
}); | |
const gaId = getCookiFromPartial('_ga_'); | |
const data = { | |
_fbp: getCookie('_fbp') || '', | |
_fbc: getCookie('_fbc') || '', | |
_ga: getCookie('_ga') || '', | |
_gaexp: getCookie('_gaexp') || '', | |
_gid: getCookie('_gid') || '', | |
__utma: getCookie('__utma') || '', | |
ttclid: getCookie('ttclid') || '', | |
crto_mapped_user_id: getCookie('crto_mapped_user_id') || '', | |
crto_is_user_optout: getCookie('crto_is_user_optout') || '', | |
user_id: customer?.id ? getIdFromGid(customer.id, 'Customer') : uuid, | |
...utmParams, | |
}; | |
if (gaId) { | |
data[gaId.name] = gaId.value; | |
} | |
return data; | |
} | |
function parseCartData(cartCost: any, cartLines: any[], cartAttributes: any) { | |
return { | |
attributes: cartAttributes || [], | |
cartTotal: cartCost?.totalAmount?.amount || '0', | |
currencyCode: cartCost?.totalAmount?.currencyCode || 'USD', | |
items: cartLines.map((item, idx: number) => { | |
const {merchandise, quantity} = item as any; | |
const {product, title, priceV2, compareAtPriceV2, image, id} = | |
merchandise; | |
const productId = getIdFromGid(product.id, 'Product'); | |
const variantId = getIdFromGid(id, 'ProductVariant'); | |
return { | |
merchandiseId: id, | |
id: merchandise.sku || productId, | |
name: product?.title || '', | |
brand: product?.vendor || '', | |
category: product?.productType || '', | |
variant: title, | |
price: priceV2?.amount || '', | |
position: idx, | |
quantity, | |
product_id: productId, | |
variant_id: variantId, | |
compare_at_price: compareAtPriceV2?.amount || '', | |
image: image?.url || '', | |
inventory: '', | |
}; | |
}), | |
}; | |
} | |
return null; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment