From 1b409cb1990c105cbeb930a6029d8b2056dba7f0 Mon Sep 17 00:00:00 2001 From: indykoning <15870933+indykoning@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:10:00 +0100 Subject: [PATCH] Rapidez v3 support (#29) --- composer.json | 2 +- config/rapidez/gtm.php | 3 -- resources/js/datalayer/ga4.js | 43 ++++++++-------- resources/js/datalayer/google-ads.js | 16 +++--- resources/js/datalayer/ua.js | 75 ---------------------------- resources/js/gtm.js | 57 +++++++++------------ 6 files changed, 55 insertions(+), 141 deletions(-) delete mode 100644 resources/js/datalayer/ua.js diff --git a/composer.json b/composer.json index 06d4f4e..03c5d76 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "php": "^8.0|^8.1|^8.2", "illuminate/support": "^9.0|^10.0|^11.0", "illuminate/view": "^9.0|^10.0|^11.0", - "rapidez/core": "^2.0" + "rapidez/core": "^3.0" }, "autoload": { "psr-4": { diff --git a/config/rapidez/gtm.php b/config/rapidez/gtm.php index a4c2193..7155bc9 100644 --- a/config/rapidez/gtm.php +++ b/config/rapidez/gtm.php @@ -12,9 +12,6 @@ 'clear-on-load' => env('GTM_CLEAR_ON_LOAD', false), - 'send-ua-events' => env('GTM_SEND_UA_EVENTS', false), - 'send-ga4-events' => env('GTM_SEND_GA4_EVENTS', true), - 'partytown' => [ 'enabled' => env('GTM_PARTYTOWN_ENABLE', false), 'domain_whitelist' => array_merge( diff --git a/resources/js/datalayer/ga4.js b/resources/js/datalayer/ga4.js index c3b1386..fc37990 100644 --- a/resources/js/datalayer/ga4.js +++ b/resources/js/datalayer/ga4.js @@ -108,7 +108,7 @@ export const addToCart = async (data) => { event: 'add_to_cart', ecommerce: { currency: window.config.currency, - value: removeTrailingZeros(data.product.price), + value: removeTrailingZeros(data.product.price * data.qty), items: [{ item_id: data.product.sku, item_name: data.product.name, @@ -126,12 +126,12 @@ export const removeFromCart = async (item) => { event: 'remove_from_cart', ecommerce: { currency: window.config.currency, - value: removeTrailingZeros(item.price), + value: removeTrailingZeros(item?.prices?.row_total_including_tax?.value), items: [{ - item_id: item.sku, - item_name: item.name, - price: removeTrailingZeros(item.price), - quantity: item.qty, + item_id: item?.product?.sku, + item_name: item?.product?.name, + price: removeTrailingZeros(item?.prices?.price_including_tax?.value), + quantity: item?.quantity, }] } }) @@ -193,7 +193,7 @@ export const addShippingInfo = async () => { ecommerce: { currency: window.config.currency, value: removeTrailingZeros(window.app.cart?.prices?.grand_total?.value), - shipping_tier: window.app.checkout.shipping_method, + shipping_tier: window.app.cart?.shipping_addresses?.[0]?.selected_shipping_method?.method_code, items: Object.values(window.app.cart.items).map(function (item) { return { item_name: item?.product?.name, @@ -214,7 +214,7 @@ export const addPaymentInfo = async () => { ecommerce: { currency: window.config.currency, value: removeTrailingZeros(window.app.cart?.prices?.grand_total?.value), - payment_type: window.app.checkout.payment_method, + payment_type: window.app.cart?.selected_payment_method?.code, items: Object.values(window.app.cart.items).map(function (item) { return { item_name: item?.product?.name, @@ -233,21 +233,22 @@ export const purchase = async (order) => { dataLayer.push({ event: window.config.gtm['purchase-event-name'], ecommerce: { - currency: order.base_currency_code, - value: removeTrailingZeros(order.base_grand_total), - transaction_id: order.increment_id, - coupon: order.coupon_code, - shipping: removeTrailingZeros(order.base_shipping_amount), - tax: removeTrailingZeros(order.tax_amount), - items: order.sales_order_items.map((item, index) => { + currency: order?.total?.base_grand_total?.currency || window.config.currency, + value: removeTrailingZeros(order?.total?.base_grand_total?.value), + transaction_id: order?.number, + coupon: order?.total?.discounts?.find((discount) => discount?.coupon?.code)?.coupon?.code, + shipping: removeTrailingZeros(order?.total?.total_shipping?.value), + tax: removeTrailingZeros(order?.total?.total_tax?.value), + items: Object.values(order?.items || {}).map((item, index) => { return { - item_id: item.sku, - item_name: item.name, - discount: removeTrailingZeros(item.base_discount_amount), index: index, - price: removeTrailingZeros(item.base_price_incl_tax), - quantity: removeTrailingZeros(item.qty_ordered) - }; + item_id: item?.product?.sku, + item_name: item?.product?.name, + price: item?.product_sale_price?.value * item?.quantity_ordered, + coupon: item?.discounts?.find((discount) => discount?.coupon?.code)?.coupon?.code, + discount: item?.discounts?.reduce((discountAmount, discount) => discountAmount + (discount?.amount?.value || 0), 0), + quantity: item?.quantity_ordered + } }), } }) diff --git a/resources/js/datalayer/google-ads.js b/resources/js/datalayer/google-ads.js index f65ac2d..c7723f7 100644 --- a/resources/js/datalayer/google-ads.js +++ b/resources/js/datalayer/google-ads.js @@ -1,13 +1,13 @@ export const setUserData = async (userData = { - email: window.app.user?.email || window.app.guestEmail, - phone_number: window.app.checkout.billing_address.telephone.replaceAll(/[^0-9\+]*/g, ''), + email: window.app.user?.email || window.app.cart?.email || window.app.guestEmail, + phone_number: window.app.cart?.billing_address?.telephone?.replaceAll(/[^0-9\+]*/g, ''), address: { - first_name: window.app.checkout.billing_address.firstname, - last_name: window.app.checkout.billing_address.lastname, - street: window.app.checkout.billing_address.street.join("\n"), - city: window.app.checkout.billing_address.city, - postal_code: window.app.checkout.billing_address.postcode, - country: window.app.checkout.billing_address.country_id, + first_name: window.app.cart?.billing_address?.firstname, + last_name: window.app.cart?.billing_address?.lastname, + street: window.app.cart?.billing_address?.street?.join("\n"), + city: window.app.cart?.billing_address?.city, + postal_code: window.app.cart?.billing_address?.postcode, + country: window.app.cart?.billing_address?.country?.code, } }) => { // https://support.google.com/google-ads/answer/13262500 diff --git a/resources/js/datalayer/ua.js b/resources/js/datalayer/ua.js deleted file mode 100644 index 8a7b24b..0000000 --- a/resources/js/datalayer/ua.js +++ /dev/null @@ -1,75 +0,0 @@ -export const pageView = async (url) => { - window.dataLayer.push({ - event: 'pageView', - virtualUrl: url - }) -} - -export const productView = async (url) => { - dataLayer.push({ ecommerce: null }) - dataLayer.push({ - 'ecommerce': { - 'detail': { - 'products': [ - // See the GTMServiceProvider for the values - Object.fromEntries(Object.entries(config.gtm.productpage).map(([key, value]) => [key, eval(value)])) - ] - } - } - }) -} - -export const addToCart = async (data) => { - dataLayer.push({ ecommerce: null }) - dataLayer.push({ - 'event': 'addToCart', - 'ecommerce': { - 'currencyCode': window.config.currency, - 'add': { - 'products': [{ - 'name': data.product.name, - 'id': data.product.entity_id || data.product.id, - 'price': removeTrailingZeros(data.product.price), - 'quantity': data.qty, - }] - } - } - }) -} - -export const removeFromCart = async (item) => { - dataLayer.push({ ecommerce: null }) - dataLayer.push({ - 'event': 'removeFromCart', - 'ecommerce': { - 'remove': { - 'products': [{ - 'name': item.name, - 'id': item.product_id, - 'price': removeTrailingZeros(item.price), - 'quantity': item.qty, - }] - } - } - }) -} - -export const checkoutStep = async (step) => { - dataLayer.push({ ecommerce: null }) - dataLayer.push({ - 'event': 'checkout', - 'ecommerce': { - 'checkout': { - 'actionField': {'step': step}, - 'products': Object.values(window.app.cart.items).map(function (item) { - return { - name: item?.product?.name, - id: item?.product?.sku, - price: item?.prices?.price_including_tax?.value, - quantity: item?.quantity, - } - }), - } - } - }) -} diff --git a/resources/js/gtm.js b/resources/js/gtm.js index a459359..65b1926 100644 --- a/resources/js/gtm.js +++ b/resources/js/gtm.js @@ -1,3 +1,5 @@ +import * as ga4 from './datalayer/ga4.js'; + window.removeTrailingZeros = (price) => parseFloat(parseFloat(price).toString()); function getUserId() { @@ -32,28 +34,21 @@ function getSessionId() { return gsCookie.split('.')?.[2] } -let dataLayersPromise = (async () => { - // This async function is in order to work around "ERROR: Top-level await is not available" when building for older browsers. - window.dataLayers = { - ua: window.config.gtm['send-ua-events'] ? await import('./datalayer/ua.js') : undefined, - ga4: window.config.gtm['send-ga4-events'] ? await import('./datalayer/ga4.js') : undefined, - } -})() +window.dataLayers = { + ga4: ga4, +} -function sendDataLayer(func, ...args) { +/** @deprecated Call the respective function on the ga4 or window.ga4 object instead. */ +window.sendDataLayer = function (func, ...args) { if (!('dataLayer' in window)) { return } - ['ua', 'ga4'].forEach(layer => { - if(dataLayers?.[layer]?.[func]) { - dataLayers?.[layer]?.[func](...args); - } - }) + if(dataLayers?.ga4?.[func]) { + dataLayers?.ga4?.[func](...args); + } } -window.sendDataLayer = sendDataLayer; - document.addEventListener('turbo:load', async (event) => { if (!('dataLayer' in window)) { return; @@ -61,53 +56,49 @@ document.addEventListener('turbo:load', async (event) => { if (window.config.gtm['clear-on-load']) { window.dataLayer = [] } - await dataLayersPromise let url = new URL(event.detail.url); - sendDataLayer('pageView', event.detail.url); + ga4.pageView(event.detail.url); if (window.config.product) { - sendDataLayer('productView', event.detail.url); + ga4.productView() } if (url.pathname === '/search' && url.searchParams.has('q')) { - sendDataLayer('search', url.searchParams.get('q')); + ga4.search(url.searchParams.get('q')) } if (url.pathname === '/cart') { - sendDataLayer('viewCart'); + ga4.viewCart(); + } + + if (url.pathname === '/checkout') { + ga4.beginCheckout() } window.app.$on('logged-in', () => { - sendDataLayer('login'); + ga4.login() }) window.app.$on('cart-add', (data) => { - sendDataLayer('addToCart', data); + ga4.addToCart(data) }) window.app.$on('cart-remove', (item) => { - sendDataLayer('removeFromCart', item); - }) - - window.app.$on('checkout-step', (step) => { - sendDataLayer('checkoutStep', step); - if (step === 1) { - sendDataLayer('beginCheckout'); - } + ga4.removeFromCart(item) }) window.app.$on('checkout-credentials-saved', () => { - sendDataLayer('addShippingInfo'); + ga4.addShippingInfo() }) window.app.$on('checkout-payment-saved', () => { - sendDataLayer('addPaymentInfo'); + ga4.addPaymentInfo() }) window.app.$on('checkout-success', (order) => { - sendDataLayer('purchase', order); + ga4.purchase(order) }) if (window.config.gtm['elgentos-serverside']) {