canina/frontend/application/components/FeaturedProducts.tsx

152 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 { 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";
function ProductCard({ product, onClick }: { product: Product; key?: string | number; onClick: (p: Product) => void }) {
const { getActivePet } = usePetStore();
const activePet = getActivePet();
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 }}
onClick={() => onClick(product)}
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 object-contain group-hover:scale-105 transition-transform duration-500 drop-shadow-2xl"
/>
<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>
{/* Hover Actions */}
<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">
<div className="w-12 h-12 rounded-full bg-white text-canina-blue flex items-center justify-center hover:scale-110 transition-transform shadow-lg">
<Eye className="w-5 h-5" />
</div>
<div 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">
<ShoppingCart className="w-5 h-5" />
</div>
</div>
</div>
{/* Content Area */}
<div className="p-8 flex flex-col flex-1">
<div className="flex items-start justify-between mb-4 gap-2">
<h3 className="text-xl font-black text-medical-gray-900 group-hover:text-canina-blue transition-colors leading-tight">
{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">
{product.description}
</p>
<div className="space-y-4 pt-4 border-t border-medical-gray-100">
<div className="flex items-center gap-3">
<div className="w-1 h-1 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-xl font-black text-medical-gray-900 font-vazir">{product.price}</div>
<button 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">
مشاهده جزییات
</button>
</div>
</div>
</div>
</motion.div>
);
}
import { useRouter } from 'next/navigation';
export default function FeaturedProducts() {
const router = useRouter();
const [products, setProducts] = useState<Product[]>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchFeatured = async () => {
try {
const data = await productService.getFeaturedProducts();
setProducts(data);
} finally {
setIsLoading(false);
}
};
fetchFeatured();
}, []);
return (
<section className="py-24 bg-medical-gray-50">
<div className="max-w-7xl mx-auto px-4">
<div className="flex flex-col lg:flex-row items-end justify-between mb-16 gap-6">
<div className="lg:w-2/3">
<div className="text-canina-blue text-xs font-bold uppercase tracking-[0.2em] mb-4 font-vazir">بیشترین انتخاب توسط دامپزشکان</div>
<h2 className="text-3xl lg:text-5xl font-black text-medical-gray-900 leading-[1.2]">
محصولات برگزیده و راهکارهای <br />
<span className="italic text-canina-blue">درمان تخصصی</span>
</h2>
</div>
<div className="lg:w-1/3 text-left lg:text-right">
<p className="text-medical-gray-500 text-sm leading-relaxed mb-4">
محصولات دارویی کانینا با استفاده از دانش پیشرفته بیوتکنولوژی و مواد اولیه ارگانیک نایاب فرآوری شدهاند.
</p>
<button
onClick={() => router.push('/shop')}
className="inline-flex items-center gap-2 text-canina-blue font-bold text-sm hover:translate-x-1 transition-transform rtl:hover:-translate-x-1"
>
مشاهده تمامی محصولات
<span className="text-lg"></span>
</button>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10">
{isLoading ? (
Array.from({ length: 3 }).map((_, i) => <ProductCardSkeleton key={i} />)
) : (
products.map((p) => (
<ProductCard key={p.id} onClick={(p) => router.push(`/shop/${p.id}`)} product={p} />
))
)}
</div>
</div>
</section>
);
}