/** * External dependencies */ import { __ } from '@wordpress/i18n'; import { addAction } from '@wordpress/hooks'; import type { ProductResponseItem, CartResponseItem } 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 ) ); /** * 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', 'woocommerce' ), 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', 'woocommerce' ), 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', 'woocommerce' ), 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', 'woocommerce' ), 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', 'woocommerce' ), 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', 'woocommerce' ), }: { products: Array< ProductResponseItem >; listName: string; } ): void => { if ( products.length === 0 ) { return; } trackEvent( 'view_item_list', { event_category: 'engagement', event_label: __( 'Viewing products', 'woocommerce' ), 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, } ); } } );