migrate to nextjs

This commit is contained in:
2026-01-29 18:50:43 +01:00
parent 019b59602f
commit 4906fc0b7b
43 changed files with 2795 additions and 3515 deletions

View File

@@ -0,0 +1,71 @@
'use client';
import React, { useEffect } from 'react';
import { createPlausibleAnalytics } from '../utils/analytics';
interface AnalyticsProps {
domain?: string;
scriptUrl?: string;
}
export const Analytics: React.FC<AnalyticsProps> = ({
domain = 'mintel.me',
scriptUrl = 'https://plausible.yourdomain.com/js/script.js'
}) => {
useEffect(() => {
const analytics = createPlausibleAnalytics({
domain: document.documentElement.lang || domain,
scriptUrl
});
// 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);
}
};
trackPageLoad();
trackOutboundLinks();
const cleanupSearch = trackSearch();
return () => {
if (cleanupSearch) cleanupSearch();
};
}, [domain, scriptUrl]);
return null;
};