canina/frontend/application/components/FeaturedProducts.tsx

191 lines
9.6 KiB
TypeScript

"use client";
import { useState, useEffect, useMemo } from "react";
import { Product } from "../lib/data/products";
import { usePetStore } from "../lib/store/usePetStore";
import { productService } from "../lib/services/productService";
import { motion, AnimatePresence } from "motion/react";
import { Eye, ShoppingCart, ShieldCheck, Heart, AlertCircle, Sparkles } from "lucide-react";
import SafeImage from "./SafeImage";
import { ProductCardSkeleton } from "./Skeleton";
import Link from 'next/link';
import { useSettingsStore } from "../lib/store/settingsStore";
import { useCartStore } from "../lib/store/cartStore";
function ProductCard({ product, onClick }: { product: Product; key?: string | number; onClick: (p: Product) => void }) {
const router = useRouter();
const getText = useSettingsStore(state => state.getText);
const { getActivePet } = usePetStore();
const activePet = getActivePet();
const { addItem } = useCartStore();
const compatibility = useMemo(() => {
if (!activePet) return null;
const sameSpecies = product.suitableFor === activePet.type || product.suitableFor === "هر دو";
const helpsSymptom = (activePet.medicalConditions || []).some(s => product.symptoms.includes(s));
if (!sameSpecies) return { type: 'alert', text: `مخصوص ${product.suitableFor}` };
if (helpsSymptom) return { type: 'success', text: `توصیه شده برای ${activePet.name}` };
return { type: 'neutral', text: `مناسب برای ${activePet.name}` };
}, [product, activePet]);
return (
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="group bg-white rounded-3xl border border-medical-gray-200 overflow-hidden hover:shadow-2xl hover:shadow-canina-blue/10 transition-all duration-500 flex flex-col h-full cursor-pointer relative"
>
{compatibility && (
<div className={`absolute top-16 right-4 z-10 text-[8px] font-black uppercase tracking-widest px-3 py-1 rounded-full shadow-md flex items-center gap-1 ${
compatibility.type === 'alert' ? 'bg-amber-100 text-amber-700' :
compatibility.type === 'success' ? 'bg-green-100 text-green-700' : 'bg-medical-gray-100 text-medical-gray-600'
}`}>
{compatibility.type === 'alert' ? <AlertCircle className="w-2.5 h-2.5" /> : compatibility.type === 'success' ? <Heart className="w-2.5 h-2.5" /> : <Sparkles className="w-2.5 h-2.5" />}
{compatibility.text}
</div>
)}
{/* Product Image Area */}
<div className="relative aspect-square bg-medical-gray-50 p-10 flex items-center justify-center overflow-hidden">
<SafeImage
src={product.image}
alt={product.name}
className="w-full h-full group-hover:scale-105 transition-transform duration-500 drop-shadow-2xl"
imgClassName="object-contain"
/>
<div className="absolute top-4 right-4 px-3 py-1 bg-white/80 backdrop-blur-md rounded-full border border-medical-gray-200 text-[10px] font-bold text-medical-gray-500 uppercase tracking-widest">
{product.category}
</div>
{/* Main PDP Link */}
<Link href={`/shop/${product.slug || product.id}`} className="absolute inset-0 z-10" />
{/* Hover Actions (z-20 to be clickable over the main PDP link) */}
<div className="absolute inset-0 bg-canina-blue/60 opacity-0 group-hover:opacity-100 backdrop-blur-sm transition-all duration-300 flex items-center justify-center gap-4 z-20">
<div
onClick={(e) => {
e.stopPropagation();
router.push(`/shop/${product.slug || product.id}`);
}}
className="w-12 h-12 rounded-full bg-white text-canina-blue flex items-center justify-center hover:scale-110 transition-transform shadow-lg cursor-pointer"
>
<Eye className="w-5 h-5" />
</div>
<div
onClick={(e) => {
e.stopPropagation();
addItem(product, 1);
toast.success(`${product.name} به سبد خرید شما اضافه شد.`);
}}
className="w-12 h-12 rounded-full bg-canina-blue text-white flex items-center justify-center hover:scale-110 transition-transform shadow-lg border border-white/20 cursor-pointer"
>
<ShoppingCart className="w-5 h-5" />
</div>
</div>
</div>
{/* Content Area */}
<div className="p-8 flex flex-col flex-1" onClick={() => router.push(`/shop/${product.slug || product.id}`)}>
<div className="flex items-start justify-between mb-4 gap-2">
<h3 dir="ltr" className="text-base md:text-lg font-black text-medical-gray-900 group-hover:text-canina-blue transition-colors leading-tight text-left line-clamp-2 flex-1">
{product.name}
</h3>
<ShieldCheck className="w-6 h-6 text-canina-blue flex-shrink-0" />
</div>
<p className="text-sm text-medical-gray-500 leading-relaxed mb-6 flex-1 line-clamp-2">
{product.shortDescription || product.description}
</p>
<div className="space-y-4 pt-4 border-t border-medical-gray-100 mt-auto">
{product.benefits && (
<div className="flex items-center gap-3">
<div className="w-1.5 h-1.5 rounded-full bg-canina-blue" />
<span className="text-xs font-semibold text-medical-gray-700 italic">{product.benefits}</span>
</div>
)}
<div className="flex items-center justify-between">
<div className="text-lg font-black text-medical-gray-900 font-vazir whitespace-nowrap">{product.price}</div>
<button
onClick={(e) => {
e.stopPropagation();
router.push(`/shop/${product.slug || product.id}`);
}}
className="text-xs font-black text-canina-blue uppercase tracking-widest border-b-2 border-canina-blue pb-1 hover:text-medical-gray-900 hover:border-medical-gray-900 transition-all whitespace-nowrap"
>
{getText('product_view_details', "مشاهده جزئیات")}
</button>
</div>
</div>
</div>
</motion.div>
);
}
import { useRouter } from 'next/navigation';
import { toast } from 'sonner';
export default function FeaturedProducts() {
const router = useRouter();
const getText = useSettingsStore(state => state.getText);
const [products, setProducts] = useState<Product[]>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchFeatured = async () => {
try {
const data = await productService.getFeaturedProducts();
setProducts(data);
} catch (error) {
console.error("Failed to fetch featured products:", error);
toast.error("خطا در دریافت لیست محصولات از سرور. لطفاً صفحه را رفرش کنید.");
} finally {
setIsLoading(false);
}
};
fetchFeatured();
}, []);
return (
<section className="py-16 bg-medical-gray-50">
<div className="max-w-7xl mx-auto px-4">
{/* Header - Correct RTL alignment with primary and secondary elements */}
<div className="flex items-center justify-between w-full mb-12 pb-6 border-b border-medical-gray-200">
<div>
<div className="text-canina-blue text-xs font-bold uppercase tracking-[0.2em] mb-2 font-vazir">{getText('featured_badge', "بیشترین انتخاب توسط دامپزشکان")}</div>
<h2 className="text-2xl md:text-3xl lg:text-4xl font-black text-medical-gray-900 font-lalezar tracking-wide">
{getText('featured_title_line1', "محصولات برگزیده و راهکارهای")} <span className="text-canina-blue">{getText('featured_title_line2', "درمان تخصصی")}</span>
</h2>
</div>
<button
onClick={() => router.push('/shop')}
className="inline-flex items-center gap-2 text-canina-blue font-black text-sm hover:translate-x-1 transition-all group font-vazir"
>
{getText('featured_view_all', "مشاهده تمامی محصولات")}
<span className="text-lg group-hover:-translate-x-1 transition-transform"></span>
</button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-10">
{isLoading ? (
Array.from({ length: 4 }).map((_, i) => <ProductCardSkeleton key={i} />)
) : products.length > 0 ? (
products.map((p) => (
<ProductCard key={p.id} onClick={(p) => router.push(`/shop/${p.slug || p.id}`)} product={p} />
))
) : (
<div className="col-span-full py-16 px-8 flex flex-col items-center justify-center text-center bg-white rounded-[2.5rem] border border-medical-gray-200 shadow-xl max-w-lg mx-auto">
<AlertCircle className="w-12 h-12 text-canina-teal mb-4" />
<h3 className="text-xl font-black text-medical-gray-900 mb-2 font-vazir">{getText('featured_no_products_title', "هیچ محصولی یافت نشد")}</h3>
<p className="text-sm text-medical-gray-500 font-medium font-vazir leading-relaxed">
{getText('featured_no_products_desc', "در حال حاضر هیچ محصول برجسته‌ای برای نمایش وجود ندارد. لطفاً در پنل ادمین محصولات برگزیده را تنظیم کنید.")}
</p>
</div>
)}
</div>
</div>
</section>
);
}