initial commit
This commit is contained in:
@ -0,0 +1,2 @@
|
||||
export const namespace = 'woocommerce-google-analytics';
|
||||
export const actionPrefix = 'experimental__woocommerce_blocks';
|
@ -0,0 +1,304 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { addAction } from '@wordpress/hooks';
|
||||
import type {
|
||||
ProductResponseItem,
|
||||
CartResponseItem,
|
||||
StoreCart,
|
||||
} from '@woocommerce/types';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { namespace, actionPrefix } from './constants';
|
||||
import {
|
||||
getProductFieldObject,
|
||||
getProductImpressionObject,
|
||||
trackEvent,
|
||||
trackCheckoutStep,
|
||||
trackCheckoutOption,
|
||||
} from './utils';
|
||||
|
||||
/**
|
||||
* Track customer progress through steps of the checkout. Triggers the event when the step changes:
|
||||
* 1 - Contact information
|
||||
* 2 - Shipping address
|
||||
* 3 - Billing address
|
||||
* 4 - Shipping options
|
||||
* 5 - Payment options
|
||||
*
|
||||
* @summary Track checkout progress with begin_checkout and checkout_progress
|
||||
* @see https://developers.google.com/analytics/devguides/collection/gtagjs/enhanced-ecommerce#1_measure_checkout_steps
|
||||
*/
|
||||
addAction(
|
||||
`${ actionPrefix }-checkout-render-checkout-form`,
|
||||
namespace,
|
||||
trackCheckoutStep( 0 )
|
||||
);
|
||||
addAction(
|
||||
`${ actionPrefix }-checkout-set-email-address`,
|
||||
namespace,
|
||||
trackCheckoutStep( 1 )
|
||||
);
|
||||
addAction(
|
||||
`${ actionPrefix }-checkout-set-shipping-address`,
|
||||
namespace,
|
||||
trackCheckoutStep( 2 )
|
||||
);
|
||||
addAction(
|
||||
`${ actionPrefix }-checkout-set-billing-address`,
|
||||
namespace,
|
||||
trackCheckoutStep( 3 )
|
||||
);
|
||||
addAction(
|
||||
`${ actionPrefix }-checkout-set-phone-number`,
|
||||
namespace,
|
||||
( { step, ...rest }: { step: string; storeCart: StoreCart } ): void => {
|
||||
trackCheckoutStep( step === 'shipping' ? 2 : 3 )( rest );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Choose a shipping rate
|
||||
*
|
||||
* @summary Track the shipping rate being set using set_checkout_option
|
||||
* @see https://developers.google.com/analytics/devguides/collection/gtagjs/enhanced-ecommerce#2_measure_checkout_options
|
||||
*/
|
||||
addAction(
|
||||
`${ actionPrefix }-checkout-set-selected-shipping-rate`,
|
||||
namespace,
|
||||
( { shippingRateId }: { shippingRateId: string } ): void => {
|
||||
trackCheckoutOption( {
|
||||
step: 4,
|
||||
option: __( 'Shipping Method', 'woo-gutenberg-products-block' ),
|
||||
value: shippingRateId,
|
||||
} )();
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Choose a payment method
|
||||
*
|
||||
* @summary Track the payment method being set using set_checkout_option
|
||||
* @see https://developers.google.com/analytics/devguides/collection/gtagjs/enhanced-ecommerce#2_measure_checkout_options
|
||||
*/
|
||||
addAction(
|
||||
`${ actionPrefix }-checkout-set-active-payment-method`,
|
||||
namespace,
|
||||
( { paymentMethodSlug }: { paymentMethodSlug: string } ): void => {
|
||||
trackCheckoutOption( {
|
||||
step: 5,
|
||||
option: __( 'Payment Method', 'woo-gutenberg-products-block' ),
|
||||
value: paymentMethodSlug,
|
||||
} )();
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Add Payment Information
|
||||
*
|
||||
* This event signifies a user has submitted their payment information. Note, this is used to indicate checkout
|
||||
* submission, not `purchase` which is triggered on the thanks page.
|
||||
*
|
||||
* @summary Track the add_payment_info event
|
||||
* @see https://developers.google.com/gtagjs/reference/ga4-events#add_payment_info
|
||||
*/
|
||||
addAction( `${ actionPrefix }-checkout-submit`, namespace, (): void => {
|
||||
trackEvent( 'add_payment_info' );
|
||||
} );
|
||||
|
||||
/**
|
||||
* Add to cart.
|
||||
*
|
||||
* This event signifies that an item was added to a cart for purchase.
|
||||
*
|
||||
* @summary Track the add_to_cart event
|
||||
* @see https://developers.google.com/gtagjs/reference/ga4-events#add_to_cart
|
||||
*/
|
||||
addAction(
|
||||
`${ actionPrefix }-cart-add-item`,
|
||||
namespace,
|
||||
( {
|
||||
product,
|
||||
quantity = 1,
|
||||
}: {
|
||||
product: ProductResponseItem;
|
||||
quantity: number;
|
||||
} ): void => {
|
||||
trackEvent( 'add_to_cart', {
|
||||
event_category: 'ecommerce',
|
||||
event_label: __( 'Add to Cart', 'woo-gutenberg-products-block' ),
|
||||
items: [ getProductFieldObject( product, quantity ) ],
|
||||
} );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Remove item from the cart
|
||||
*
|
||||
* @summary Track the remove_from_cart event
|
||||
* @see https://developers.google.com/gtagjs/reference/ga4-events#remove_from_cart
|
||||
*/
|
||||
addAction(
|
||||
`${ actionPrefix }-cart-remove-item`,
|
||||
namespace,
|
||||
( {
|
||||
product,
|
||||
quantity = 1,
|
||||
}: {
|
||||
product: CartResponseItem;
|
||||
quantity: number;
|
||||
} ): void => {
|
||||
trackEvent( 'remove_from_cart', {
|
||||
event_category: 'ecommerce',
|
||||
event_label: __(
|
||||
'Remove Cart Item',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
items: [ getProductFieldObject( product, quantity ) ],
|
||||
} );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Change cart item quantities
|
||||
*
|
||||
* @summary Custom change_cart_quantity event.
|
||||
*/
|
||||
addAction(
|
||||
`${ actionPrefix }-cart-set-item-quantity`,
|
||||
namespace,
|
||||
( {
|
||||
product,
|
||||
quantity = 1,
|
||||
}: {
|
||||
product: CartResponseItem;
|
||||
quantity: number;
|
||||
} ): void => {
|
||||
trackEvent( 'change_cart_quantity', {
|
||||
event_category: 'ecommerce',
|
||||
event_label: __(
|
||||
'Change Cart Item Quantity',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
items: [ getProductFieldObject( product, quantity ) ],
|
||||
} );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Product List View
|
||||
*
|
||||
* @summary Track the view_item_list event
|
||||
* @see https://developers.google.com/gtagjs/reference/ga4-events#view_item_list
|
||||
*/
|
||||
addAction(
|
||||
`${ actionPrefix }-product-list-render`,
|
||||
namespace,
|
||||
( {
|
||||
products,
|
||||
listName = __( 'Product List', 'woo-gutenberg-products-block' ),
|
||||
}: {
|
||||
products: Array< ProductResponseItem >;
|
||||
listName: string;
|
||||
} ): void => {
|
||||
if ( products.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
trackEvent( 'view_item_list', {
|
||||
event_category: 'engagement',
|
||||
event_label: __(
|
||||
'Viewing products',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
items: products.map( ( product, index ) => ( {
|
||||
...getProductImpressionObject( product, listName ),
|
||||
list_position: index + 1,
|
||||
} ) ),
|
||||
} );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Product View Link Clicked
|
||||
*
|
||||
* @summary Track the select_content event
|
||||
* @see https://developers.google.com/gtagjs/reference/ga4-events#select_content
|
||||
*/
|
||||
addAction(
|
||||
`${ actionPrefix }-product-view-link`,
|
||||
namespace,
|
||||
( {
|
||||
product,
|
||||
listName,
|
||||
}: {
|
||||
product: ProductResponseItem;
|
||||
listName: string;
|
||||
} ): void => {
|
||||
trackEvent( 'select_content', {
|
||||
content_type: 'product',
|
||||
items: [ getProductImpressionObject( product, listName ) ],
|
||||
} );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Product Search
|
||||
*
|
||||
* @summary Track the search event
|
||||
* @see https://developers.google.com/gtagjs/reference/ga4-events#search
|
||||
*/
|
||||
addAction(
|
||||
`${ actionPrefix }-product-search`,
|
||||
namespace,
|
||||
( { searchTerm }: { searchTerm: string } ): void => {
|
||||
trackEvent( 'search', {
|
||||
search_term: searchTerm,
|
||||
} );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Single Product View
|
||||
*
|
||||
* @summary Track the view_item event
|
||||
* @see https://developers.google.com/gtagjs/reference/ga4-events#view_item
|
||||
*/
|
||||
addAction(
|
||||
`${ actionPrefix }-product-render`,
|
||||
namespace,
|
||||
( {
|
||||
product,
|
||||
listName,
|
||||
}: {
|
||||
product: ProductResponseItem;
|
||||
listName: string;
|
||||
} ): void => {
|
||||
if ( product ) {
|
||||
trackEvent( 'view_item', {
|
||||
items: [ getProductImpressionObject( product, listName ) ],
|
||||
} );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Track notices as Exception events.
|
||||
*
|
||||
* @summary Track the exception event
|
||||
* @see https://developers.google.com/analytics/devguides/collection/gtagjs/exceptions
|
||||
*/
|
||||
addAction(
|
||||
`${ actionPrefix }-store-notice-create`,
|
||||
namespace,
|
||||
( { status, content }: { status: string; content: string } ): void => {
|
||||
if ( status === 'error' ) {
|
||||
trackEvent( 'exception', {
|
||||
description: content,
|
||||
fatal: false,
|
||||
} );
|
||||
}
|
||||
}
|
||||
);
|
@ -0,0 +1,118 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import type {
|
||||
ProductResponseItem,
|
||||
CartResponseItem,
|
||||
StoreCart,
|
||||
} from '@woocommerce/types';
|
||||
|
||||
interface ImpressionItem extends Gtag.Item {
|
||||
list_name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats data into the productFieldObject shape.
|
||||
*
|
||||
* @see https://developers.google.com/analytics/devguides/collection/gtagjs/enhanced-ecommerce#product-data
|
||||
*/
|
||||
export const getProductFieldObject = (
|
||||
product: ProductResponseItem | CartResponseItem,
|
||||
quantity: number | undefined
|
||||
): Gtag.Item => {
|
||||
const productIdentifier = product.sku ? product.sku : '#' + product.id;
|
||||
const productCategory =
|
||||
'categories' in product && product.categories.length
|
||||
? product.categories[ 0 ].name
|
||||
: '';
|
||||
return {
|
||||
id: productIdentifier,
|
||||
name: product.name,
|
||||
quantity,
|
||||
category: productCategory,
|
||||
price: (
|
||||
parseInt( product.prices.price, 10 ) /
|
||||
10 ** product.prices.currency_minor_unit
|
||||
).toString(),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats data into the impressionFieldObject shape.
|
||||
*
|
||||
* @see https://developers.google.com/analytics/devguides/collection/gtagjs/enhanced-ecommerce#impression-data
|
||||
*/
|
||||
export const getProductImpressionObject = (
|
||||
product: ProductResponseItem,
|
||||
listName: string
|
||||
): ImpressionItem => {
|
||||
const productIdentifier = product.sku ? product.sku : '#' + product.id;
|
||||
const productCategory = product.categories.length
|
||||
? product.categories[ 0 ].name
|
||||
: '';
|
||||
return {
|
||||
id: productIdentifier,
|
||||
name: product.name,
|
||||
list_name: listName,
|
||||
category: productCategory,
|
||||
price: (
|
||||
parseInt( product.prices.price, 10 ) /
|
||||
10 ** product.prices.currency_minor_unit
|
||||
).toString(),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Track an event using the global gtag function.
|
||||
*/
|
||||
export const trackEvent = (
|
||||
eventName: Gtag.EventNames | string,
|
||||
eventParams?: Gtag.ControlParams | Gtag.EventParams | Gtag.CustomParams
|
||||
): void => {
|
||||
if ( typeof gtag !== 'function' ) {
|
||||
throw new Error( 'Function gtag not implemented.' );
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.log( `Tracking event ${ eventName }` );
|
||||
window.gtag( 'event', eventName, eventParams );
|
||||
};
|
||||
|
||||
let currentStep = -1;
|
||||
|
||||
export const trackCheckoutStep = ( step: number ) => ( {
|
||||
storeCart,
|
||||
}: {
|
||||
storeCart: StoreCart;
|
||||
} ): void => {
|
||||
if ( currentStep === step ) {
|
||||
return;
|
||||
}
|
||||
trackEvent( step === 0 ? 'begin_checkout' : 'checkout_progress', {
|
||||
items: storeCart.cartItems.map( getProductFieldObject ),
|
||||
coupon: storeCart.cartCoupons[ 0 ]?.code || '',
|
||||
currency: storeCart.cartTotals.currency_code,
|
||||
value: (
|
||||
parseInt( storeCart.cartTotals.total_price, 10 ) /
|
||||
10 ** storeCart.cartTotals.currency_minor_unit
|
||||
).toString(),
|
||||
checkout_step: step,
|
||||
} );
|
||||
currentStep = step;
|
||||
};
|
||||
|
||||
export const trackCheckoutOption = ( {
|
||||
step,
|
||||
option,
|
||||
value,
|
||||
}: {
|
||||
step: number;
|
||||
option: string;
|
||||
value: string;
|
||||
} ) => (): void => {
|
||||
trackEvent( 'set_checkout_option', {
|
||||
checkout_step: step,
|
||||
checkout_option: option,
|
||||
value,
|
||||
} );
|
||||
currentStep = step;
|
||||
};
|
Reference in New Issue
Block a user