88 lines
2.2 KiB
TypeScript
88 lines
2.2 KiB
TypeScript
/**
|
|
* Web Vitals Reporting Service
|
|
* Tracks and reports Core Web Vitals metrics to Google Analytics
|
|
*/
|
|
|
|
import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals';
|
|
|
|
/**
|
|
* Web Vitals metric data structure
|
|
*/
|
|
interface Metric {
|
|
name: string;
|
|
value: number;
|
|
rating: 'good' | 'needs-improvement' | 'poor';
|
|
delta: number;
|
|
id: string;
|
|
}
|
|
|
|
/**
|
|
* Send Web Vitals metric to Google Analytics and dispatch custom event
|
|
*/
|
|
function sendToAnalytics(metric: Metric) {
|
|
const body = JSON.stringify({
|
|
name: metric.name,
|
|
value: metric.value,
|
|
rating: metric.rating,
|
|
delta: metric.delta,
|
|
id: metric.id
|
|
});
|
|
|
|
// Send to Google Analytics
|
|
if (window.gtag) {
|
|
window.gtag('event', metric.name, {
|
|
event_category: 'Web Vitals',
|
|
event_label: metric.id,
|
|
value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
|
|
non_interaction: true,
|
|
metric_rating: metric.rating,
|
|
metric_delta: metric.delta
|
|
});
|
|
}
|
|
|
|
// Dispatch custom event for Performance Monitor
|
|
window.dispatchEvent(new CustomEvent('web-vitals', {
|
|
detail: {
|
|
name: metric.name,
|
|
value: metric.value,
|
|
rating: metric.rating,
|
|
timestamp: Date.now()
|
|
}
|
|
}));
|
|
|
|
// Comment out analytics endpoint for now - not implemented
|
|
// if (navigator.sendBeacon) {
|
|
// navigator.sendBeacon('/api/analytics', body);
|
|
// }
|
|
}
|
|
|
|
/**
|
|
* Initialize Web Vitals reporting for all Core Web Vitals metrics
|
|
*/
|
|
export function reportWebVitals() {
|
|
onCLS(sendToAnalytics);
|
|
onINP(sendToAnalytics);
|
|
onLCP(sendToAnalytics);
|
|
onFCP(sendToAnalytics);
|
|
onTTFB(sendToAnalytics);
|
|
}
|
|
|
|
/**
|
|
* Calculate Web Vitals rating based on metric thresholds
|
|
*/
|
|
export function getWebVitalsScore(metric: Metric): string {
|
|
const thresholds = {
|
|
LCP: { good: 2500, poor: 4000 },
|
|
INP: { good: 200, poor: 500 },
|
|
CLS: { good: 0.1, poor: 0.25 },
|
|
FCP: { good: 1800, poor: 3000 },
|
|
TTFB: { good: 800, poor: 1800 }
|
|
};
|
|
|
|
const threshold = thresholds[metric.name as keyof typeof thresholds];
|
|
if (!threshold) return 'unknown';
|
|
|
|
if (metric.value <= threshold.good) return 'good';
|
|
if (metric.value <= threshold.poor) return 'needs-improvement';
|
|
return 'poor';
|
|
} |