'use client'; import { useEffect } from 'react'; import { useWebVitals } from '@/lib/useWebVitals'; import { trackEvent, trackPageLoad } from '@/lib/analytics'; interface AnalyticsProviderProps { children: React.ReactNode; } export const AnalyticsProvider: React.FC = ({ children }) => { // Initialize Web Vitals tracking useWebVitals(); useEffect(() => { if (typeof window === 'undefined') return; // Track page view const trackPageView = () => { trackEvent('page-view', { url: window.location.pathname, referrer: document.referrer, timestamp: Date.now(), }); }; // Track page load performance trackPageLoad(); // Track initial page view trackPageView(); // Track route changes (for SPA navigation) const handleRouteChange = () => { setTimeout(() => { trackPageView(); trackPageLoad(); }, 100); }; // Listen for popstate events (back/forward navigation) window.addEventListener('popstate', handleRouteChange); // Track user interactions const handleClick = (event: MouseEvent) => { const target = event.target as HTMLElement; const element = target.tagName.toLowerCase(); const className = target.className; const id = target.id; trackEvent('click', { element, className: className ? className.split(' ')[0] : undefined, id: id || undefined, url: window.location.pathname, }); }; // Track form submissions const handleSubmit = (event: SubmitEvent) => { const form = event.target as HTMLFormElement; trackEvent('form-submit', { formId: form.id || undefined, formClass: form.className || undefined, url: window.location.pathname, }); }; // Track scroll depth let maxScrollDepth = 0; const handleScroll = () => { const scrollDepth = Math.round( (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100 ); if (scrollDepth > maxScrollDepth) { maxScrollDepth = scrollDepth; // Track scroll milestones if (scrollDepth >= 25 && scrollDepth < 50 && maxScrollDepth >= 25) { trackEvent('scroll-depth', { depth: 25, url: window.location.pathname }); } else if (scrollDepth >= 50 && scrollDepth < 75 && maxScrollDepth >= 50) { trackEvent('scroll-depth', { depth: 50, url: window.location.pathname }); } else if (scrollDepth >= 75 && scrollDepth < 90 && maxScrollDepth >= 75) { trackEvent('scroll-depth', { depth: 75, url: window.location.pathname }); } else if (scrollDepth >= 90 && maxScrollDepth >= 90) { trackEvent('scroll-depth', { depth: 90, url: window.location.pathname }); } } }; // Add event listeners document.addEventListener('click', handleClick); document.addEventListener('submit', handleSubmit); window.addEventListener('scroll', handleScroll, { passive: true }); // Track errors const handleError = (event: ErrorEvent) => { trackEvent('error', { message: event.message, filename: event.filename, lineno: event.lineno, colno: event.colno, url: window.location.pathname, }); }; const handleUnhandledRejection = (event: PromiseRejectionEvent) => { trackEvent('unhandled-rejection', { reason: event.reason?.toString(), url: window.location.pathname, }); }; window.addEventListener('error', handleError); window.addEventListener('unhandledrejection', handleUnhandledRejection); // Cleanup return () => { window.removeEventListener('popstate', handleRouteChange); document.removeEventListener('click', handleClick); document.removeEventListener('submit', handleSubmit); window.removeEventListener('scroll', handleScroll); window.removeEventListener('error', handleError); window.removeEventListener('unhandledrejection', handleUnhandledRejection); }; }, []); return <>{children}; };