145 lines
8.0 KiB
TypeScript
145 lines
8.0 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useMemo } from 'react';
|
||
import ArticleCard from "@/components/articlecard";
|
||
import { articles } from "@/lib/data";
|
||
import { Search, BookOpen, FileText } from "lucide-react";
|
||
|
||
// رابط کاربری مقالات (فیلد category اضافه شد)
|
||
interface Article {
|
||
title: string;
|
||
image: string;
|
||
excerpt: string;
|
||
date: string;
|
||
category?: string; // این فیلد را در صورت امکان به دیتای خود در lib/data.ts اضافه کنید
|
||
}
|
||
|
||
export default function BlogListingPage() {
|
||
// ۱. استیتهای مربوط به فیلتر و جستجو
|
||
const [activeCategory, setActiveCategory] = useState<string>('همه');
|
||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||
|
||
// ۲. استخراج خودکار دستهبندیهای یکتا از دیتای مقالات
|
||
const categories = useMemo(() => {
|
||
// اگر مقالهای دستهبندی نداشت، آن را در 'سایر' قرار میدهیم
|
||
const allCategories = articles.map(article => article.category || 'سایر');
|
||
const uniqueCategories = Array.from(new Set(allCategories));
|
||
return ['همه', ...uniqueCategories];
|
||
}, []);
|
||
|
||
// ۳. فیلتر کردن مقالات بر اساس دستهبندی و متن جستجو
|
||
const filteredArticles = useMemo(() => {
|
||
return articles.filter((article) => {
|
||
const articleCategory = article.category || 'سایر';
|
||
const matchesCategory = activeCategory === 'همه' || articleCategory === activeCategory;
|
||
const matchesSearch =
|
||
article.title.includes(searchQuery) ||
|
||
article.excerpt.includes(searchQuery);
|
||
|
||
return matchesCategory && matchesSearch;
|
||
});
|
||
}, [activeCategory, searchQuery]);
|
||
|
||
return (
|
||
<main className="min-h-screen bg-gray-50/30 pb-20">
|
||
{/*
|
||
بخش هدر (Hero Section)
|
||
استفاده از گرادیانت ملایم و استایلهای مدرن
|
||
*/}
|
||
<div className="bg-gradient-to-b from-gray-50/80 to-transparent pt-12 pb-8 mb-8 border-b border-gray-100">
|
||
<div className="container mx-auto px-4 max-w-6xl">
|
||
<div className="flex flex-col md:flex-row items-center justify-between gap-6">
|
||
<div className="text-center md:text-right">
|
||
<h1 className="text-3xl md:text-4xl font-black text-gray-800 mb-4 flex items-center justify-center md:justify-start gap-3">
|
||
<BookOpen className="text-[#ffb900]" size={32} />
|
||
وبلاگ و مقالات آموزشی
|
||
</h1>
|
||
<p className="text-gray-500 text-xs max-w-2xl">
|
||
جدیدترین اخبار، آموزشها، ترفندها و مقالات تخصصی در حوزه صنعت و قطعات را اینجا بخوانید.
|
||
</p>
|
||
</div>
|
||
|
||
{/* باکس آمار مقالات */}
|
||
<div className="hidden lg:flex items-center gap-4 bg-white p-4 rounded-2xl shadow-sm border border-gray-100">
|
||
<div className="bg-[#ffb900] p-3 rounded-xl ">
|
||
<FileText size={24} />
|
||
</div>
|
||
<div>
|
||
<p className="text-xs text-gray-400 font-bold mb-1">تعداد کل مقالات</p>
|
||
<p className="text-xl font-black text-gray-800">{articles.length} مقاله</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="container mx-auto px-4 max-w-6xl">
|
||
|
||
{/* بخش فیلترها و جستجو */}
|
||
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-6 mb-10 bg-white p-4 rounded-3xl shadow-sm border border-gray-100">
|
||
|
||
{/* فیلتر دستهبندی (قابلیت اسکرول افقی در موبایل با مخفی کردن نوار اسکرول) */}
|
||
<div className="flex w-full lg:w-auto overflow-x-auto gap-2 pb-2 lg:pb-0 [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]">
|
||
{categories.map((category) => (
|
||
<button
|
||
key={category}
|
||
onClick={() => setActiveCategory(category)}
|
||
className={`
|
||
whitespace-nowrap px-5 py-2.5 rounded-full text-sm font-bold transition-all
|
||
${activeCategory === category
|
||
? 'bg-[#ffb900] text-white '
|
||
: 'bg-gray-50 text-gray-600 hover:bg-gray-100 border border-gray-100'}
|
||
`}
|
||
>
|
||
{category}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
{/* باکس جستجو */}
|
||
<div className="relative w-full lg:w-80 shrink-0">
|
||
<Search className="absolute right-4 top-1/2 -translate-y-1/2 text-gray-400" size={18} />
|
||
<input
|
||
type="text"
|
||
placeholder="جستجو در مقالات..."
|
||
value={searchQuery}
|
||
onChange={(e) => setSearchQuery(e.target.value)}
|
||
className="w-full bg-gray-50 border border-gray-100 rounded-full py-3 pr-11 pl-4 text-sm font-medium text-gray-700 placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 focus:bg-white transition-all"
|
||
/>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
{/* گرید مقالات */}
|
||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 gap-2 md:gap-5">
|
||
{filteredArticles.map((article) => (
|
||
<div key={article.title} className="transform hover:-translate-y-1 transition-transform duration-300">
|
||
<ArticleCard article={article} />
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* حالت خالی (وقتی مقالهای با فیلتر یا جستجو پیدا نشد) */}
|
||
{filteredArticles.length === 0 && (
|
||
<div className="flex flex-col justify-center items-center py-20 bg-white rounded-3xl border border-gray-100 border-dashed mt-8 text-center px-4">
|
||
<div className="bg-gray-50 p-6 rounded-full mb-4">
|
||
<Search size={40} className="text-gray-300" />
|
||
</div>
|
||
<h3 className="text-xl font-bold text-gray-700 mb-2">مقالهای پیدا نشد!</h3>
|
||
<p className="text-gray-500 text-sm">
|
||
با کلمه جستجو شده یا دستهبندی انتخاب شده، مقالهای وجود ندارد. لطفا فیلترها را تغییر دهید.
|
||
</p>
|
||
<button
|
||
onClick={() => { setActiveCategory('همه'); setSearchQuery(''); }}
|
||
className="mt-6 px-6 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 font-semibold rounded-xl transition-colors text-sm"
|
||
>
|
||
پاک کردن فیلترها
|
||
</button>
|
||
</div>
|
||
)}
|
||
|
||
</div>
|
||
</main>
|
||
);
|
||
}
|