Files
novalon-website/src/app/(marketing)/cases/[id]/client.tsx
T
张翔 2f45818724 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
2026-04-22 07:19:29 +08:00

287 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useEffect, useRef, useState } from 'react';
import { StaticLink } from '@/components/ui/static-link';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { BackButton } from '@/components/ui/back-button';
import {
Target,
Quote,
Clock,
MessageCircle,
Award,
TrendingUp,
} from 'lucide-react';
interface CaseKeyMoment {
title: string;
description: string;
}
interface CaseResult {
label: string;
value: string;
}
interface CaseTestimonial {
quote: string;
author: string;
role: string;
}
interface CaseItem {
id: string;
title: string;
excerpt: string;
content: string;
category: string;
slug: string;
date: string;
image?: string;
/** 客户面临的挑战 */
challenge: string;
/** 我们的解决方案 */
solution: string;
/** 关键时刻 */
keyMoments: CaseKeyMoment[];
/** 成果数据 */
results: CaseResult[];
/** 客户证言 */
testimonial?: CaseTestimonial;
/** 合作时长 */
duration: string;
}
interface CaseDetailClientProps {
caseItem: CaseItem;
}
export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
const [isVisible, setIsVisible] = useState(false);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry?.isIntersecting) {
setIsVisible(true);
}
},
{ threshold: 0.1 }
);
if (contentRef.current) {
observer.observe(contentRef.current);
}
return () => observer.disconnect();
}, []);
return (
<div className="min-h-screen bg-white">
<div className="relative overflow-hidden bg-gradient-to-b from-[#FAFAFA] to-white">
<div className="container-wide relative z-10 pt-32 pb-20">
<BackButton />
<div className="max-w-4xl mt-8">
<Badge className="mb-4 bg-[#C41E3A]/10 text-[#C41E3A] hover:bg-[#C41E3A]/20">
{caseItem.category}
</Badge>
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-semibold text-[#1C1C1C] mb-2">
{caseItem.title}
</h1>
<p className="text-lg text-[#5C5C5C]">{caseItem.excerpt}</p>
</div>
</div>
</div>
<div ref={contentRef} className="container-wide py-12 md:py-16">
<div
className={`
grid lg:grid-cols-3 gap-8 lg:gap-12
opacity-0 translate-y-4
${isVisible ? 'animate-fade-in-up' : ''}
`}
>
<div className="lg:col-span-2 space-y-12">
{/* 客户遇到的成长瓶颈 */}
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<MessageCircle className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
</h2>
</div>
<p className="text-[#5C5C5C] leading-relaxed text-lg">
{caseItem.challenge}
</p>
</section>
{/* 我们如何智连未来 */}
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<Target className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
</h2>
</div>
<div className="prose prose-base max-w-none [&_h3]:text-xl [&_h3]:font-semibold [&_h3]:text-[#1C1C1C] [&_h3]:mt-8 [&_h3]:mb-4 [&_p]:text-[#5C5C5C] [&_p]:leading-[1.8] [&_p]:mb-4 [&_p]:text-base">
<p className="text-[#5C5C5C] leading-relaxed text-lg">
{caseItem.solution}
</p>
</div>
</section>
{/* 共同成长的故事 */}
{caseItem.keyMoments && caseItem.keyMoments.length > 0 && (
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<Clock className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
</h2>
</div>
<div className="space-y-4">
{caseItem.keyMoments.map((moment, index) => (
<div
key={index}
className="p-4 bg-white rounded-lg border border-[#E5E5E5]"
>
<div className="flex items-start gap-3">
<Quote className="w-5 h-5 text-[#C41E3A] flex-shrink-0 mt-0.5" />
<div>
<h4 className="font-semibold text-[#1C1C1C] mb-2">
{moment.title}
</h4>
<p className="text-sm text-[#737373]">
{moment.description}
</p>
</div>
</div>
</div>
))}
</div>
</section>
)}
{/* 今天,他们走到了哪里 */}
{caseItem.results && caseItem.results.length > 0 && (
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<Award className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
</h2>
</div>
<div className="grid sm:grid-cols-3 gap-4">
{caseItem.results.map((result, index) => (
<div
key={index}
className="p-6 bg-white rounded-lg border border-[#E5E5E5] hover:border-[#C41E3A] transition-colors"
>
<TrendingUp className="w-8 h-8 text-[#C41E3A] mb-3" />
<div className="text-2xl font-semibold text-[#C41E3A] mb-1">
{result.value}
</div>
<div className="text-sm text-[#737373]">
{result.label}
</div>
</div>
))}
</div>
</section>
)}
{/* 客户证言精选 */}
{caseItem.testimonial && (
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<Quote className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
</h2>
</div>
<div className="p-6 bg-white rounded-lg border border-[#E5E5E5]">
<Quote className="w-8 h-8 text-[#C41E3A] mb-4" />
<p className="text-lg text-[#1C1C1C] leading-relaxed mb-4">
{caseItem.testimonial.quote}
</p>
<div className="flex items-center gap-3">
<div className="w-12 h-12 bg-[#C41E3A] rounded-full flex items-center justify-center">
<span className="text-white font-semibold"></span>
</div>
<div>
<p className="font-semibold text-[#1C1C1C]">
{caseItem.testimonial.author}
</p>
<p className="text-sm text-[#737373]">
{caseItem.testimonial.role}
</p>
</div>
</div>
</div>
</section>
)}
</div>
{/* 侧边栏 */}
<div className="space-y-6">
<div className="p-6 bg-[#FAFAFA] rounded-lg border border-[#E5E5E5]">
<h3 className="text-lg font-semibold text-[#1C1C1C] mb-4">
</h3>
<dl className="space-y-3">
<div>
<dt className="text-sm text-[#737373]"></dt>
<dd className="text-[#1C1C1C] font-medium">
{caseItem.testimonial?.author || '客户企业'}
</dd>
</div>
<div>
<dt className="text-sm text-[#737373]"></dt>
<dd className="text-[#1C1C1C] font-medium">
{caseItem.category}
</dd>
</div>
<div>
<dt className="text-sm text-[#737373]"></dt>
<dd className="text-[#1C1C1C] font-medium">
{caseItem.duration || '3年'}
</dd>
</div>
<div>
<dt className="text-sm text-[#737373]"></dt>
<dd className="text-[#1C1C1C] font-medium">{caseItem.date}</dd>
</div>
</dl>
</div>
<div className="p-6 bg-gradient-to-br from-[#C41E3A] to-[#8B1429] rounded-lg text-white">
<h3 className="text-lg font-semibold mb-2"></h3>
<p className="text-sm text-white/80 mb-4">
</p>
<Button
className="w-full bg-white text-[#C41E3A] hover:bg-white/90"
asChild
>
<StaticLink href="/contact"></StaticLink>
</Button>
</div>
</div>
</div>
</div>
</div>
);
}