/** * External dependencies */ import type { ConversionEvent, Maybe, TrackEvent } from '@nab/types'; /** * Internal dependencies */ import type { Session, Settings } from '../types'; import { getSettings } from '../utils/helpers/internal/get-settings'; function sendToGA4( eventData: TrackEvent ) { const settings = getSettings(); if ( ( ! settings?.ga4TrackingMethod || settings.ga4TrackingMethod === 'gtm' ) && hasDataLayer( window ) ) { window.dataLayer.push( { event: eventData.kind === 'visit' ? 'experiment_impression' : 'conversion', experiment_id: eventData.experiment, variant_id: eventData.alternative, variant_name: getVariantName( settings, eventData ), ...( eventData.kind === 'conversion' && { goal_id: eventData.goal, goal_name: getGoalName( settings, eventData ), } ), } ); } else if ( ( ! settings?.ga4TrackingMethod || settings.ga4TrackingMethod === 'gtag' ) && hasGTag( window ) ) { window.gtag( 'event', eventData.kind === 'visit' ? 'experiment_impression' : 'conversion', { experiment_id: eventData.experiment, variant_id: eventData.alternative, variant_name: getVariantName( settings, eventData ), ...( eventData.kind === 'conversion' && { goal_id: eventData.goal, goal_name: getGoalName( settings, eventData ), } ), } ); } } const hasGTag = ( win: unknown ): win is { gtag: ( command: string, eventName: string, params?: Record< string, unknown > ) => void; } => !! win && 'object' === typeof win && 'gtag' in win; const hasDataLayer = ( win: unknown ): win is { dataLayer: Array< Record< string, unknown > >; } => !! win && 'object' === typeof win && 'dataLayer' in win; export function initGA4Tracking( session: Session ): void { if ( ! session.isGA4Integrated ) { return; } window.addEventListener( 'nelio-ab-testing/track', ( e: CustomEventInit< { event: TrackEvent } > ) => { if ( e.detail && e.detail.event && [ 'visit', 'conversion' ].includes( e.detail.event.kind ) ) { sendToGA4( e.detail.event ); } } ); } export function getGA4ClientId(): string | undefined { const match = document.cookie.match( /_ga=GA\d\.\d\.(\d+\.\d+)/ ); return match ? match[ 1 ] : undefined; } function getVariantName( settings: Maybe< Required< Settings > >, eventData: TrackEvent ): Maybe< string > { if ( ! settings ) { return undefined; } const exp = settings.experiments.find( ( e ) => e.id === eventData.experiment ); if ( ! exp ) { return undefined; } if ( ! exp.alternatives ) { return undefined; } if ( ! exp.alternatives[ eventData.alternative ] ) { return undefined; } const alt = exp.alternatives[ eventData.alternative ]; if ( !! alt && typeof alt === 'object' && 'name' in alt && typeof alt.name === 'string' ) { return alt.name; } return eventData.alternative === 0 ? 'Control' : `Variant ${ eventData.alternative }`; } function getGoalName( settings: Maybe< Required< Settings > >, eventData: ConversionEvent ): Maybe< string > { if ( ! settings ) { return undefined; } const exp = settings.experiments.find( ( e ) => e.id === eventData.experiment ); if ( ! exp ) { return undefined; } if ( ! exp.goals ) { return undefined; } if ( ! exp.goals[ eventData.goal ] ) { return undefined; } const goal = exp.goals[ eventData.goal ]; if ( ! goal ) { return undefined; } return goal.name; }