Files
ayristech_new/components/BlogClient.tsx
T
2026-05-30 18:23:21 +03:00

197 lines
8.3 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 { motion, AnimatePresence } from "framer-motion";
import { useState } from "react";
import Link from "next/link";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import type { Locale } from "@/i18n-config";
import { blogPosts, BlogPost } from "@/data/blog";
const expo = [0.16, 1, 0.3, 1] as [number, number, number, number];
export default function BlogClient({
lang,
dict,
initialPosts,
}: {
lang: Locale;
dict: any;
initialPosts?: BlogPost[];
}) {
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const [searchQuery, setSearchQuery] = useState("");
const postsList = initialPosts && initialPosts.length > 0 ? initialPosts : blogPosts;
const categories = Array.from(
new Set(
postsList.map((post) => {
const content = post[lang] || post.en;
return (content.category as string).split(" · ")[0]; // Main category e.g. YZ, Web3, Web
})
)
);
const filteredPosts = postsList.filter((post) => {
const content = post[lang] || post.en;
const matchesCategory =
!selectedCategory || (content.category as string).startsWith(selectedCategory);
const matchesSearch =
(content.title as string).toLowerCase().includes(searchQuery.toLowerCase()) ||
(content.excerpt as string).toLowerCase().includes(searchQuery.toLowerCase()) ||
(content.tags as string[]).some((t: string) => t.toLowerCase().includes(searchQuery.toLowerCase()));
return matchesCategory && matchesSearch;
});
return (
<div className="bg-[#F4F0E8] text-[#0A0A0A] font-body min-h-screen">
<Header lang={lang} dict={dict.nav} />
<main className="pt-32 pb-24 px-6 max-w-7xl mx-auto">
{/* Header Block */}
<div className="border-b border-[#C8C2B8] pb-12 mb-12">
<div className="flex items-center gap-3 mb-6">
<div className="w-3 h-3 bg-[#FFE600]" />
<span className="font-mono text-[10px] tracking-[0.3em] uppercase text-[#A0998E]">
{dict.blog.badge}
</span>
</div>
<motion.h1
className="font-display font-black text-5xl sm:text-6xl lg:text-8xl uppercase leading-[0.85] tracking-tight"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: expo }}
>
{dict.blog.title}
</motion.h1>
<p className="text-[#6A6460] mt-6 text-lg max-w-2xl font-light">
{dict.blog.desc}
</p>
</div>
{/* Filter Controls & Search */}
<div className="flex flex-col md:flex-row gap-6 justify-between items-stretch md:items-center mb-12">
{/* Categories */}
<div className="flex flex-wrap gap-2">
<button
onClick={() => setSelectedCategory(null)}
className={`font-mono text-[10px] tracking-[0.15em] uppercase px-4 py-2 border transition-all cursor-pointer ${
selectedCategory === null
? "bg-[#0A0A0A] text-[#F4F0E8] border-[#0A0A0A]"
: "border-[#C8C2B8] text-[#6A6460] hover:border-[#0A0A0A] hover:text-[#0A0A0A]"
}`}
>
{lang === "tr" ? "HEPSİ" : "ALL"}
</button>
{categories.map((cat) => (
<button
key={cat}
onClick={() => setSelectedCategory(cat)}
className={`font-mono text-[10px] tracking-[0.15em] uppercase px-4 py-2 border transition-all cursor-pointer ${
selectedCategory === cat
? "bg-[#0A0A0A] text-[#F4F0E8] border-[#0A0A0A]"
: "border-[#C8C2B8] text-[#6A6460] hover:border-[#0A0A0A] hover:text-[#0A0A0A]"
}`}
>
{cat}
</button>
))}
</div>
{/* Search Box */}
<div className="relative flex-grow md:max-w-xs">
<input
type="text"
placeholder={lang === "tr" ? "Arama yap..." : "Search..."}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full font-mono text-[11px] bg-[#EDE8E0] border border-[#C8C2B8] hover:border-[#0A0A0A] focus:border-[#0A0A0A] outline-none px-4 py-3 placeholder-[#A0998E] text-[#0A0A0A] transition-colors"
/>
{searchQuery && (
<button
onClick={() => setSearchQuery("")}
className="absolute right-3 top-1/2 -translate-y-1/2 text-[10px] font-mono text-[#A0998E] hover:text-[#0A0A0A] cursor-pointer"
>
[X]
</button>
)}
</div>
</div>
{/* Grid List */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<AnimatePresence mode="popLayout">
{filteredPosts.map((post, idx) => {
const content = post[lang] || post.en;
const accent = ["#FFE600", "#FF4500", "#00CC77"][idx % 3] || "#FFE600";
return (
<motion.div
key={post.slug}
layout
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.4, ease: expo }}
className="group flex flex-col bg-[#F4F0E8] border border-[#C8C2B8] hover:border-[#0A0A0A] p-8 relative overflow-hidden transition-all duration-300 h-full"
whileHover={{ y: -6, backgroundColor: "#EDE8E0", boxShadow: "4px 4px 0px 0px #0A0A0A" }}
>
<div className="flex items-center gap-3 mb-6">
<span
className="font-mono text-[9px] tracking-[0.2em] uppercase px-2.5 py-1 border border-[#C8C2B8] text-[#6A6460]"
style={{ borderLeftColor: accent, borderLeftWidth: 3 }}
>
{content.category}
</span>
<span className="font-mono text-[9px] text-[#A0998E]">
{post.date}
</span>
</div>
<h3 className="font-display font-black text-xl uppercase text-[#0A0A0A] mb-4 leading-tight">
{content.title}
</h3>
<p className="text-[#6A6460] text-[13px] leading-relaxed mb-8 flex-grow">
{content.excerpt}
</p>
<div className="pt-6 border-t border-[#C8C2B8] flex items-center justify-between mt-auto">
<div className="flex flex-col">
<span className="font-display font-black text-[11px] uppercase text-[#0A0A0A]">{post.author}</span>
<span className="font-mono text-[9px] text-[#A0998E] uppercase tracking-wider">{post.authorRole}</span>
</div>
<Link
href={`/${lang}/blog/${post.slug}`}
className="w-10 h-10 border border-[#C8C2B8] group-hover:border-[#0A0A0A] group-hover:bg-[#0A0A0A] group-hover:text-[#FFE600] text-[#0A0A0A] flex items-center justify-center transition-all"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<line x1="5" y1="12" x2="19" y2="12" />
<polyline points="12 5 19 12 12 19" />
</svg>
</Link>
</div>
{/* Category Accent Stripe */}
<div className="absolute top-0 left-0 right-0 h-1 transition-colors" style={{ backgroundColor: accent }} />
</motion.div>
);
})}
</AnimatePresence>
{filteredPosts.length === 0 && (
<div className="col-span-full py-16 text-center border border-dashed border-[#C8C2B8]">
<p className="font-mono text-xs uppercase text-[#A0998E]">
{lang === "tr" ? "Sonuç bulunamadı." : "No entries match your parameters."}
</p>
</div>
)}
</div>
</main>
<Footer lang={lang} dict={dict} />
</div>
);
}