canina/frontend/application/components/SearchResultsPage.tsx
parsa aghaei 1a0c98a34e feat: dynamic UI texts system + admin panel editor + Vazirmatn local font + bug fixes
- Add getText() system for all homepage sections (Header, Hero, FeaturedProducts, SmartAdvisor, VetGallery, Footer)
- Add UITexts admin page in frontend/admin-panel with search, group collapse, per-key save
- Add seed-ui-texts.ts with 102 texts in 9 groups
- Add local VazirmatnVariable.woff2 font, remove CDN + Inter references
- Fix hero image path, productService return type { data, meta }
- Fix ArchivePage pagination + CATEGORIES list + breadcrumbs
- Fix BlogPage/Wiki page links + brightness-0 invert removal
- Fix IngredientWiki search + product links + placeholder
- Fix all getProducts() callers for { data } destructuring
- Fix sitemap.ts, CartDrawer, B2BPortal, CheckoutPage type issues
- Add .gitignore for /fonts/ and /backend/uploads/
2026-07-11 17:19:55 +03:30

142 lines
7.8 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 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({ limit: 999 })
.then(res => setProducts(res.data))
.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>
);
}