Files
mintel.me/src/components/Analytics.tsx
Marc Mintel 1f57bae339
Some checks failed
Build & Deploy Mintel Blog / build-and-deploy (push) Failing after 1m26s
deploy
2026-01-30 13:57:36 +01:00

90 lines
3.0 KiB
TypeScript

'use client';
import { useEffect } from 'react';
import { getDefaultAnalytics } from '../utils/analytics';
import { getDefaultErrorTracking } from '../utils/error-tracking';
export const Analytics: React.FC = () => {
useEffect(() => {
const analytics = getDefaultAnalytics();
const errorTracking = getDefaultErrorTracking();
// Track page load performance
const trackPageLoad = () => {
const perfData = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
if (perfData && typeof perfData.loadEventEnd === 'number' && typeof perfData.startTime === 'number') {
const loadTime = perfData.loadEventEnd - perfData.startTime;
analytics.trackPageLoad(
loadTime,
window.location.pathname,
navigator.userAgent
);
}
};
// Track outbound links
const trackOutboundLinks = () => {
document.querySelectorAll('a[href^="http"]').forEach(link => {
const anchor = link as HTMLAnchorElement;
if (!anchor.href.includes(window.location.hostname)) {
anchor.addEventListener('click', () => {
analytics.trackOutboundLink(anchor.href, anchor.textContent?.trim() || 'unknown');
});
}
});
};
// Track search
const trackSearch = () => {
const searchInput = document.querySelector('input[type="search"]') as HTMLInputElement;
if (searchInput) {
const handleSearch = (e: Event) => {
const target = e.target as HTMLInputElement;
if (target.value) {
analytics.trackSearch(target.value, window.location.pathname);
}
};
searchInput.addEventListener('search', handleSearch);
return () => searchInput.removeEventListener('search', handleSearch);
}
};
// Global error handler for error tracking
const handleGlobalError = (event: ErrorEvent) => {
errorTracking.captureException(event.error || event.message);
};
const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
errorTracking.captureException(event.reason);
};
window.addEventListener('error', handleGlobalError);
window.addEventListener('unhandledrejection', handleUnhandledRejection);
trackPageLoad();
trackOutboundLinks();
const cleanupSearch = trackSearch();
return () => {
if (cleanupSearch) cleanupSearch();
window.removeEventListener('error', handleGlobalError);
window.removeEventListener('unhandledrejection', handleUnhandledRejection);
};
}, []);
const analytics = getDefaultAnalytics();
const adapter = analytics.getAdapter();
const scriptTag = adapter.getScriptTag ? adapter.getScriptTag() : null;
if (!scriptTag) return null;
// We use dangerouslySetInnerHTML to inject the script tag from the adapter
// This is safe here because the script URLs and IDs come from our own config/env
return (
<div
dangerouslySetInnerHTML={{ __html: scriptTag }}
style={{ display: 'none' }}
/>
);
};