- Fix seed-products.ts TS error (implicit any) and BOM handling - Re-run full seed to restore correct Persian encoding in DB - Fix productService query→search param mismatch - Fix IngredientWiki hardcoded port 4000→use settingsStore - Restore seed-products-data.json from git after accidental corruption
142 lines
7.8 KiB
TypeScript
142 lines
7.8 KiB
TypeScript
"use client";
|
||
import React, { useMemo, useState, useEffect } from "react";
|
||
import { Search, ShoppingBag, ChevronLeft, ArrowRight, Activity, Sparkles, HeartPulse, Stethoscope, ShieldCheck, Heart, AlertCircle } from "lucide-react";
|
||
import { usePetStore } from "../lib/store/usePetStore";
|
||
import { motion } from "motion/react";
|
||
import { Product } from "../lib/data/products";
|
||
import { productService } from "../lib/services/productService";
|
||
|
||
import { useRouter } from 'next/navigation';
|
||
|
||
export default function SearchResultsPage({ query }: { query: string }) {
|
||
const router = useRouter();
|
||
const { getActivePet } = usePetStore();
|
||
const activePet = getActivePet();
|
||
const [products, setProducts] = useState<Product[]>([]);
|
||
|
||
useEffect(() => {
|
||
productService.getProducts()
|
||
.then(setProducts)
|
||
.catch(err => console.error("Error fetching products in SearchResultsPage:", err));
|
||
}, []);
|
||
|
||
const results = products.filter(p =>
|
||
p.name.toLowerCase().includes(query.toLowerCase()) ||
|
||
p.description.toLowerCase().includes(query.toLowerCase()) ||
|
||
p.main_ingredients.some(ing => ing.toLowerCase().includes(query.toLowerCase())) ||
|
||
p.symptoms.some(sym => sym.toLowerCase().includes(query.toLowerCase()))
|
||
);
|
||
|
||
const bestSellers = products.slice(0, 3);
|
||
|
||
return (
|
||
<div className="min-h-screen bg-medical-gray-50 py-20 px-6 font-vazir" dir="rtl">
|
||
<div className="max-w-7xl mx-auto">
|
||
<button onClick={() => router.back()} className="flex items-center gap-2 text-medical-gray-400 font-black mb-10 hover:text-canina-blue transition-colors">
|
||
<ArrowRight className="w-5 h-5 rotate-180" />
|
||
بازگشت
|
||
</button>
|
||
|
||
<div className="flex flex-col md:flex-row md:items-end justify-between mb-16 gap-6">
|
||
<div>
|
||
<h1 className="text-4xl font-black text-medical-gray-900 italic mb-4">
|
||
نتایج جستجو برای: <span className="text-canina-blue">"{query}"</span>
|
||
</h1>
|
||
<p className="text-medical-gray-500 font-medium">
|
||
{results.length} محصول با معیارهای شما مطابقت دارد.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{results.length > 0 ? (
|
||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||
{results.map((product) => {
|
||
const compatibility = (() => {
|
||
if (!activePet) return null;
|
||
const sameSpecies = product.suitableFor === activePet.type || product.suitableFor === "هر دو";
|
||
const helpsSymptom = activePet.medicalConditions.some(c => product.symptoms.includes(c));
|
||
if (!sameSpecies) return { type: 'alert', text: `مخصوص ${product.suitableFor}` };
|
||
if (helpsSymptom) return { type: 'success', text: `توصیه شده برای ${activePet.name}` };
|
||
return { type: 'neutral', text: `مناسب برای ${activePet.name}` };
|
||
})();
|
||
|
||
return (
|
||
<motion.div
|
||
key={product.id}
|
||
layoutId={product.id}
|
||
onClick={() => router.push(`/shop/${product.slug || product.id}`)}
|
||
className="bg-white rounded-[2.5rem] p-8 border border-medical-gray-200 group cursor-pointer hover:shadow-2xl hover:shadow-canina-blue/10 transition-all flex flex-col relative"
|
||
>
|
||
<div className="relative aspect-square mb-8 bg-medical-gray-50 rounded-[2rem] overflow-hidden p-6 flex items-center justify-center">
|
||
<img
|
||
src={product.image}
|
||
alt={product.name}
|
||
className="w-full h-full object-contain group-hover:scale-110 transition-transform duration-500"
|
||
referrerPolicy="no-referrer"
|
||
/>
|
||
{product.specialBadge && (
|
||
<div className="absolute top-4 right-4 bg-medical-gray-900 text-white text-[8px] font-black px-3 py-1 rounded-full uppercase tracking-widest">
|
||
{product.specialBadge}
|
||
</div>
|
||
)}
|
||
{compatibility && (
|
||
<div className={`absolute top-14 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>
|
||
)}
|
||
</div>
|
||
|
||
<div className="flex-1">
|
||
<span className="text-[10px] font-black text-canina-blue uppercase tracking-widest mb-2 block">{product.category}</span>
|
||
<h3 className="text-xl font-black text-medical-gray-900 mb-2 leading-tight group-hover:text-canina-blue transition-colors">{product.name}</h3>
|
||
<p className="text-xs text-medical-gray-400 line-clamp-2 leading-relaxed mb-6">{product.benefits}</p>
|
||
</div>
|
||
|
||
<div className="flex items-center justify-between pt-6 border-t border-medical-gray-100 italic">
|
||
<div>
|
||
<span className="text-lg font-black text-medical-gray-900">{product.price}</span>
|
||
</div>
|
||
<button className="bg-medical-gray-900 text-white w-10 h-10 rounded-xl flex items-center justify-center group-hover:bg-canina-blue transition-colors">
|
||
<ChevronLeft className="w-5 h-5" />
|
||
</button>
|
||
</div>
|
||
</motion.div>
|
||
)
|
||
})}
|
||
</div>
|
||
) : (
|
||
<div className="space-y-16">
|
||
<div className="bg-white border border-medical-gray-200 rounded-[3rem] p-12 text-center shadow-sm">
|
||
<div className="w-20 h-20 bg-medical-gray-50 rounded-full flex items-center justify-center mx-auto text-medical-gray-300 mb-6">
|
||
<Search className="w-10 h-10" />
|
||
</div>
|
||
<h3 className="text-2xl font-black text-medical-gray-800 mb-4">نتیجهای یافت نشد</h3>
|
||
<p className="text-medical-gray-500 font-medium max-w-md mx-auto leading-relaxed">
|
||
متاسفیم، هیچ محصولی با عبارت مورد نظر شما مطابقت نداشت. شاید محصولات پرفروش ما برای شما مفید باشند؟
|
||
</p>
|
||
</div>
|
||
|
||
<div>
|
||
<h3 className="text-2xl font-black text-medical-gray-900 mb-10 italic">محصولات پیشنهادی (پرفروش)</h3>
|
||
<div className="grid md:grid-cols-3 gap-8 opacity-60 hover:opacity-100 transition-opacity">
|
||
{bestSellers.map(p => (
|
||
<div key={p.id} onClick={() => router.push(`/shop/${p.id}`)} className="cursor-pointer group">
|
||
<div className="aspect-square bg-white rounded-[2rem] p-6 mb-4 border border-medical-gray-100 flex items-center justify-center">
|
||
<img src={p.image} className="w-40 h-40 object-contain group-hover:scale-105 transition-transform" />
|
||
</div>
|
||
<h4 className="font-black text-medical-gray-900 text-center">{p.name}</h4>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|