443 lines
20 KiB
TypeScript
443 lines
20 KiB
TypeScript
"use client";
|
||
import React, { useState, useMemo } from "react";
|
||
import { motion, AnimatePresence } from "motion/react";
|
||
import {
|
||
ChevronRight,
|
||
ChevronLeft,
|
||
Calendar,
|
||
User,
|
||
ArrowLeft,
|
||
Sparkles,
|
||
BookOpen,
|
||
Mail,
|
||
Check,
|
||
Clock,
|
||
Tag,
|
||
Search,
|
||
Pin,
|
||
Eye,
|
||
Filter,
|
||
} from "lucide-react";
|
||
import { toast } from "sonner";
|
||
import { Product } from "../lib/data/products";
|
||
import { productService } from "../lib/services/productService";
|
||
import { useRouter } from 'next/navigation';
|
||
import Image from 'next/image';
|
||
import SafeImage from './SafeImage';
|
||
|
||
export interface BlogCategory {
|
||
id: string;
|
||
name: string;
|
||
slug: string;
|
||
}
|
||
|
||
export interface BlogPostItem {
|
||
id: string;
|
||
slug: string;
|
||
title: string;
|
||
excerpt: string;
|
||
image: string;
|
||
imageAlt?: string;
|
||
date: string;
|
||
author: string;
|
||
content: string;
|
||
category?: string;
|
||
categoryId?: string;
|
||
tags?: { id: string; name: string }[];
|
||
readingTime?: number;
|
||
featured?: boolean;
|
||
viewCount?: number;
|
||
}
|
||
|
||
export default function BlogPage({
|
||
blogs = [],
|
||
categories = [],
|
||
}: {
|
||
blogs: BlogPostItem[];
|
||
categories?: BlogCategory[];
|
||
}) {
|
||
const router = useRouter();
|
||
const [products, setProducts] = useState<Product[]>([]);
|
||
const [selectedCategory, setSelectedCategory] = useState<string>("ALL");
|
||
const [searchQuery, setSearchQuery] = useState<string>("");
|
||
const [selectedTag, setSelectedTag] = useState<string | null>(null);
|
||
|
||
React.useEffect(() => {
|
||
productService.getProducts({ limit: 8 }).then((res) => setProducts(res.data));
|
||
}, []);
|
||
|
||
// Filtered blogs list
|
||
const filteredBlogs = useMemo(() => {
|
||
return blogs.filter((b) => {
|
||
const matchCategory =
|
||
selectedCategory === "ALL" ||
|
||
b.categoryId === selectedCategory ||
|
||
b.category === selectedCategory;
|
||
const matchSearch =
|
||
!searchQuery.trim() ||
|
||
b.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||
b.excerpt.toLowerCase().includes(searchQuery.toLowerCase());
|
||
const matchTag =
|
||
!selectedTag ||
|
||
(b.tags && b.tags.some((t) => t.name === selectedTag));
|
||
|
||
return matchCategory && matchSearch && matchTag;
|
||
});
|
||
}, [blogs, selectedCategory, searchQuery, selectedTag]);
|
||
|
||
// Featured post (prioritize featured flag, otherwise the first item)
|
||
const featuredPost = useMemo(() => {
|
||
if (filteredBlogs.length === 0) return null;
|
||
const explicitlyFeatured = filteredBlogs.find((b) => b.featured);
|
||
return explicitlyFeatured || filteredBlogs[0];
|
||
}, [filteredBlogs]);
|
||
|
||
// Regular posts excluding featured if displayed prominently
|
||
const regularPosts = useMemo(() => {
|
||
if (!featuredPost) return filteredBlogs;
|
||
return filteredBlogs.filter((b) => b.id !== featuredPost.id);
|
||
}, [filteredBlogs, featuredPost]);
|
||
|
||
// Extract all unique tags
|
||
const allTags = useMemo(() => {
|
||
const map = new Map<string, string>();
|
||
blogs.forEach((b) => {
|
||
b.tags?.forEach((t) => {
|
||
map.set(t.name, t.name);
|
||
});
|
||
});
|
||
return Array.from(map.values());
|
||
}, [blogs]);
|
||
|
||
return (
|
||
<div className="min-h-screen bg-medical-gray-50 pt-8 pb-24 px-4 font-vazir" dir="rtl">
|
||
<div className="max-w-7xl mx-auto">
|
||
{/* Breadcrumb Navigation */}
|
||
<div className="flex items-center justify-between mb-8 pb-4 border-b border-medical-gray-200/60 font-vazir">
|
||
<div className="flex items-center gap-2 text-xs font-bold text-medical-gray-400">
|
||
<span className="cursor-pointer hover:text-canina-blue transition-colors" onClick={() => router.push('/')}>
|
||
خانه
|
||
</span>
|
||
<ChevronRight className="w-3 h-3 text-medical-gray-300" />
|
||
<span className="text-canina-blue font-black">مجله سلامت و مقالات</span>
|
||
</div>
|
||
|
||
<button
|
||
onClick={() => router.push('/')}
|
||
className="px-3.5 py-1.5 bg-white border border-medical-gray-200 rounded-lg text-medical-gray-600 shadow-xs flex items-center gap-1.5 font-bold text-xs hover:border-canina-blue hover:text-canina-blue transition-all whitespace-nowrap cursor-pointer"
|
||
>
|
||
<span>بازگشت به خانه</span>
|
||
<ChevronLeft className="w-3.5 h-3.5" />
|
||
</button>
|
||
</div>
|
||
|
||
{/* Page Header */}
|
||
<div className="mb-10 text-right">
|
||
<div className="inline-flex items-center gap-2 px-3.5 py-1.5 bg-canina-blue/10 text-canina-blue rounded-full text-xs font-black uppercase tracking-widest mb-3 shadow-2xs">
|
||
<BookOpen className="w-4 h-4" />
|
||
<span>دانشنامه علمی و درمانی کنینا آلمان</span>
|
||
</div>
|
||
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-black text-medical-gray-900 leading-tight">
|
||
مجله تخصصی <span className="text-canina-blue">سلامت و تغذیه سگ و گربه</span>
|
||
</h1>
|
||
<p className="text-sm sm:text-base text-medical-gray-500 font-medium mt-3 max-w-2xl leading-relaxed">
|
||
جدیدترین یافتههای بالینی، مقالات تخصصی دامپزشکی و پروتکلهای پیشگیری و درمان بر پایه استانداردهای علمی کنینا آلمان.
|
||
</p>
|
||
</div>
|
||
|
||
{/* Search & Category Filter Section */}
|
||
<div className="bg-white p-6 rounded-3xl border border-medical-gray-200 shadow-sm mb-12 space-y-5">
|
||
<div className="flex flex-col md:flex-row items-center gap-4">
|
||
{/* Search Input */}
|
||
<div className="relative flex-1 w-full">
|
||
<Search className="w-4 h-4 absolute right-4 top-1/2 -translate-y-1/2 text-medical-gray-400" />
|
||
<input
|
||
type="text"
|
||
placeholder="جستجو در موضوعات، بیماریها، ویتامینها..."
|
||
value={searchQuery}
|
||
onChange={(e) => setSearchQuery(e.target.value)}
|
||
className="w-full pl-4 pr-11 py-3 rounded-2xl bg-medical-gray-50 border border-medical-gray-200 focus:border-canina-blue focus:bg-white text-xs sm:text-sm font-bold outline-none transition-all"
|
||
/>
|
||
{searchQuery && (
|
||
<button
|
||
onClick={() => setSearchQuery("")}
|
||
className="absolute left-3 top-1/2 -translate-y-1/2 text-xs font-bold text-medical-gray-400 hover:text-medical-gray-600"
|
||
>
|
||
پاک کردن
|
||
</button>
|
||
)}
|
||
</div>
|
||
|
||
{selectedTag && (
|
||
<div className="flex items-center gap-2 px-3 py-2 bg-purple-50 text-purple-700 border border-purple-200 rounded-xl text-xs font-bold">
|
||
<span>تگ انتخاب شده: #{selectedTag}</span>
|
||
<button
|
||
onClick={() => setSelectedTag(null)}
|
||
className="text-purple-500 hover:text-purple-900"
|
||
>
|
||
✕
|
||
</button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Category Filter Pills */}
|
||
<div className="flex items-center gap-2 overflow-x-auto pb-1 scrollbar-none">
|
||
<button
|
||
onClick={() => setSelectedCategory("ALL")}
|
||
className={`px-4 py-2 rounded-2xl text-xs font-black transition-all cursor-pointer whitespace-nowrap ${
|
||
selectedCategory === "ALL"
|
||
? "bg-canina-blue text-white shadow-md shadow-canina-blue/20 scale-102"
|
||
: "bg-medical-gray-100 text-medical-gray-600 hover:bg-medical-gray-200"
|
||
}`}
|
||
>
|
||
همه دستهها ({blogs.length})
|
||
</button>
|
||
{categories.map((c) => (
|
||
<button
|
||
key={c.id}
|
||
onClick={() => setSelectedCategory(c.id)}
|
||
className={`px-4 py-2 rounded-2xl text-xs font-black transition-all cursor-pointer whitespace-nowrap ${
|
||
selectedCategory === c.id
|
||
? "bg-canina-blue text-white shadow-md shadow-canina-blue/20 scale-102"
|
||
: "bg-medical-gray-100 text-medical-gray-600 hover:bg-medical-gray-200"
|
||
}`}
|
||
>
|
||
{c.name}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
{/* Quick Tag Cloud */}
|
||
{allTags.length > 0 && (
|
||
<div className="flex flex-wrap items-center gap-1.5 pt-2 border-t border-medical-gray-100">
|
||
<span className="text-[11px] font-bold text-medical-gray-400 flex items-center gap-1 ml-2">
|
||
<Tag className="w-3 h-3" />
|
||
محبوبترین تگها:
|
||
</span>
|
||
{allTags.slice(0, 10).map((tag) => (
|
||
<button
|
||
key={tag}
|
||
onClick={() => setSelectedTag(selectedTag === tag ? null : tag)}
|
||
className={`text-[11px] font-bold px-2.5 py-1 rounded-lg transition-all cursor-pointer ${
|
||
selectedTag === tag
|
||
? "bg-purple-600 text-white"
|
||
: "bg-medical-gray-100 text-medical-gray-600 hover:bg-medical-gray-200"
|
||
}`}
|
||
>
|
||
#{tag}
|
||
</button>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Featured Post Banner */}
|
||
{featuredPost && (
|
||
<div className="mb-16">
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 20 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
className="bg-white rounded-[3.5rem] overflow-hidden border border-medical-gray-200 shadow-2xl flex flex-col lg:flex-row cursor-pointer group hover:border-canina-blue/30 transition-all"
|
||
onClick={() => router.push(`/blog/${featuredPost.slug}`)}
|
||
>
|
||
<div className="lg:w-1/2 aspect-video lg:aspect-auto relative overflow-hidden bg-medical-gray-100">
|
||
<SafeImage
|
||
src={featuredPost.image}
|
||
alt={featuredPost.imageAlt || featuredPost.title}
|
||
priority
|
||
sizes="(max-width: 1024px) 100vw, 50vw"
|
||
className="w-full h-full"
|
||
imgClassName="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700"
|
||
/>
|
||
{featuredPost.featured && (
|
||
<div className="absolute top-6 right-6 px-3.5 py-1.5 bg-amber-500 text-white rounded-full text-xs font-black flex items-center gap-1.5 shadow-lg backdrop-blur-md">
|
||
<Pin className="w-3.5 h-3.5 fill-white" />
|
||
<span>مقاله ویژه و برگزیده</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div className="lg:w-1/2 p-8 sm:p-12 flex flex-col justify-center">
|
||
<div className="flex flex-wrap items-center gap-3 text-xs font-bold text-canina-blue mb-5">
|
||
<div className="bg-canina-blue/10 px-3.5 py-1 rounded-full uppercase tracking-wider">
|
||
{featuredPost.category || "پژوهش بالینی"}
|
||
</div>
|
||
<div className="flex items-center gap-1 text-medical-gray-400">
|
||
<Calendar className="w-3.5 h-3.5" />
|
||
{featuredPost.date}
|
||
</div>
|
||
<div className="flex items-center gap-1 text-medical-gray-400">
|
||
<Clock className="w-3.5 h-3.5" />
|
||
{featuredPost.readingTime || 4} دقیقه مطالعه
|
||
</div>
|
||
</div>
|
||
<h2 className="text-2xl sm:text-3xl lg:text-4xl font-black text-medical-gray-900 mb-5 leading-tight group-hover:text-canina-blue transition-colors">
|
||
{featuredPost.title}
|
||
</h2>
|
||
<p className="text-sm sm:text-base text-medical-gray-500 mb-8 leading-relaxed line-clamp-3">
|
||
{featuredPost.excerpt}
|
||
</p>
|
||
<div className="flex items-center justify-between pt-6 border-t border-medical-gray-100">
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-10 h-10 bg-medical-gray-100 rounded-full flex items-center justify-center text-medical-gray-400">
|
||
<User className="w-5 h-5" />
|
||
</div>
|
||
<div className="text-xs">
|
||
<div className="font-black text-medical-gray-900">{featuredPost.author}</div>
|
||
<div className="font-bold text-medical-gray-400">تیم پژوهش کنینا</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2 text-canina-blue font-black text-sm">
|
||
<span>مطالعه مقاله کامل</span>
|
||
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-2 transition-transform" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Regular Posts Grid */}
|
||
{regularPosts.length > 0 ? (
|
||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||
{regularPosts.map((post, idx) => (
|
||
<motion.div
|
||
key={post.id}
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ delay: idx * 0.05 }}
|
||
className="bg-white rounded-[2.5rem] overflow-hidden border border-medical-gray-200 shadow-lg group hover:shadow-2xl hover:border-canina-blue/30 transition-all cursor-pointer flex flex-col justify-between"
|
||
onClick={() => router.push(`/blog/${post.slug}`)}
|
||
>
|
||
<div>
|
||
<div className="aspect-[16/10] overflow-hidden relative bg-medical-gray-100">
|
||
<SafeImage
|
||
src={post.image}
|
||
alt={post.imageAlt || post.title}
|
||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||
className="w-full h-full"
|
||
imgClassName="w-full h-full object-cover group-hover:scale-108 transition-transform duration-700"
|
||
/>
|
||
{post.category && (
|
||
<span className="absolute bottom-3 right-3 px-3 py-1 bg-black/60 backdrop-blur-md text-white rounded-xl text-[10px] font-bold z-10">
|
||
{post.category}
|
||
</span>
|
||
)}
|
||
</div>
|
||
<div className="p-6 sm:p-7">
|
||
<div className="flex items-center justify-between text-[11px] font-bold text-medical-gray-400 mb-3">
|
||
<div className="flex items-center gap-1">
|
||
<Calendar className="w-3 h-3" />
|
||
{post.date}
|
||
</div>
|
||
<div className="flex items-center gap-1">
|
||
<Clock className="w-3 h-3" />
|
||
{post.readingTime || 3} دقیقه
|
||
</div>
|
||
</div>
|
||
<h3 className="text-lg font-black text-medical-gray-900 mb-3 group-hover:text-canina-blue transition-colors line-clamp-2 leading-snug">
|
||
{post.title}
|
||
</h3>
|
||
<p className="text-xs text-medical-gray-500 line-clamp-3 leading-relaxed mb-4">
|
||
{post.excerpt}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="px-6 sm:px-7 pb-6 pt-3 border-t border-medical-gray-100 flex items-center justify-between">
|
||
<div className="text-[11px] font-bold text-medical-gray-500 flex items-center gap-1.5">
|
||
<User className="w-3.5 h-3.5 text-medical-gray-400" />
|
||
<span>{post.author}</span>
|
||
</div>
|
||
<div className="flex items-center gap-1 text-canina-blue font-black text-xs group-hover:underline">
|
||
<span>مطالعه</span>
|
||
<ArrowLeft className="w-3.5 h-3.5 group-hover:-translate-x-1 transition-transform" />
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
))}
|
||
</div>
|
||
) : (
|
||
<div className="text-center py-16 bg-white rounded-3xl border border-medical-gray-200">
|
||
<p className="text-medical-gray-500 font-bold text-sm">
|
||
هیچ مقالهای با معیارهای جستجوی شما یافت نشد.
|
||
</p>
|
||
<button
|
||
onClick={() => {
|
||
setSelectedCategory("ALL");
|
||
setSearchQuery("");
|
||
setSelectedTag(null);
|
||
}}
|
||
className="mt-3 text-xs text-canina-blue font-black hover:underline cursor-pointer"
|
||
>
|
||
پاکسازی همه فیلترها
|
||
</button>
|
||
</div>
|
||
)}
|
||
|
||
{/* Newsletter Section */}
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 30 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.7 }}
|
||
className="mt-20 bg-gradient-to-br from-canina-blue via-canina-blue/95 to-medical-gray-900 rounded-[3rem] p-8 sm:p-14 text-white relative overflow-hidden font-vazir shadow-2xl border border-white/10"
|
||
dir="rtl"
|
||
>
|
||
<div className="absolute top-0 right-0 w-96 h-96 bg-canina-teal/15 rounded-full blur-[100px] pointer-events-none" />
|
||
<div className="absolute bottom-0 left-0 w-80 h-80 bg-canina-gold/10 rounded-full blur-[90px] pointer-events-none" />
|
||
|
||
<div className="relative z-10 max-w-4xl mx-auto flex flex-col items-center text-center space-y-8">
|
||
<div className="inline-flex items-center gap-2.5 px-4 py-2 bg-white/10 backdrop-blur-md rounded-full border border-white/20 shadow-inner">
|
||
<div className="w-6 h-6 rounded-lg bg-canina-gold text-canina-dark flex items-center justify-center font-black text-xs italic shadow-md">
|
||
C
|
||
</div>
|
||
<span className="text-xs font-black text-white/90 tracking-wider font-vazir">
|
||
خبرنامه علمی و تخصصی کنینا آلمان
|
||
</span>
|
||
<Sparkles className="w-3.5 h-3.5 text-canina-gold animate-pulse" />
|
||
</div>
|
||
|
||
<div className="space-y-4 max-w-2xl">
|
||
<h3 className="text-3xl sm:text-4xl font-black font-vazir text-white leading-tight">
|
||
مشترک <span className="text-transparent bg-clip-text bg-gradient-to-r from-canina-gold via-white to-canina-teal">دانشنامه پزشکی</span> شوید
|
||
</h3>
|
||
<p className="text-xs sm:text-sm text-white/80 font-medium font-vazir leading-relaxed">
|
||
تازهترین یافتههای علمی تغذیه و سلامت حیوانات خانگی را ماهانه در صندوق ایمیل خود دریافت نمایید.
|
||
</p>
|
||
</div>
|
||
|
||
<div className="w-full max-w-xl">
|
||
<form
|
||
onSubmit={(e) => {
|
||
e.preventDefault();
|
||
toast.success("عضویت شما در خبرنامه علمی با موفقیت ثبت شد!");
|
||
}}
|
||
className="w-full bg-white/15 backdrop-blur-xl border border-white/25 p-2 rounded-2xl sm:rounded-full flex flex-col sm:flex-row items-stretch sm:items-center gap-2 sm:gap-3 shadow-2xl hover:border-canina-gold/40 transition-all group"
|
||
dir="ltr"
|
||
>
|
||
<input
|
||
type="email"
|
||
required
|
||
placeholder="آدرس ایمیل شما (name@domain.com)..."
|
||
className="flex-1 bg-transparent border-none text-xs sm:text-sm outline-none text-white placeholder:text-white/45 px-3 sm:px-4 py-2.5 sm:py-0 font-sans text-left"
|
||
/>
|
||
<button
|
||
type="submit"
|
||
className="bg-canina-gold hover:bg-white text-canina-dark px-6 sm:px-8 py-3.5 rounded-xl sm:rounded-full font-black text-xs sm:text-sm transition-all duration-300 whitespace-nowrap shadow-xl hover:scale-105 flex items-center justify-center gap-2 font-vazir cursor-pointer flex-shrink-0"
|
||
dir="rtl"
|
||
>
|
||
<span>عضویت آنی</span>
|
||
<Mail className="w-4 h-4" />
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|