NebulaCloud/components/BlogShowcase.tsx

288 lines
16 KiB
TypeScript
Raw Permalink Normal View History

2025-09-15 17:28:58 +08:00
'use client';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import { Locale } from '../lib/i18n';
import { getTranslations } from '../lib/translations';
interface BlogShowcaseProps {
locale: Locale;
}
export default function BlogShowcase({ locale }: BlogShowcaseProps) {
const t = getTranslations(locale);
const [hoveredPost, setHoveredPost] = useState<string | null>(null);
// 获取博客文章数据
const blogPosts = [
{
id: 'featured',
...t.blog.posts.featured,
image: 'featured',
},
{
id: 'post1',
...t.blog.posts.post1,
image: 'post1',
},
{
id: 'post2',
...t.blog.posts.post2,
image: 'post2',
},
{
id: 'post3',
...t.blog.posts.post3,
image: 'post3',
},
];
const getCategoryColor = (category: string) => {
switch (category) {
case 'playerStories':
return 'bg-amber-500';
case 'development':
return 'bg-green-500';
case 'ecoFacts':
return 'bg-emerald-500';
default:
return 'bg-blue-500';
}
};
const getCategoryName = (category: string) => {
switch (category) {
case 'playerStories':
return t.blog.categories.playerStories;
case 'development':
return t.blog.categories.development;
case 'ecoFacts':
return t.blog.categories.ecoFacts;
default:
return category;
}
};
const getImageGradient = (imageId: string) => {
const gradients = {
featured: 'from-purple-500 via-blue-500 to-indigo-600',
post1: 'from-amber-500 via-orange-500 to-red-500',
post2: 'from-cyan-500 via-blue-500 to-indigo-500',
post3: 'from-green-500 via-emerald-500 to-teal-500',
};
return gradients[imageId as keyof typeof gradients] || 'from-gray-500 to-gray-600';
};
return (
<section className="relative z-10 py-20 bg-gradient-to-br from-slate-800 via-slate-900 to-black">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Section Header */}
<div className="text-center mb-16">
<h2 className="text-4xl md:text-5xl font-bold text-white mb-4">
{t.blog.title}
</h2>
<p className="text-xl text-white/80 max-w-3xl mx-auto leading-relaxed">
{t.blog.subtitle}
</p>
<div className="mt-8">
<Link
href={`/${locale}/blog`}
className="inline-flex items-center bg-gradient-to-r from-green-500 to-emerald-600 hover:from-green-600 hover:to-emerald-700 text-white px-8 py-3 rounded-full font-semibold transition-all duration-200 transform hover:scale-105 shadow-lg"
>
{t.blog.categories.all}
<svg
className="w-5 h-5 ml-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M17 8l4 4m0 0l-4 4m4-4H3"
/>
</svg>
</Link>
</div>
</div>
{/* Blog Posts Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Featured Post */}
<div className="lg:row-span-2">
<Link href={`/${locale}/blog/${blogPosts[0].id}`}>
<article
className="group cursor-pointer h-full"
onMouseEnter={() => setHoveredPost(blogPosts[0].id)}
onMouseLeave={() => setHoveredPost(null)}
>
<div className="bg-white/10 backdrop-blur-md rounded-3xl overflow-hidden border border-white/20 shadow-2xl hover:shadow-3xl transition-all duration-500 transform hover:scale-[1.02] h-full">
{/* Featured Post Image */}
<div className="relative h-80 overflow-hidden">
<div
className={`absolute inset-0 bg-gradient-to-br ${getImageGradient(blogPosts[0].image)} opacity-90`}
>
<div className="absolute inset-0 bg-black/20"></div>
{/* Animated particles for featured post */}
<div className="absolute inset-0">
{[...Array(6)].map((_, i) => (
<div
key={i}
className={`absolute w-2 h-2 bg-white/30 rounded-full animate-pulse ${
hoveredPost === blogPosts[0].id
? 'animate-bounce'
: ''
}`}
style={{
left: `${20 + i * 15}%`,
top: `${30 + (i % 2) * 20}%`,
animationDelay: `${i * 0.2}s`,
}}
/>
))}
</div>
</div>
<div className="absolute top-6 left-6">
<span
className={`${getCategoryColor(blogPosts[0].category)} text-white px-4 py-2 rounded-full text-sm font-medium shadow-lg`}
>
{getCategoryName(blogPosts[0].category)}
</span>
</div>
<div className="absolute top-6 right-6">
<div className="bg-white/20 backdrop-blur-sm rounded-full px-3 py-1">
<span className="text-white text-sm font-medium">
{t.blog.featured}
</span>
</div>
</div>
<div className="absolute bottom-6 left-6 right-6">
<div className="text-white/90 text-sm mb-2">
<div className="flex items-center space-x-4">
<span>{blogPosts[0].author}</span>
<span></span>
<span>{blogPosts[0].date}</span>
<span></span>
<span>{blogPosts[0].readTime}</span>
</div>
</div>
</div>
</div>
{/* Featured Post Content */}
<div className="p-8">
<h3 className="text-2xl font-bold text-white mb-4 group-hover:text-green-300 transition-colors duration-200 leading-tight">
{blogPosts[0].title}
</h3>
<p className="text-white/80 mb-6 leading-relaxed line-clamp-3">
{blogPosts[0].excerpt}
</p>
<div className="flex items-center justify-between">
<span className="text-green-300 font-medium group-hover:text-green-200 transition-colors duration-200">
{t.blog.readMore}
</span>
</div>
</div>
</div>
</article>
</Link>
</div>
{/* Other Posts */}
<div className="space-y-8">
{blogPosts.slice(1).map((post, index) => (
<Link key={post.id} href={`/${locale}/blog/${post.id}`}>
<article
className="group cursor-pointer"
onMouseEnter={() => setHoveredPost(post.id)}
onMouseLeave={() => setHoveredPost(null)}
>
<div className="bg-white/10 backdrop-blur-md rounded-2xl overflow-hidden border border-white/20 shadow-xl hover:shadow-2xl transition-all duration-300 transform hover:scale-[1.02]">
<div className="flex">
{/* Post Image */}
<div className="relative w-32 h-32 flex-shrink-0 overflow-hidden">
<div
className={`absolute inset-0 bg-gradient-to-br ${getImageGradient(post.image)} opacity-90`}
>
<div className="absolute inset-0 bg-black/20"></div>
{/* Mini particles */}
<div className="absolute inset-0">
{[...Array(3)].map((_, i) => (
<div
key={i}
className={`absolute w-1 h-1 bg-white/40 rounded-full ${
hoveredPost === post.id
? 'animate-ping'
: 'animate-pulse'
}`}
style={{
left: `${30 + i * 20}%`,
top: `${40 + i * 10}%`,
animationDelay: `${i * 0.3}s`,
}}
/>
))}
</div>
</div>
<div className="absolute top-2 left-2">
<span
className={`${getCategoryColor(post.category)} text-white px-2 py-1 rounded-full text-xs font-medium`}
>
{getCategoryName(post.category)}
</span>
</div>
</div>
{/* Post Content */}
<div className="flex-1 p-6">
<h4 className="text-lg font-bold text-white mb-2 group-hover:text-green-300 transition-colors duration-200 line-clamp-2">
{post.title}
</h4>
<p className="text-white/70 text-sm mb-3 line-clamp-2">
{post.excerpt}
</p>
<div className="flex items-center justify-between">
<div className="text-white/60 text-xs">
<span>{post.author}</span>
<span className="mx-2"></span>
<span>{post.readTime}</span>
</div>
<span className="text-green-300 text-sm font-medium group-hover:text-green-200 transition-colors duration-200">
</span>
</div>
</div>
</div>
</div>
</article>
</Link>
))}
</div>
</div>
{/* Bottom CTA */}
<div className="text-center mt-16">
<div className="bg-gradient-to-r from-green-500/20 to-emerald-500/20 backdrop-blur-md rounded-2xl p-8 border border-green-500/30">
<h3 className="text-2xl font-bold text-white mb-4">{t.blog.stayUpdated}</h3>
<p className="text-white/80 mb-6 max-w-2xl mx-auto">
{t.blog.stayUpdatedDesc}
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link
href={`/${locale}/blog`}
className="bg-green-500 hover:bg-green-600 text-white px-8 py-3 rounded-full font-semibold transition-all duration-200 transform hover:scale-105"
>
{t.blog.exploreAll}
</Link>
<button className="border-2 border-green-500 text-green-400 hover:bg-green-500 hover:text-white px-8 py-3 rounded-full font-semibold transition-all duration-200">
{t.blog.subscribe}
</button>
</div>
</div>
</div>
</div>
</section>
);
}