type GtagFn = (...args: unknown[]) => void; const TEXT_LIMIT = 80; function gtag(): GtagFn | null { if (typeof window === 'undefined') return null; const fn = (window as unknown as { gtag?: GtagFn }).gtag; return typeof fn === 'function' ? fn : null; } export function trackPageView(path: string, title: string): void { const send = gtag(); if (!send) return; send('event', 'page_view', { page_path: path, page_title: title, page_location: window.location.href }); } function getLabel(el: HTMLElement): string { const aria = el.getAttribute('aria-label'); if (aria) return aria.trim().slice(0, TEXT_LIMIT); const text = (el.textContent ?? '').replace(/\s+/g, ' ').trim(); if (text) return text.slice(0, TEXT_LIMIT); const title = el.getAttribute('title'); if (title) return title.trim().slice(0, TEXT_LIMIT); return ''; } function getLocation(el: HTMLElement): string { const dataLoc = el.closest('[data-track-location]'); if (dataLoc?.dataset.trackLocation) { return dataLoc.dataset.trackLocation.slice(0, TEXT_LIMIT); } const section = el.closest('section[id]'); if (section?.id) return section.id; if (el.closest('header')) return 'header'; if (el.closest('footer')) return 'footer'; if (el.closest('nav')) return 'nav'; return 'body'; } export function initClickTracking(): () => void { if (typeof window === 'undefined') return () => {}; const handler = (event: MouseEvent) => { const send = gtag(); if (!send) return; const target = event.target; if (!(target instanceof Element)) return; const interactive = target.closest('a, button, [role="button"]'); if (!interactive) return; const isLink = interactive.tagName === 'A'; const label = getLabel(interactive); const location = getLocation(interactive); const params: Record = { element: isLink ? 'link' : 'button', label, location, page_path: window.location.pathname }; if (isLink) { const href = (interactive as HTMLAnchorElement).href; if (href) { params.link_url = href; try { const url = new URL(href, window.location.href); params.outbound = url.hostname !== window.location.hostname; } catch { params.outbound = false; } } } else { const btn = interactive as HTMLButtonElement; if (btn.type) params.button_type = btn.type; if (btn.name) params.button_name = btn.name; } send('event', isLink ? 'link_click' : 'button_click', params); }; document.addEventListener('click', handler, { capture: true }); return () => document.removeEventListener('click', handler, { capture: true }); }