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
+20 -15
View File
@@ -1,17 +1,27 @@
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
import { describe, it, expect, jest, beforeEach } from '@jest/globals';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { BackButton } from './back-button';
jest.mock('next/navigation', () => ({
useRouter: () => ({
back: jest.fn(),
}),
}));
describe('BackButton', () => {
const mockBack = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
mockBack.mockClear();
// Mock window.history.back
Object.defineProperty(window, 'history', {
value: {
back: mockBack,
forward: jest.fn(),
go: jest.fn(),
length: 1,
pushState: jest.fn(),
replaceState: jest.fn(),
scrollRestoration: 'auto',
state: null,
},
writable: true,
});
});
describe('Rendering', () => {
@@ -33,16 +43,11 @@ describe('BackButton', () => {
});
describe('Interaction', () => {
it('should call router.back() when clicked', () => {
const mockBack = jest.fn();
jest.spyOn(require('next/navigation'), 'useRouter').mockReturnValue({
back: mockBack,
});
it('should call window.history.back() when clicked', () => {
render(<BackButton />);
fireEvent.click(screen.getByRole('button'));
expect(mockBack).toHaveBeenCalled();
expect(mockBack).toHaveBeenCalledTimes(1);
});
});
+2
View File
@@ -1,6 +1,7 @@
'use client';
import { Component, ReactNode } from 'react';
import { trackError } from '@/lib/analytics';
interface Props {
children: ReactNode;
@@ -24,6 +25,7 @@ export class ErrorBoundary extends Component<Props, State> {
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
trackError('react_error', error.message, true);
}
render() {