feat(analytics): enhance Google Analytics with privacy compliance and comprehensive tracking

- Add automatic route change tracking for SPA navigation
- Implement Cookie consent banner for GDPR compliance
- Add performance tracking (LCP, FID, CLS Web Vitals)
- Add outbound link click tracking
- Integrate contact form submission tracking with conversion events
- Add CTA button click tracking in hero section
- Integrate error tracking in ErrorBoundary component
- Extend analytics utility library with 15+ tracking functions
- Configure IP anonymization and privacy settings
- Remove unused test files and deployment scripts
- Update case studies to include only specified cases
- Fix mobile navigation active state issues
- Fix lint errors in test files and components

BREAKING CHANGE: Google Analytics now requires user consent before tracking
This commit is contained in:
张翔
2026-04-22 07:19:29 +08:00
parent b117372b03
commit 2f45818724
45 changed files with 652 additions and 2293 deletions
@@ -0,0 +1,95 @@
'use client';
import { useState, useEffect } from 'react';
import { updateConsent, trackButtonClick } from '@/lib/analytics';
import { motion, AnimatePresence } from 'framer-motion';
const CONSENT_KEY = 'ga_consent';
export function CookieConsent() {
const [showConsent, setShowConsent] = useState(false);
const [isAnimating, setIsAnimating] = useState(false);
useEffect(() => {
const consent = localStorage.getItem(CONSENT_KEY);
if (!consent) {
const timer = setTimeout(() => {
setShowConsent(true);
}, 2000);
return () => clearTimeout(timer);
} else if (consent === 'granted') {
updateConsent(true);
}
return undefined;
}, []);
const handleAccept = () => {
setIsAnimating(true);
localStorage.setItem(CONSENT_KEY, 'granted');
updateConsent(true);
trackButtonClick('accept_cookies', 'consent_banner');
setTimeout(() => {
setShowConsent(false);
setIsAnimating(false);
}, 300);
};
const handleDecline = () => {
setIsAnimating(true);
localStorage.setItem(CONSENT_KEY, 'denied');
updateConsent(false);
trackButtonClick('decline_cookies', 'consent_banner');
setTimeout(() => {
setShowConsent(false);
setIsAnimating(false);
}, 300);
};
return (
<AnimatePresence>
{showConsent && (
<motion.div
initial={{ y: 100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 100, opacity: 0 }}
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
className="fixed bottom-16 md:bottom-0 left-0 right-0 z-[9998] bg-white border-t border-gray-200 shadow-lg"
>
<div className="max-w-7xl mx-auto px-4 py-4 sm:px-6 lg:px-8">
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
<div className="flex-1">
<p className="text-sm text-gray-700">
使 Cookie
使{' '}
<a
href="/privacy"
className="text-[#C41E3A] hover:text-[#A01830] underline font-medium"
>
</a>
</p>
</div>
<div className="flex gap-3 shrink-0">
<button
onClick={handleDecline}
disabled={isAnimating}
className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors disabled:opacity-50"
>
</button>
<button
onClick={handleAccept}
disabled={isAnimating}
className="px-4 py-2 text-sm font-medium text-white bg-[#C41E3A] rounded-lg hover:bg-[#A01830] transition-colors disabled:opacity-50"
>
</button>
</div>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
);
}