'use client'; import { useEffect } from 'react'; import { trackWebVitals, trackPerformance } from './analytics'; // Web Vitals types interface Metric { name: string; value: number; delta: number; id: string; } // Simple Web Vitals implementation (since we don't want to add external dependencies) const getCLS = (onPerfEntry: (metric: Metric) => void) => { let clsValue = 0; let sessionValue = 0; let sessionEntries: PerformanceEntry[] = []; const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (!(entry as PerformanceEntry & { hadRecentInput?: boolean }).hadRecentInput) { const firstSessionEntry = sessionEntries[0]; const lastSessionEntry = sessionEntries[sessionEntries.length - 1]; if (sessionValue && entry.startTime - lastSessionEntry.startTime < 1000 && entry.startTime - firstSessionEntry.startTime < 5000) { sessionValue += (entry as PerformanceEntry & { value?: number }).value || 0; sessionEntries.push(entry); } else { sessionValue = (entry as PerformanceEntry & { value?: number }).value || 0; sessionEntries = [entry]; } if (sessionValue > clsValue) { clsValue = sessionValue; onPerfEntry({ name: 'CLS', value: clsValue, delta: clsValue, id: `cls-${Date.now()}`, }); } } } }); observer.observe({ type: 'layout-shift', buffered: true }); }; const getFID = (onPerfEntry: (metric: Metric) => void) => { const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { onPerfEntry({ name: 'FID', value: (entry as PerformanceEntry & { processingStart?: number }).processingStart! - entry.startTime, delta: (entry as PerformanceEntry & { processingStart?: number }).processingStart! - entry.startTime, id: `fid-${Date.now()}`, }); } }); observer.observe({ type: 'first-input', buffered: true }); }; const getFCP = (onPerfEntry: (metric: Metric) => void) => { const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.name === 'first-contentful-paint') { onPerfEntry({ name: 'FCP', value: entry.startTime, delta: entry.startTime, id: `fcp-${Date.now()}`, }); } } }); observer.observe({ type: 'paint', buffered: true }); }; const getLCP = (onPerfEntry: (metric: Metric) => void) => { const observer = new PerformanceObserver((list) => { const entries = list.getEntries(); const lastEntry = entries[entries.length - 1]; onPerfEntry({ name: 'LCP', value: lastEntry.startTime, delta: lastEntry.startTime, id: `lcp-${Date.now()}`, }); }); observer.observe({ type: 'largest-contentful-paint', buffered: true }); }; const getTTFB = (onPerfEntry: (metric: Metric) => void) => { const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.entryType === 'navigation') { const navEntry = entry as PerformanceNavigationTiming; onPerfEntry({ name: 'TTFB', value: navEntry.responseStart - navEntry.fetchStart, delta: navEntry.responseStart - navEntry.fetchStart, id: `ttfb-${Date.now()}`, }); } } }); observer.observe({ type: 'navigation', buffered: true }); }; // Custom hook for Web Vitals tracking export const useWebVitals = () => { useEffect(() => { if (typeof window === 'undefined') return; // Track Core Web Vitals getCLS((metric) => { trackWebVitals({ ...metric, name: metric.name as 'CLS' | 'FID' | 'FCP' | 'LCP' | 'TTFB', url: window.location.pathname, }); }); getFID((metric) => { trackWebVitals({ ...metric, name: metric.name as 'CLS' | 'FID' | 'FCP' | 'LCP' | 'TTFB', url: window.location.pathname, }); }); getFCP((metric) => { trackWebVitals({ ...metric, name: metric.name as 'CLS' | 'FID' | 'FCP' | 'LCP' | 'TTFB', url: window.location.pathname, }); }); getLCP((metric) => { trackWebVitals({ ...metric, name: metric.name as 'CLS' | 'FID' | 'FCP' | 'LCP' | 'TTFB', url: window.location.pathname, }); }); getTTFB((metric) => { trackWebVitals({ ...metric, name: metric.name as 'CLS' | 'FID' | 'FCP' | 'LCP' | 'TTFB', url: window.location.pathname, }); }); // Track page load performance const handleLoad = () => { setTimeout(() => { trackPerformance({ name: 'page-load-complete', value: performance.now(), url: window.location.pathname, timestamp: Date.now(), userAgent: navigator.userAgent, }); }, 0); }; if (document.readyState === 'complete') { handleLoad(); } else { window.addEventListener('load', handleLoad); } return () => { window.removeEventListener('load', handleLoad); }; }, []); };