342 lines
15 KiB
TypeScript
342 lines
15 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useRef } from 'react';
|
|
import Link from 'next/link';
|
|
import { useRouter } from 'next/navigation';
|
|
import Navigation from '../../../../components/Navigation';
|
|
import FloatingLanguageSwitcher from '../../../../components/FloatingLanguageSwitcher';
|
|
import Footer from '../../../../components/Footer';
|
|
import { Locale } from '../../../../lib/i18n';
|
|
import { getTranslations, Translations } from '../../../../lib/translations';
|
|
import { updateDocumentMeta } from '../../../../lib/seo-utils';
|
|
|
|
// Gentle Particle Background Component
|
|
function GentleParticleBackground() {
|
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
|
|
useEffect(() => {
|
|
const canvas = canvasRef.current;
|
|
if (!canvas) return;
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
if (!ctx) return;
|
|
|
|
// Set canvas size
|
|
const resizeCanvas = () => {
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
};
|
|
resizeCanvas();
|
|
window.addEventListener('resize', resizeCanvas);
|
|
|
|
// Gentle particle system
|
|
const particles: Array<{
|
|
x: number;
|
|
y: number;
|
|
vx: number;
|
|
vy: number;
|
|
size: number;
|
|
opacity: number;
|
|
}> = [];
|
|
|
|
// Create particles
|
|
for (let i = 0; i < 60; i++) {
|
|
particles.push({
|
|
x: Math.random() * canvas.width,
|
|
y: Math.random() * canvas.height,
|
|
vx: (Math.random() - 0.5) * 0.2,
|
|
vy: (Math.random() - 0.5) * 0.2,
|
|
size: Math.random() * 2 + 1,
|
|
opacity: Math.random() * 0.3 + 0.1,
|
|
});
|
|
}
|
|
|
|
// Animation loop
|
|
const animate = () => {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
particles.forEach((particle) => {
|
|
// Update position
|
|
particle.x += particle.vx;
|
|
particle.y += particle.vy;
|
|
|
|
// Wrap around edges
|
|
if (particle.x < 0) particle.x = canvas.width;
|
|
if (particle.x > canvas.width) particle.x = 0;
|
|
if (particle.y < 0) particle.y = canvas.height;
|
|
if (particle.y > canvas.height) particle.y = 0;
|
|
|
|
// Draw particle
|
|
ctx.beginPath();
|
|
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
|
|
ctx.fillStyle = `rgba(255, 255, 255, ${particle.opacity})`;
|
|
ctx.fill();
|
|
});
|
|
|
|
requestAnimationFrame(animate);
|
|
};
|
|
|
|
animate();
|
|
|
|
return () => {
|
|
window.removeEventListener('resize', resizeCanvas);
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<canvas
|
|
ref={canvasRef}
|
|
className="absolute inset-0 w-full h-full"
|
|
style={{ pointerEvents: 'none' }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
type Post = Translations['blog']['posts']['featured']; // Assuming all posts have the same structure
|
|
|
|
export default function BlogPostClient({
|
|
locale,
|
|
post,
|
|
t,
|
|
}: {
|
|
locale: Locale;
|
|
post: Post | null;
|
|
t: Translations;
|
|
}) {
|
|
const router = useRouter();
|
|
|
|
// Update meta tags when locale changes
|
|
useEffect(() => {
|
|
updateDocumentMeta(locale);
|
|
}, [locale]);
|
|
|
|
if (!post) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center">
|
|
<div className="text-center">
|
|
<h1 className="text-4xl font-bold text-white mb-4">Post Not Found</h1>
|
|
<Link
|
|
href={`/${locale}/blog`}
|
|
className="text-green-400 hover:text-green-300 underline"
|
|
>
|
|
Back to Blog
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
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;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen relative overflow-hidden">
|
|
{/* Gentle Background with Overlay */}
|
|
<div
|
|
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
|
|
style={{
|
|
backgroundImage: `linear-gradient(rgba(30, 58, 138, 0.8), rgba(15, 23, 42, 0.9)), url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800"><defs><linearGradient id="gentle1" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:%234F46E5;stop-opacity:1" /><stop offset="50%" style="stop-color:%237C3AED;stop-opacity:1" /><stop offset="100%" style="stop-color:%231E40AF;stop-opacity:1" /></linearGradient></defs><rect width="1200" height="800" fill="url(%23gentle1)"/><path d="M0,400 Q300,300 600,350 T1200,300 L1200,800 L0,800 Z" fill="%23312E81" opacity="0.5"/><path d="M0,500 Q400,400 800,450 T1200,400 L1200,800 L0,800 Z" fill="%231E3A8A" opacity="0.3"/></svg>')`,
|
|
}}
|
|
/>
|
|
|
|
{/* Gentle Particle Background */}
|
|
<GentleParticleBackground />
|
|
|
|
{/* Navigation */}
|
|
<Navigation locale={locale} />
|
|
|
|
{/* Floating Language Switcher */}
|
|
<FloatingLanguageSwitcher locale={locale} />
|
|
|
|
{/* Article Content */}
|
|
<article className="relative z-10 pt-32 pb-20 px-4">
|
|
<div className="max-w-4xl mx-auto">
|
|
{/* Back to Blog Link */}
|
|
<div className="mb-8">
|
|
<Link
|
|
href={`/${locale}/blog`}
|
|
className="inline-flex items-center text-white/80 hover:text-white transition-colors duration-200"
|
|
>
|
|
<svg
|
|
className="w-5 h-5 mr-2"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M15 19l-7-7 7-7"
|
|
/>
|
|
</svg>
|
|
{t.blog.backToBlog}
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Article Header */}
|
|
<header className="mb-12">
|
|
<div className="mb-6">
|
|
<span
|
|
className={`${getCategoryColor(post.category)} text-white px-4 py-2 rounded-full text-sm font-medium`}
|
|
>
|
|
{getCategoryName(post.category)}
|
|
</span>
|
|
</div>
|
|
|
|
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6 leading-tight">
|
|
{post.title}
|
|
</h1>
|
|
|
|
<div className="flex flex-wrap items-center text-white/70 text-sm space-x-6 mb-6">
|
|
<div className="flex items-center space-x-2">
|
|
<svg
|
|
className="w-4 h-4"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
|
/>
|
|
</svg>
|
|
<span>{post.author}</span>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<svg
|
|
className="w-4 h-4"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
|
/>
|
|
</svg>
|
|
<span>{post.date}</span>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<svg
|
|
className="w-4 h-4"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
/>
|
|
</svg>
|
|
<span>{post.readTime}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="text-xl text-white/90 leading-relaxed">{post.excerpt}</p>
|
|
</header>
|
|
|
|
{/* Article Body */}
|
|
<div className="bg-white/10 backdrop-blur-md rounded-3xl p-8 md:p-12 border border-white/20 shadow-2xl">
|
|
<div className="prose prose-lg prose-invert max-w-none">
|
|
{post.content.split('\n\n').map((paragraph, index) => (
|
|
<p key={index} className="text-white/90 leading-relaxed mb-6 text-lg">
|
|
{paragraph}
|
|
</p>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Article Footer */}
|
|
<footer className="mt-12 pt-8 border-t border-white/20">
|
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-center space-y-4 md:space-y-0">
|
|
<div className="flex items-center space-x-4">
|
|
<div className="w-12 h-12 bg-gradient-to-br from-green-400 to-blue-500 rounded-full flex items-center justify-center">
|
|
<span className="text-white font-bold text-lg">
|
|
{post.author.charAt(0)}
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<p className="text-white font-medium">{post.author}</p>
|
|
<p className="text-white/70 text-sm">
|
|
Environmental Writer
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex space-x-4">
|
|
<button className="bg-white/20 hover:bg-white/30 text-white px-6 py-2 rounded-full transition-colors duration-200">
|
|
Share
|
|
</button>
|
|
<button className="bg-green-500 hover:bg-green-600 text-white px-6 py-2 rounded-full transition-colors duration-200">
|
|
Like
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
{/* Related Posts Section */}
|
|
<section className="mt-16">
|
|
<h3 className="text-2xl font-bold text-white mb-8">Related Posts</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<Link href={`/${locale}/blog/post1`}>
|
|
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-6 border border-white/20 hover:bg-white/20 transition-all duration-200 cursor-pointer">
|
|
<h4 className="text-white font-semibold mb-2">
|
|
Player Spotlight: Building a Virtual Eco-City
|
|
</h4>
|
|
<p className="text-white/70 text-sm">
|
|
Meet Alex, a player who created an incredible sustainable
|
|
city...
|
|
</p>
|
|
</div>
|
|
</Link>
|
|
<Link href={`/${locale}/blog/post2`}>
|
|
<div className="bg-white/10 backdrop-blur-md rounded-2xl p-6 border border-white/20 hover:bg-white/20 transition-all duration-200 cursor-pointer">
|
|
<h4 className="text-white font-semibold mb-2">
|
|
10 Amazing Facts About Ocean Conservation
|
|
</h4>
|
|
<p className="text-white/70 text-sm">
|
|
Dive into fascinating facts about our oceans...
|
|
</p>
|
|
</div>
|
|
</Link>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</article>
|
|
|
|
{/* Footer */}
|
|
<Footer locale={locale} />
|
|
</div>
|
|
);
|
|
}
|