Compare commits

..

No commits in common. "0d805b0eb9d1bbb137534d02a2252e2e14137153" and "fd45910387642ade883ef7e497ba7afe6130f92a" have entirely different histories.

15 changed files with 426 additions and 1029 deletions

View File

@ -75,18 +75,18 @@ export default function HomeClient() {
{/* About Section Teaser */}
<section className="py-16 bg-white overflow-hidden font-sans">
<div className="max-w-7xl mx-auto px-4 grid lg:grid-cols-2 gap-16 lg:gap-24 items-center">
<div className="relative overflow-hidden rounded-[3rem]">
<div className="absolute -top-10 -right-10 w-40 h-40 bg-canina-blue/5 rounded-full blur-3xl" />
<img
src="/assets/images/regenerated_image_1779109861747.png"
alt="آزمایشگاه تخصصی تولید مکمل‌های دارویی کانینا آلمان"
className="rounded-[3rem] shadow-2xl relative z-10 grayscale-[10%] w-full"
/>
<div className="absolute bottom-4 left-4 sm:bottom-6 sm:left-6 bg-canina-blue text-white p-4 sm:p-6 md:p-8 rounded-[1.5rem] sm:rounded-[2rem] shadow-2xl z-20 max-w-[80%] border border-white/10">
<div className="text-2xl sm:text-4xl font-black mb-1 font-lalezar tracking-wide">{getText('quality', "کیفیت")}</div>
<div className="text-[10px] sm:text-xs uppercase tracking-widest font-bold opacity-90 font-vazir">{getText('ultra_pharmaceutical', "استانداردهای فوق‌دارویی")}</div>
</div>
</div>
<div className="relative">
<div className="absolute -top-10 -right-10 w-40 h-40 bg-canina-blue/5 rounded-full blur-3xl" />
<img
src="/assets/images/regenerated_image_1779109861747.png"
alt="آزمایشگاه تخصصی تولید مکمل‌های دارویی کانینا آلمان"
className="rounded-[3rem] shadow-2xl relative z-10 grayscale-[10%]"
/>
<div className="absolute -bottom-6 -left-6 bg-canina-blue text-white p-8 rounded-[2rem] shadow-xl z-20">
<div className="text-4xl font-black mb-1 font-lalezar tracking-wide">{getText('quality', "کیفیت")}</div>
<div className="text-xs uppercase tracking-widest font-bold opacity-80 font-vazir">{getText('ultra_pharmaceutical', "استانداردهای فوق‌دارویی")}</div>
</div>
</div>
<div>
<div className="text-canina-blue text-xs font-bold uppercase tracking-[0.2em] mb-4 font-vazir">
{getText('about_label', "میراث ما از آلمان")}

View File

@ -104,7 +104,7 @@
html, body, #root {
font-family: "Vazirmatn" !important;
@apply bg-medical-gray-50 text-medical-gray-900 antialiased overflow-x-hidden max-w-[100vw];
@apply bg-medical-gray-50 text-medical-gray-900 antialiased;
font-feature-settings: "ss01", "ss02", "cv01", "cv11";
}

View File

@ -3,7 +3,6 @@ import React, { useState, useMemo, useEffect } from "react";
import { Product, PetType } from "../lib/data/products";
import { usePetStore } from "../lib/store/usePetStore";
import { productService } from "../lib/services/productService";
import Link from "next/link";
import { motion, AnimatePresence } from "motion/react";
import { ProductCardSkeleton } from "./Skeleton";
import { toast } from "sonner";
@ -67,14 +66,12 @@ const CATEGORY_MAP: Record<string, { category?: string; query?: string; symptoms
"شستشوی چشم": { category: "special-care", query: "چشم" },
};
const ArchiveProductCard: React.FC<{ product: Product }> = ({ product }) => {
const ArchiveProductCard: React.FC<{ product: Product, onClick: (p: Product) => void }> = ({ product, onClick }) => {
const { addItem } = useCartStore();
const [isAdding, setIsAdding] = useState(false);
const activePet = usePetStore(state => state.pets.find(p => p.id === state.activePetId) || null);
const productUrl = `/shop/${product.slug || product.id}`;
const handleAddToCart = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setIsAdding(true);
addItem(product, 1);
@ -86,12 +83,10 @@ const ArchiveProductCard: React.FC<{ product: Product }> = ({ product }) => {
if (!activePet) return null;
const sameSpecies = product.suitableFor === activePet.type || product.suitableFor === "هر دو";
const matchedSymptom = (activePet.medicalConditions || []).find(mc =>
(product.symptoms || []).some(s => mc.includes(s) || s.includes(mc))
);
const helpsSymptom = (activePet.medicalConditions || []).some(s => product.symptoms.includes(s));
if (!sameSpecies) return { type: 'alert', text: `مخصوص ${product.suitableFor}` };
if (matchedSymptom) return { type: 'success', text: `توصیه شده برای ${matchedSymptom} ${activePet.name}` };
if (helpsSymptom) return { type: 'success', text: `توصیه شده برای راهکار ${activePet.name}` };
if (sameSpecies) return { type: 'neutral', text: `مناسب برای ${activePet.name}` };
return null;
@ -102,79 +97,75 @@ const ArchiveProductCard: React.FC<{ product: Product }> = ({ product }) => {
layout
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="h-full"
onClick={() => onClick(product)}
className="group bg-white rounded-[2.5rem] border border-medical-gray-200 overflow-hidden hover:shadow-2xl transition-all cursor-pointer flex flex-col h-full relative"
>
<Link
href={productUrl}
className="group bg-white rounded-[2.5rem] border border-medical-gray-200 overflow-hidden hover:shadow-2xl transition-all flex flex-col h-full relative"
>
{product.specialBadge && (
<div className="absolute top-4 right-4 z-10 bg-canina-blue text-white text-[9px] font-black uppercase tracking-widest px-3 py-1 rounded-full shadow-lg">
{product.specialBadge}
</div>
)}
{product.specialBadge && (
<div className="absolute top-4 right-4 z-10 bg-canina-blue text-white text-[9px] font-black uppercase tracking-widest px-3 py-1 rounded-full shadow-lg">
{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>
)}
{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 className="aspect-square bg-medical-gray-50 flex items-center justify-center p-8 overflow-hidden relative">
<SafeImage src={product.image} alt={product.name} className="w-full h-full group-hover:scale-110 transition-transform duration-700" imgClassName="object-contain" />
<div className="absolute bottom-4 left-4 flex gap-2">
{(product.suitableFor === "سگ" || product.suitableFor === "هر دو") && (
<div className="w-7 h-7 bg-white rounded-lg flex items-center justify-center text-medical-gray-400 border border-medical-gray-100 shadow-sm">
<Dog className="w-4 h-4" />
</div>
)}
{(product.suitableFor === "گربه" || product.suitableFor === "هر دو") && (
<div className="w-7 h-7 bg-white rounded-lg flex items-center justify-center text-medical-gray-400 border border-medical-gray-100 shadow-sm">
<Cat className="w-4 h-4" />
</div>
)}
</div>
<div className="aspect-square bg-medical-gray-50 flex items-center justify-center p-8 overflow-hidden relative">
<SafeImage src={product.image} alt={product.name} className="w-full h-full group-hover:scale-110 transition-transform duration-700" imgClassName="object-contain" />
<div className="absolute bottom-4 left-4 flex gap-2">
{(product.suitableFor === "سگ" || product.suitableFor === "هر دو") && (
<div className="w-7 h-7 bg-white rounded-lg flex items-center justify-center text-medical-gray-400 border border-medical-gray-100 shadow-sm">
<Dog className="w-4 h-4" />
</div>
)}
{(product.suitableFor === "گربه" || product.suitableFor === "هر دو") && (
<div className="w-7 h-7 bg-white rounded-lg flex items-center justify-center text-medical-gray-400 border border-medical-gray-100 shadow-sm">
<Cat className="w-4 h-4" />
</div>
)}
</div>
</div>
<div className="p-6 flex flex-col flex-1">
<div className="text-[10px] font-bold text-canina-blue uppercase tracking-widest mb-2">{product.category}</div>
<div className="flex flex-col gap-1 mb-3">
<h3 dir="rtl" className="text-base font-black text-medical-gray-900 group-hover:text-canina-blue transition-colors leading-tight text-right font-vazir">
{product.nameFa || product.name}
</h3>
{product.nameEn && (
<span dir="ltr" className="text-[11px] font-bold text-slate-400 font-sans text-left block">
{product.nameEn}
</span>
)}
</div>
<div className="p-6 flex flex-col flex-1">
<div className="text-[10px] font-bold text-canina-blue uppercase tracking-widest mb-2">{product.category}</div>
<div className="flex flex-col gap-1 mb-3">
<h3 dir="rtl" className="text-base font-black text-medical-gray-900 group-hover:text-canina-blue transition-colors leading-tight text-right font-vazir">
{product.nameFa || product.name}
</h3>
{product.nameEn && (
<span dir="ltr" className="text-[11px] font-bold text-slate-400 font-sans text-left block">
{product.nameEn}
</span>
)}
</div>
<div className="flex flex-wrap gap-1">
{product.symptoms.slice(0, 2).map((s, i) => (
<span key={i} className="text-[9px] font-bold bg-medical-gray-100 text-medical-gray-500 px-2 py-0.5 rounded-md">
{s}
</span>
))}
</div>
<div className="mt-auto flex items-center justify-between pt-4 border-t border-medical-gray-50">
<span className="font-black text-medical-gray-900 font-vazir">{product.price}</span>
<button
onClick={handleAddToCart}
disabled={isAdding}
className="w-10 h-10 rounded-xl bg-medical-gray-50 text-canina-blue flex items-center justify-center group-hover:bg-canina-blue group-hover:text-white transition-all disabled:opacity-50"
>
{isAdding ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />}
</button>
</div>
<div className="flex flex-wrap gap-1">
{product.symptoms.slice(0, 2).map((s, i) => (
<span key={i} className="text-[9px] font-bold bg-medical-gray-100 text-medical-gray-500 px-2 py-0.5 rounded-md">
{s}
</span>
))}
</div>
</Link>
<div className="mt-auto flex items-center justify-between pt-4 border-t border-medical-gray-50">
<span className="font-black text-medical-gray-900 font-vazir">{product.price}</span>
<button
onClick={handleAddToCart}
disabled={isAdding}
className="w-10 h-10 rounded-xl bg-medical-gray-50 text-canina-blue flex items-center justify-center group-hover:bg-canina-blue group-hover:text-white transition-all disabled:opacity-50"
>
{isAdding ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />}
</button>
</div>
</div>
</motion.div>
);
};
}
import { useRouter } from 'next/navigation';
@ -286,24 +277,28 @@ export default function ArchivePage({
router.push(newUrl, { scroll: false });
}, [selectedCategory, selectedPet, searchQuery, activeSymptoms]);
const fetchProducts = async () => {
const fetchProducts = async (resetPage = true) => {
setIsUpdating(true);
try {
const res = await productService.getProducts({
category: selectedCategory,
petType: selectedPet,
query: searchQuery,
limit: 999,
// Send the first active symptom to the backend for server-side filtering
symptom: activeSymptoms.length === 1 ? activeSymptoms[0] : undefined,
page: resetPage ? 1 : page,
limit: 20,
});
let result = res.data;
// Filter with OR logic (union of symptoms) if symptoms are active
if (activeSymptoms.length > 0) {
result = result.filter(p => p.symptoms && p.symptoms.some(s => activeSymptoms.includes(s)));
// If multiple symptoms selected, do client-side intersection (rare case)
if (activeSymptoms.length > 1) {
result = result.filter(p => activeSymptoms.every(s => p.symptoms.includes(s)));
}
setFilteredProducts(result);
setMeta(res.meta);
if (resetPage) setPage(1);
} catch (error) {
console.error("Failed to fetch products:", error);
toast.error("خطا در دریافت لیست محصولات از سرور. لطفاً صفحه را رفرش کنید.");
@ -313,9 +308,32 @@ export default function ArchivePage({
};
useEffect(() => {
fetchProducts();
fetchProducts(true);
}, [selectedCategory, selectedPet, searchQuery, activeSymptoms]);
const loadMore = async () => {
if (page >= meta.lastPage) return;
try {
const res = await productService.getProducts({
category: selectedCategory,
petType: selectedPet,
query: searchQuery,
symptom: activeSymptoms.length === 1 ? activeSymptoms[0] : undefined,
page: page + 1,
limit: 20,
});
let newItems = res.data;
if (activeSymptoms.length > 0) {
newItems = newItems.filter(p => p.symptoms.some(s => activeSymptoms.includes(s)));
}
setFilteredProducts(prev => [...prev, ...newItems]);
setMeta(res.meta);
setPage(prev => prev + 1);
} catch (error) {
console.error("Failed to load more:", error);
}
};
const toggleSymptom = (s: string) => {
setActiveSymptoms(prev => prev.includes(s) ? prev.filter(item => item !== s) : [...prev, s]);
};
@ -473,10 +491,20 @@ export default function ArchivePage({
<div key="grid" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
<AnimatePresence mode="popLayout">
{filteredProducts.map((p: Product) => (
<ArchiveProductCard key={p.id} product={p} />
<ArchiveProductCard key={p.id} product={p} onClick={(p) => router.push(`/shop/${p.slug || p.id}`)} />
))}
</AnimatePresence>
</div>
{page < meta.lastPage && (
<div className="flex justify-center mt-10">
<button
onClick={loadMore}
className="bg-canina-blue text-white px-8 py-3 rounded-xl font-black text-sm hover:scale-105 transition-transform shadow-lg shadow-canina-blue/20"
>
بارگذاری محصولات بیشتر
</button>
</div>
)}
</>
) : (
<div key="empty" className="bg-white rounded-[3rem] p-20 text-center border-2 border-dashed border-medical-gray-200 flex flex-col items-center">

View File

@ -7,8 +7,6 @@ import { useCartStore } from "../lib/store/cartStore";
import { Product } from "../lib/data/products";
import { productService } from "../lib/services/productService";
import SafeImage from "./SafeImage";
import { useUserStore } from "../lib/store/userStore";
import AuthModal from "./AuthModal";
import { toast } from "sonner";
export default function CartDrawer({ isOpen, onClose, onCheckout, onShopNavigate }: {
@ -18,8 +16,6 @@ export default function CartDrawer({ isOpen, onClose, onCheckout, onShopNavigate
onShopNavigate?: () => void;
}) {
const { items, updateQuantity, removeItem, isSubscribed, toggleSubscription, getTotal, getSubtotal, getDiscount, coupon, applyCoupon, addItem } = useCartStore();
const { isLoggedIn } = useUserStore();
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
const [couponCode, setCouponCode] = useState("");
const [couponStatus, setCouponStatus] = useState<"idle" | "success" | "error">("idle");
const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null);
@ -260,14 +256,7 @@ export default function CartDrawer({ isOpen, onClose, onCheckout, onShopNavigate
{/* Subscription Toggle */}
<div
onClick={() => {
if (!isLoggedIn && !isSubscribed) {
toast.info("برای فعال‌سازی سرویس تمدید خودکار و دریافت یادآورها، لطفاً ابتدا وارد حساب کاربری خود شوید.");
setIsAuthModalOpen(true);
return;
}
toggleSubscription();
}}
onClick={toggleSubscription}
className={`p-4 rounded-2xl border-2 transition-all cursor-pointer ${isSubscribed ? 'bg-canina-blue/5 border-canina-blue' : 'bg-white border-medical-gray-100 hover:border-canina-blue/30'}`}
>
<div className="flex items-center gap-3">
@ -276,8 +265,7 @@ export default function CartDrawer({ isOpen, onClose, onCheckout, onShopNavigate
</div>
<div className="flex-1">
<h5 className="text-xs font-black text-medical-gray-900">فعال‌سازی سرویس تمدید خودکار (Refill)</h5>
<p className="text-[10px] text-canina-blue font-black mt-0.5">۵٪ تخفیف روی تمام فاکتور + ارسال اولویت‌دار</p>
<p className="text-[9px] text-medical-gray-400 font-bold mt-1">ویژه اعضا - محاسبه هوشمند دوره مصرف و یادآوری پیامکی قبل از اتمام</p>
<p className="text-[10px] text-medical-gray-500 font-medium">۵٪ تخفیف روی تمام فاکتور + ارسال اولویت‌دار</p>
</div>
<div className={`w-5 h-5 rounded-full border-2 flex items-center justify-center ${isSubscribed ? 'bg-canina-blue border-canina-blue' : 'border-medical-gray-200'}`}>
{isSubscribed && <X className="w-3 h-3 text-white" />}
@ -285,8 +273,6 @@ export default function CartDrawer({ isOpen, onClose, onCheckout, onShopNavigate
</div>
</div>
<AuthModal isOpen={isAuthModalOpen} onClose={() => setIsAuthModalOpen(false)} />
<div className="space-y-2">
<div className="flex items-center justify-between text-sm px-2">
<span className="text-medical-gray-500 font-bold">مجموع اقلام</span>

View File

@ -1,6 +1,6 @@
"use client";
import React, { useState, useEffect } from "react";
import { Phone, Mail, MapPin, Instagram, ShieldCheck, Globe, ArrowUp } from "lucide-react";
import React from "react";
import { Phone, Mail, MapPin, Instagram, ShieldCheck, Globe } from "lucide-react";
import { useSettingsStore } from "../lib/store/settingsStore";
import Link from "next/link";
@ -10,38 +10,8 @@ export default function Footer({ onNavigate, onShopNavigate, onB2BOpen }: {
onB2BOpen?: () => void
}) {
const getText = useSettingsStore(state => state.getText);
const [showScroll, setShowScroll] = useState(false);
useEffect(() => {
const checkScroll = () => {
if (window.scrollY > 300) {
setShowScroll(true);
} else {
setShowScroll(false);
}
};
window.addEventListener('scroll', checkScroll);
return () => window.removeEventListener('scroll', checkScroll);
}, []);
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
};
return (
<footer className="bg-medical-gray-900 text-white pt-24 pb-12 font-shabnam relative" dir="rtl">
{/* Floating Back-to-Top Button */}
{showScroll && (
<button
onClick={scrollToTop}
className="fixed bottom-24 left-6 sm:bottom-8 sm:left-8 z-40 bg-canina-blue text-white p-3 rounded-full shadow-2xl hover:bg-canina-dark hover:scale-110 transition-all border border-white/20 group flex items-center gap-2 font-vazir text-xs font-black"
title="بازگشت به بالای صفحه"
>
<ArrowUp className="w-5 h-5 group-hover:-translate-y-1 transition-transform" />
<span className="hidden sm:inline">بازگشت به بالا</span>
</button>
)}
<footer className="bg-medical-gray-900 text-white pt-24 pb-12 font-shabnam" dir="rtl">
<div className="max-w-7xl mx-auto px-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-16 mb-20">
@ -155,10 +125,10 @@ export default function Footer({ onNavigate, onShopNavigate, onB2BOpen }: {
<div className="pt-12 border-t border-white/5 flex flex-col md:flex-row items-center justify-between gap-6 text-[10px] font-bold uppercase tracking-[0.3em] font-shabnam">
<div className="text-slate-400 font-shabnam">{getText('footer_copyright', "© ۲۰۲۶ کانینا ایران. تمامی حقوق محفوظ است.")}</div>
<div className="flex gap-4 sm:gap-8 font-shabnam text-slate-400 items-center">
<Link href="/about" className="font-extrabold hover:text-white transition-colors whitespace-nowrap">{getText('footer_about', "درباره ما")}</Link>
<Link href="/contact" className="font-extrabold hover:text-white transition-colors whitespace-nowrap">{getText('footer_contact', "تماس با ما")}</Link>
<Link href="/privacy" className="font-extrabold hover:text-white transition-colors whitespace-nowrap">{getText('footer_privacy', "حریم خصوصی")}</Link>
<div className="flex gap-8 font-shabnam text-slate-400">
<Link href="/about" className="font-extrabold hover:text-white transition-colors">{getText('footer_about', "درباره ما")}</Link>
<Link href="/contact" className="font-extrabold hover:text-white transition-colors">{getText('footer_contact', "تماس با ما")}</Link>
<Link href="/privacy" className="font-extrabold hover:text-white transition-colors">{getText('footer_privacy', "حریم خصوصی")}</Link>
</div>
</div>
</div>

View File

@ -47,32 +47,8 @@ export default function Header({
title: string;
icon: React.ReactNode;
solutions: string[];
}[]>([
{
id: "joints",
title: "مفاصل و استخوان",
icon: MENU_ICONS["joints"],
solutions: ["لنگیدن", "سختی در بلند شدن", "درد مفاصل", "رشد سریع توله‌سگ", "پاهای پرانتزی", "ضعف تاندون"]
},
{
id: "immune",
title: "تقویت سیستم ایمنی و گوارش",
icon: MENU_ICONS["immune"],
solutions: ["ضعف بعد از بیماری", "بی‌اشتهایی", "بعد از زایمان", "اسهال", "مشکلات گوارشی"]
},
{
id: "energy",
title: "ویتامین‌ها و انرژی‌بخش‌ها",
icon: MENU_ICONS["energy"],
solutions: ["نفس‌نفس زدن", "خستگی", "سن بالا", "بی‌حالی"]
},
{
id: "special-care",
title: "مراقبت‌های ویژه (پوست، دندان و چشم)",
icon: MENU_ICONS["special-care"],
solutions: ["ریزش مو", "خشکی پوست", "خارش", "جرم دندان", "سوزش چشم"]
}
]);
}[]>([]);
const petMenuRef = useRef<HTMLDivElement>(null);
const { getTotalItems } = useCartStore();
@ -85,15 +61,13 @@ export default function Header({
const loadNavFilters = async () => {
try {
const data = await productService.getNavigationFilters();
if (data && data.length > 0) {
const mapped = data.map(item => ({
id: item.slug,
title: item.name,
icon: MENU_ICONS[item.slug] || <Pill className="w-5 h-5" />,
solutions: item.symptoms || []
}));
setMenuItems(mapped);
}
const mapped = data.map(item => ({
id: item.slug,
title: item.name,
icon: MENU_ICONS[item.slug] || <Pill className="w-5 h-5" />,
solutions: item.symptoms
}));
setMenuItems(mapped);
} catch (e) {
console.error("Failed to load navigation filters:", e);
}
@ -130,49 +104,46 @@ export default function Header({
</div>
<header className="sticky top-0 z-50 bg-white/95 backdrop-blur-md border-b border-medical-gray-100 shadow-sm">
<div className="max-w-7xl mx-auto py-2 min-[470px]:py-0 min-[470px]:h-24 flex flex-col min-[470px]:flex-row min-[470px]:items-center justify-between gap-2 min-[470px]:gap-4 px-3 sm:px-6 lg:px-8" dir="rtl">
{/* Logo & Mobile Toggle Area */}
<div className="flex items-center justify-between min-[470px]:justify-start gap-2 w-full min-[470px]:w-auto">
<div className="flex items-center gap-2">
{/* Mobile Menu Toggle (visible below 800px) */}
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="min-[800px]:hidden p-2 rounded-xl text-medical-gray-600 hover:bg-medical-gray-50 transition-all flex-shrink-0"
aria-label="منو"
>
{isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
</button>
<div className="max-w-7xl mx-auto h-24 flex items-center justify-between gap-4 px-4 sm:px-6 lg:px-8" dir="rtl">
<div className="flex items-center gap-2">
{/* Mobile Menu Toggle */}
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="lg:hidden p-2 rounded-xl text-medical-gray-600 hover:bg-medical-gray-50 transition-all"
aria-label="منو"
>
{isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
</button>
{/* Logo Section */}
<Link
href="/"
className="flex items-center gap-2 sm:gap-3 cursor-pointer group"
onClick={() => setIsMobileMenuOpen(false)}
>
<div className="w-9 h-9 sm:w-12 sm:h-12 bg-canina-blue rounded-2xl flex items-center justify-center text-white font-bold text-lg sm:text-2xl group-hover:bg-medical-gray-900 transition-all shadow-xl shadow-canina-blue/20 italic flex-shrink-0">C</div>
<div className="flex flex-col justify-center gap-0.5 sm:gap-1">
<span className="text-canina-blue font-black text-base sm:text-2xl tracking-tighter italic font-sans flex items-center gap-1.5 sm:gap-2 leading-none whitespace-nowrap">
Canina
<span className="text-xs sm:text-sm not-italic font-medium border-r-2 border-medical-gray-200 pr-1.5 sm:pr-2 font-vazir">
{getText('brand_name_fa', "ایران")}
</span>
{/* Logo Section */}
<Link
href="/"
className="flex-shrink-0 flex items-center gap-3 cursor-pointer group"
onClick={() => setIsMobileMenuOpen(false)}
>
<div className="w-12 h-12 bg-canina-blue rounded-2xl flex items-center justify-center text-white font-bold text-2xl group-hover:bg-medical-gray-900 transition-all shadow-xl shadow-canina-blue/20 italic">C</div>
<div className="flex flex-col justify-center gap-1">
<span className="text-canina-blue font-black text-2xl tracking-tighter italic font-sans flex items-center gap-2 leading-none">
Canina
<span className="text-sm not-italic font-medium border-r-2 border-medical-gray-200 pr-2 font-vazir">
{getText('brand_name_fa', "ایران")}
</span>
<span className="text-[8px] sm:text-[9px] text-medical-gray-400 font-bold uppercase tracking-wider font-vazir leading-none whitespace-nowrap">
{getText('brand_subtitle', "نماینده رسمی Canina Pharma GmbH آلمان")}
</span>
</div>
</Link>
</div>
</span>
<span className="text-[9px] text-medical-gray-400 font-bold uppercase tracking-wider font-vazir leading-none">
{getText('brand_subtitle', "نماینده رسمی Canina Pharma GmbH آلمان")}
</span>
</div>
</Link>
</div>
{/* Navigation Items: Centered on XL screens (1224px+) */}
<nav className="hidden xl:flex items-center gap-1 flex-grow justify-center h-full">
{/* Navigation Items */}
<nav className="hidden lg:flex items-center gap-1 flex-grow justify-center h-full">
<div
className="relative h-full flex items-center group"
onMouseEnter={() => setIsMegaMenuOpen(true)}
onMouseLeave={() => setIsMegaMenuOpen(false)}
>
<button className={`flex items-center justify-center gap-1.5 px-3 xl:px-4 py-3 rounded-xl text-xs xl:text-sm font-black transition-all whitespace-nowrap focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none min-h-[48px] ${isMegaMenuOpen ? 'bg-medical-gray-50 text-canina-blue' : 'text-medical-gray-600 hover:bg-medical-gray-50 hover:text-canina-blue'} font-vazir`}>
<button className={`flex items-center justify-center gap-1.5 px-4 py-3 rounded-xl text-sm font-black transition-all whitespace-nowrap focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none min-h-[48px] ${isMegaMenuOpen ? 'bg-medical-gray-50 text-canina-blue' : 'text-medical-gray-600 hover:bg-medical-gray-50 hover:text-canina-blue'} font-vazir`}>
{getText('nav_solutions', "دسته بندی درمانی")}
<ChevronDown className={`w-4 h-4 transition-transform duration-300 ${isMegaMenuOpen ? 'rotate-180' : ''}`} />
</button>
@ -238,7 +209,7 @@ export default function Header({
<Link
key={item.id}
href={item.href}
className={`px-3 xl:px-4 py-3 rounded-xl text-xs xl:text-sm font-black transition-all font-vazir whitespace-nowrap focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none min-h-[48px] flex items-center justify-center ${currentView === item.id ? 'bg-canina-blue/5 text-canina-blue' : 'text-medical-gray-600 hover:bg-medical-gray-50 hover:text-canina-blue'}`}
className={`px-4 py-3 rounded-xl text-sm font-black transition-all font-vazir whitespace-nowrap focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none min-h-[48px] flex items-center justify-center ${currentView === item.id ? 'bg-canina-blue/5 text-canina-blue' : 'text-medical-gray-600 hover:bg-medical-gray-50 hover:text-canina-blue'}`}
>
{item.label}
</Link>
@ -246,7 +217,7 @@ export default function Header({
</nav>
{/* Action Area */}
<div className="flex items-center gap-2 sm:gap-4 flex-shrink-0 justify-end h-auto md:h-16 pl-1 sm:pl-2">
<div className="flex items-center gap-4 flex-shrink-0 justify-end h-16 pl-2">
{/* Elastic Search */}
<div className="relative flex items-center justify-end z-20">
@ -254,10 +225,10 @@ export default function Header({
{isSearchFocused && (
<motion.div
initial={{ width: 0, opacity: 0 }}
animate={{ width: 260, opacity: 1 }}
animate={{ width: 320, opacity: 1 }}
exit={{ width: 0, opacity: 0 }}
transition={{ type: "spring", damping: 25, stiffness: 220 }}
className="absolute right-0 top-1/2 -translate-y-1/2 bg-white border border-canina-blue/30 border-l rounded-2xl shadow-xl shadow-canina-blue/5 overflow-hidden h-10 sm:h-12 flex items-center pr-10 pl-3 z-25"
className="absolute right-0 top-1/2 -translate-y-1/2 bg-white border border-canina-blue/30 border-l rounded-2xl shadow-xl shadow-canina-blue/5 overflow-hidden h-12 flex items-center pr-12 pl-4 z-25"
>
<form onSubmit={handleSearch} className="w-full flex items-center">
<input
@ -266,9 +237,9 @@ export default function Header({
value={searchQuery}
onChange={e => setSearchQuery(e.target.value)}
onBlur={() => !searchQuery && setIsSearchFocused(false)}
placeholder="جستجو…"
placeholder="جستجوی محصولات، مقالات سلامت و غیره…"
dir="rtl"
className="w-full bg-transparent py-2 pr-2 text-[12px] font-black outline-none font-vazir text-medical-gray-900 text-right placeholder:text-medical-gray-400 placeholder:text-right"
className="w-full bg-transparent py-2 pr-4 text-[12px] font-black outline-none font-vazir text-medical-gray-900 text-right placeholder:text-medical-gray-400 placeholder:text-right"
/>
<input type="submit" className="hidden" />
</form>
@ -279,7 +250,7 @@ export default function Header({
<HeaderButton
variant={isSearchFocused ? "primary" : "secondary"}
className={cn(
"w-10 h-10 sm:w-12 sm:h-12 transition-all duration-300",
"w-12 h-12 transition-all duration-300",
isSearchFocused
? "z-30 bg-canina-blue text-white rounded-r-2xl rounded-l-none border border-canina-blue border-l-0 shadow-none"
: "rounded-2xl"
@ -293,45 +264,45 @@ export default function Header({
}
}}
>
<Search className={cn("w-4 h-4 sm:w-5 sm:h-5", isSearchFocused ? "text-white" : "text-canina-blue")} />
<Search className={cn("w-5 h-5", isSearchFocused ? "text-white" : "text-canina-blue")} />
</HeaderButton>
</div>
<div className="flex gap-1.5 sm:gap-2 items-center h-12">
<div className="flex gap-2 items-center h-12">
{isLoggedIn ? (
<div className="flex items-center h-full gap-1.5 sm:gap-2 p-1 bg-medical-gray-50/50 border border-medical-gray-100 rounded-3xl">
<div className="flex items-center h-full gap-2 p-1.5 bg-medical-gray-50/50 border border-medical-gray-100 rounded-3xl">
{/* User Name (Direct link to dashboard) */}
<div
className="flex items-center gap-1.5 sm:gap-2.5 px-2.5 sm:px-4 py-2 hover:bg-white rounded-2xl cursor-pointer transition-all border border-transparent hover:border-medical-gray-200"
className="flex items-center gap-2.5 px-4 py-2 hover:bg-white rounded-2xl cursor-pointer transition-all border border-transparent hover:border-medical-gray-200"
onClick={() => onNavigate("user-dashboard")}
>
<User className="w-4 h-4 sm:w-5 sm:h-5 text-canina-blue" />
<span className="text-[12px] sm:text-[14px] font-black font-vazir text-medical-gray-900 leading-none">
<User className="w-5 h-5 text-canina-blue" />
<span className="text-[14px] font-black font-vazir text-medical-gray-900 leading-none">
{profile.firstName}
</span>
</div>
{/* Separator Line */}
<div className="w-[1.5px] h-4 sm:h-5 bg-medical-gray-200" />
<div className="w-[1.5px] h-5 bg-medical-gray-200" />
{/* Pet Name & Switcher */}
<div className="relative h-full flex items-center" ref={petMenuRef}>
<div
className={cn(
"flex items-center gap-1.5 sm:gap-2.5 px-2.5 sm:px-4 py-2 rounded-2xl transition-all cursor-pointer",
"flex items-center gap-2.5 px-4 py-2 rounded-2xl transition-all cursor-pointer",
isPetSwitcherOpen ? "bg-canina-blue text-white shadow-lg shadow-canina-blue/20" : "text-canina-blue hover:bg-white"
)}
onClick={() => setIsPetSwitcherOpen(!isPetSwitcherOpen)}
>
{activePet?.type === "گربه" ? (
<Cat className={cn("w-4 h-4 sm:w-5 sm:h-5", isPetSwitcherOpen ? "text-white" : "text-canina-blue/60")} />
<Cat className={cn("w-5 h-5", isPetSwitcherOpen ? "text-white" : "text-canina-blue/60")} />
) : (
<Dog className={cn("w-4 h-4 sm:w-5 sm:h-5", isPetSwitcherOpen ? "text-white" : "text-canina-blue/60")} />
<Dog className={cn("w-5 h-5", isPetSwitcherOpen ? "text-white" : "text-canina-blue/60")} />
)}
<span className={cn("text-[12px] sm:text-[14px] font-black font-vazir leading-none", isPetSwitcherOpen ? "text-white" : "text-canina-blue")}>
<span className={cn("text-[14px] font-black font-vazir leading-none", isPetSwitcherOpen ? "text-white" : "text-canina-blue")}>
{activePet?.name || "بدون پت"}
</span>
<ChevronDown className={cn("w-3.5 h-3.5 sm:w-4 sm:h-4 transition-transform", isPetSwitcherOpen ? "rotate-180" : "")} />
<ChevronDown className={cn("w-4 h-4 transition-transform", isPetSwitcherOpen ? "rotate-180" : "")} />
</div>
<AnimatePresence>
@ -420,95 +391,26 @@ export default function Header({
<HeaderButton
variant="outline"
layout="row"
className="px-3 sm:px-6 h-10 sm:h-12 text-xs sm:text-sm font-black flex items-center justify-center"
className="px-6 h-12 text-sm font-black flex items-center justify-center"
onClick={() => setIsAuthModalOpen(true)}
icon={<LogIn className="w-4 h-4 sm:w-5 sm:h-5 text-canina-blue" />}
icon={<LogIn className="w-5 h-5 text-canina-blue" />}
>
<span className="hidden xs:inline">{getText('nav_login', "ورود / ثبت‌نام")}</span>
<span className="xs:hidden">ورود</span>
{getText('nav_login', "ورود / ثبت‌نام")}
</HeaderButton>
)}
<HeaderButton
variant="primary"
className="w-12 sm:w-16 h-10 sm:h-12"
className="w-16 h-12"
onClick={onCartOpen}
badge={getTotalItems()}
icon={<ShoppingBag className="w-5 h-5 sm:w-6 sm:h-6" />}
icon={<ShoppingBag className="w-6 h-6" />}
>
<div className="hidden sm:block text-[8px] font-black tracking-tight whitespace-nowrap mt-0.5">{getText('nav_cart', "سبد خرید")}</div>
<div className="text-[8px] font-black tracking-tight whitespace-nowrap mt-0.5">{getText('nav_cart', "سبد خرید")}</div>
</HeaderButton>
</div>
</div>
</div>
{/* Medium Screen Sub-Navbar (Visible only between 800px and 1224px) */}
<div className="hidden min-[800px]:flex xl:hidden border-t border-medical-gray-100 bg-medical-gray-50/50 py-2 px-6 justify-center items-center gap-2 font-vazir relative" dir="rtl">
<div className="relative">
<button
onClick={() => setIsMegaMenuOpen(!isMegaMenuOpen)}
className={`px-3 py-1.5 rounded-lg text-xs font-black transition-all flex items-center gap-1.5 whitespace-nowrap ${isMegaMenuOpen ? 'bg-canina-blue text-white shadow-sm' : 'text-medical-gray-700 hover:bg-white'}`}
>
{getText('nav_solutions', "دسته بندی درمانی")}
<ChevronDown className={`w-3.5 h-3.5 transition-transform ${isMegaMenuOpen ? 'rotate-180 text-white' : 'text-canina-blue'}`} />
</button>
<AnimatePresence>
{isMegaMenuOpen && (
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.98 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.98 }}
className="absolute top-[120%] right-0 w-[600px] max-w-[90vw] bg-white border border-medical-gray-100 shadow-2xl rounded-[2rem] p-6 grid grid-cols-2 gap-6 z-50 pointer-events-auto"
>
{menuItems.map((item, idx) => (
<div key={idx} className="group/item">
<Link
href={`/shop?category=${item.id}`}
onClick={() => setIsMegaMenuOpen(false)}
className="flex items-center gap-3 mb-3 cursor-pointer block"
>
<div className="p-2 bg-medical-gray-50 rounded-xl text-canina-blue group-hover/item:bg-canina-blue group-hover/item:text-white transition-all shadow-sm">
{item.icon}
</div>
<h4 className="font-black text-medical-gray-900 text-xs font-vazir">{item.title}</h4>
</Link>
{item.solutions.length > 0 && (
<div className="flex flex-wrap gap-1 border-r-2 border-medical-gray-100 pr-2">
{item.solutions.slice(0, 6).map((sol, sIdx) => (
<Link
key={sIdx}
href={`/shop?category=${item.id}&symptom=${encodeURIComponent(sol)}`}
className="inline-flex items-center px-2 py-0.5 rounded-full text-[9px] font-bold bg-medical-gray-50 border border-medical-gray-200 text-medical-gray-500 hover:bg-canina-blue/5 hover:border-canina-blue/30 hover:text-canina-blue transition-all cursor-pointer font-vazir whitespace-nowrap"
onClick={() => setIsMegaMenuOpen(false)}
>
{sol}
</Link>
))}
</div>
)}
</div>
))}
</motion.div>
)}
</AnimatePresence>
</div>
<div className="w-[1px] h-4 bg-medical-gray-200" />
{[
{ id: 'shop', label: getText('nav_products', 'محصولات تخصصی'), href: '/shop' },
{ id: 'wiki', label: getText('nav_wiki', 'دانشنامه علمی'), href: '/wiki' },
{ id: 'blog', label: getText('nav_blog', 'مجله سلامت پت'), href: '/blog' },
{ id: 'profile', label: getText('nav_pet_profiles', 'شناسنامه پت'), href: '/profile' }
].map((item) => (
<Link
key={item.id}
href={item.href}
className={`px-3 py-1.5 rounded-lg text-xs font-black transition-all whitespace-nowrap ${currentView === item.id ? 'bg-canina-blue text-white shadow-sm' : 'text-medical-gray-600 hover:bg-white hover:text-canina-blue'}`}
>
{item.label}
</Link>
))}
</div>
</header>
{/* Mobile Drawer Menu */}
@ -521,7 +423,7 @@ export default function Header({
animate={{ opacity: 0.5 }}
exit={{ opacity: 0 }}
onClick={() => setIsMobileMenuOpen(false)}
className="fixed inset-0 bg-black z-40 min-[800px]:hidden"
className="fixed inset-0 bg-black z-40 lg:hidden"
/>
{/* Drawer */}
<motion.div
@ -529,7 +431,7 @@ export default function Header({
animate={{ x: 0 }}
exit={{ x: "100%" }}
transition={{ type: "spring", damping: 25, stiffness: 200 }}
className="fixed top-0 right-0 bottom-0 w-80 max-w-[85vw] bg-white z-50 shadow-2xl p-6 flex flex-col gap-6 overflow-y-auto min-[800px]:hidden"
className="fixed top-0 right-0 bottom-0 w-80 max-w-[85vw] bg-white z-50 shadow-2xl p-6 flex flex-col gap-6 overflow-y-auto lg:hidden"
dir="rtl"
>
<div className="flex items-center justify-between border-b border-medical-gray-100 pb-4">

View File

@ -168,46 +168,46 @@ export default function Hero({ banners = [] }: { banners?: any[] }) {
</div>
</div>
{/* Stats - 3 side-by-side cards with centered text on all screens */}
<div className="max-w-7xl mx-auto px-2 sm:px-4 relative">
{/* Stats - Centered, full-width row under Hero content to decrease cognitive load */}
<div className="max-w-7xl mx-auto px-4 relative">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.8, duration: 0.6 }}
className="grid grid-cols-3 gap-2 sm:gap-6 py-6 sm:py-10 border-t border-medical-gray-150"
className="grid grid-cols-1 md:grid-cols-3 gap-8 py-10 border-t border-medical-gray-150"
>
{/* Stat 1: Founded Year */}
<div className="bg-medical-gray-50/50 border border-medical-gray-100 rounded-2xl sm:rounded-3xl p-2.5 sm:p-6 flex flex-col sm:flex-row items-center justify-center text-center sm:text-right gap-2 sm:gap-6 group hover:shadow-xl hover:shadow-canina-blue/5 transition-all">
<div className="w-9 h-9 sm:w-14 sm:h-14 bg-white rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue shadow-sm group-hover:bg-canina-blue group-hover:text-white transition-all duration-300 flex-shrink-0">
<Calendar className="w-4 h-4 sm:w-7 sm:h-7" />
<div className="bg-medical-gray-50/50 border border-medical-gray-100 rounded-3xl p-6 flex items-center gap-6 group hover:shadow-xl hover:shadow-canina-blue/5 transition-all">
<div className="w-14 h-14 bg-white rounded-2xl flex items-center justify-center text-canina-blue shadow-sm group-hover:bg-canina-blue group-hover:text-white transition-all duration-300">
<Calendar className="w-7 h-7" />
</div>
<div className="flex flex-col items-center sm:items-start text-center sm:text-right">
<div className="text-base sm:text-3xl font-black text-canina-blue font-vazir leading-none">۱۹۸۴</div>
<div className="text-[9px] sm:text-xs text-medical-gray-500 font-extrabold uppercase tracking-tight mt-1 sm:mt-2 font-vazir">{getText('hero_stat_founded', "سال تاسیس برند")}</div>
<div className="text-right">
<div className="text-3xl font-black text-canina-blue font-vazir leading-none">۱۹۸۴</div>
<div className="text-xs text-medical-gray-500 font-extrabold uppercase tracking-tight mt-2 font-vazir">{getText('hero_stat_founded', "سال تاسیس برند در آلمان")}</div>
</div>
</div>
{/* Stat 2: Active Agencies */}
<div className="bg-medical-gray-50/50 border border-medical-gray-100 rounded-2xl sm:rounded-3xl p-2.5 sm:p-6 flex flex-col sm:flex-row items-center justify-center text-center sm:text-right gap-2 sm:gap-6 group hover:shadow-xl hover:shadow-canina-blue/5 transition-all">
<div className="w-9 h-9 sm:w-14 sm:h-14 bg-white rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue shadow-sm group-hover:bg-canina-blue group-hover:text-white transition-all duration-300 flex-shrink-0">
<Globe className="w-4 h-4 sm:w-7 sm:h-7" />
<div className="bg-medical-gray-50/50 border border-medical-gray-100 rounded-3xl p-6 flex items-center gap-6 group hover:shadow-xl hover:shadow-canina-blue/5 transition-all">
<div className="w-14 h-14 bg-white rounded-2xl flex items-center justify-center text-canina-blue shadow-sm group-hover:bg-canina-blue group-hover:text-white transition-all duration-300">
<Globe className="w-7 h-7" />
</div>
<div className="flex flex-col items-center sm:items-start text-center sm:text-right">
<div className="flex items-baseline justify-center sm:justify-start">
<div className="text-right">
<div className="flex items-baseline">
<StatCounter target={40} />
</div>
<div className="text-[9px] sm:text-xs text-medical-gray-500 font-extrabold uppercase tracking-tight mt-1 sm:mt-2 font-vazir">{getText('hero_stat_agencies', "نمایندگی فعال")}</div>
<div className="text-xs text-medical-gray-500 font-extrabold uppercase tracking-tight mt-2 font-vazir">{getText('hero_stat_agencies', "نمایندگی فعال جهانی")}</div>
</div>
</div>
{/* Stat 3: German Formula */}
<div className="bg-medical-gray-50/50 border border-medical-gray-100 rounded-2xl sm:rounded-3xl p-2.5 sm:p-6 flex flex-col sm:flex-row items-center justify-center text-center sm:text-right gap-2 sm:gap-6 group hover:shadow-xl hover:shadow-canina-blue/5 transition-all">
<div className="w-9 h-9 sm:w-14 sm:h-14 bg-white rounded-xl sm:rounded-2xl flex items-center justify-center text-canina-blue shadow-sm group-hover:bg-canina-blue group-hover:text-white transition-all duration-300 flex-shrink-0">
<ShieldCheck className="w-4 h-4 sm:w-7 sm:h-7" />
<div className="bg-medical-gray-50/50 border border-medical-gray-100 rounded-3xl p-6 flex items-center gap-6 group hover:shadow-xl hover:shadow-canina-blue/5 transition-all">
<div className="w-14 h-14 bg-white rounded-2xl flex items-center justify-center text-canina-blue shadow-sm group-hover:bg-canina-blue group-hover:text-white transition-all duration-300">
<ShieldCheck className="w-7 h-7" />
</div>
<div className="flex flex-col items-center sm:items-start text-center sm:text-right">
<div className="text-base sm:text-3xl font-black text-canina-blue font-vazir leading-none">۱۰۰٪</div>
<div className="text-[9px] sm:text-xs text-medical-gray-500 font-extrabold uppercase tracking-tight mt-1 sm:mt-2 font-vazir">{getText('hero_stat_german_formula', "فرمول آلمانی")}</div>
<div className="text-right">
<div className="text-3xl font-black text-canina-blue font-vazir leading-none">۱۰۰٪</div>
<div className="text-xs text-medical-gray-500 font-extrabold uppercase tracking-tight mt-2 font-vazir">{getText('hero_stat_german_formula', "فرمولاسیون علمی اختصاصی")}</div>
</div>
</div>
</motion.div>

View File

@ -6,14 +6,10 @@ import { FlaskConical, CheckCircle2, ChevronRight, ChevronLeft, Beaker, Search }
import { useSettingsStore } from "../lib/store/settingsStore";
import { productService } from "../lib/services/productService";
import Link from "next/link";
import { SCIENTIFIC_TERMS } from "../lib/data/scientificTerms";
import { useRouter, useSearchParams } from 'next/navigation';
import { useRouter } from 'next/navigation';
export default function IngredientWiki() {
const router = useRouter();
const searchParams = useSearchParams();
const termParam = searchParams.get('term');
const texts = useSettingsStore(state => state.texts);
const scientificTerms = useSettingsStore(state => state.scientificTerms);
const [ingredientsWiki, setIngredientsWiki] = useState<IngredientInfo[]>([]);
@ -25,51 +21,42 @@ export default function IngredientWiki() {
}, []);
useEffect(() => {
let list: IngredientInfo[] = [];
const raw = texts.ingredients_wiki || texts.ingredients_wiki_text;
if (raw) {
try {
const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
if (Array.isArray(parsed)) {
list = parsed.map((item: any) => ({
setIngredientsWiki(parsed.map((item: any) => ({
id: item.id,
name: item.name,
description: item.description,
benefits: Array.isArray(item.benefits) ? item.benefits : []
}));
})));
return;
}
} catch (e) {
console.error("Failed to parse ingredients_wiki:", e);
}
}
// Merge terms from scientificTerms and static SCIENTIFIC_TERMS dictionary
const mergedTerms = { ...SCIENTIFIC_TERMS, ...scientificTerms };
Object.entries(mergedTerms).forEach(([key, t]: [string, any]) => {
const wikiId = t.wikiId || t.key || key;
if (!list.some(item => item.id === wikiId || item.name.includes(t.term))) {
list.push({
id: wikiId,
const terms = Object.values(scientificTerms);
if (terms.length > 0) {
setIngredientsWiki(terms.map((t: any) => {
let benefits: string[] = [];
if (t.keywords) {
try {
const parsed = typeof t.keywords === 'string' ? JSON.parse(t.keywords) : t.keywords;
if (Array.isArray(parsed)) benefits = parsed;
} catch (e) {}
}
return {
id: t.key,
name: t.term,
description: t.definition,
benefits: []
});
}
});
setIngredientsWiki(list);
}, [texts, scientificTerms]);
useEffect(() => {
if (termParam && ingredientsWiki.length > 0) {
const el = document.getElementById(`wiki-${termParam}`);
if (el) {
setTimeout(() => {
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 100);
}
benefits
};
}));
}
}, [termParam, ingredientsWiki]);
}, [texts, scientificTerms]);
return (
<div className="bg-white py-12 px-4 overflow-hidden font-vazir" dir="rtl">
@ -163,36 +150,13 @@ export default function IngredientWiki() {
(ing.id === 'l-carnitine' && (miClean.includes('کارنیتین') || miClean.includes('carnitine'))) ||
(ing.id === 'seaweed' && (miClean.includes('جلبک') || miClean.includes('seaweed'))) ||
(ing.id === 'hyaluronic' && (miClean.includes('هیالورونات') || miClean.includes('hyaluron'))) ||
(ing.id === 'calendula-sea-buckthorn' && (miClean.includes('کالاندولا') || miClean.includes('سنجد') || miClean.includes('خولان') || miClean.includes('calendula'))) ||
(ing.id === 'colloidal-silver' && miClean.includes('کلوئیدی')) ||
(ing.id === 'retinol-palmitate' && (miClean.includes('رتینول') || miClean.includes('پالمیتات'))) ||
(ing.id === 'grapefruit-seed' && miClean.includes('گریپ')) ||
(ing.id === 'vaseline' && miClean.includes('وازلین')) ||
(ing.id === 'panthenol' && miClean.includes('پانتنول')) ||
(ing.id === 'diatomaceous' && miClean.includes('دیاتومه')) ||
(ing.id === 'artichoke' && miClean.includes('کنگر')) ||
(ing.id === 'brewers-yeast' && (miClean.includes('مخمر') || miClean.includes('yeast'))) ||
(ing.id === 'willow-bark' && miClean.includes('بید')) ||
(ing.id === 'ginger' && miClean.includes('زنجبیل')) ||
(ing.id === 'bovine-serum' && miClean.includes('سرم')) ||
(ing.id === 'bovine-blood' && miClean.includes('خون')) ||
(ing.id === 'beef-fat' && miClean.includes('چربی')) ||
(ing.id === 'argan-oil' && miClean.includes('آرگان')) ||
(ing.id === 'rosehip-oil' && miClean.includes('گل رز')) ||
(ing.id === 'walnut-oil' && miClean.includes('گردو')) ||
(ing.id === 'polyglyceryl-caprate' && (miClean.includes('پلی گلیسریل') || miClean.includes('کاپرات'))) ||
(ing.id === 'carbomer' && miClean.includes('کربومر')) ||
(ing.id === 'calendula-sea-buckthorn' && (miClean.includes('کالاندولا') || miClean.includes('سنجد') || miClean.includes('calendula'))) ||
(ing.id === 'dodecanoic' && (miClean.includes('دودکانوئیک') || miClean.includes('مارگوسا') || miClean.includes('insect')));
});
});
const isTargeted = termParam === ing.id;
return (
<div
key={ing.id}
id={`wiki-${ing.id}`}
className={`flex flex-col lg:flex-row items-center gap-16 p-6 rounded-[3rem] transition-all duration-500 ${isTargeted ? 'bg-canina-blue/5 border-2 border-canina-blue ring-4 ring-canina-blue/10 shadow-2xl' : ''} ${idx % 2 === 1 ? 'lg:flex-row-reverse' : ''}`}
>
<div key={ing.id} className={`flex flex-col lg:flex-row items-center gap-16 ${idx % 2 === 1 ? 'lg:flex-row-reverse' : ''}`}>
{/* Content */}
<div className="lg:w-1/2 space-y-8">
<motion.div
@ -237,14 +201,14 @@ export default function IngredientWiki() {
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 relative z-10">
{hasIngredient.length > 0 ? hasIngredient.map(p => (
<Link
<div
key={p.id}
href={`/shop/${p.slug || p.id}`}
className="bg-white rounded-[2.5rem] p-6 shadow-xl hover:scale-105 transition-transform cursor-pointer group block"
onClick={() => router.push(`/shop/${p.slug || p.id}`)}
className="bg-white rounded-[2.5rem] p-6 shadow-xl hover:scale-105 transition-transform cursor-pointer group"
>
<img src={p.image} alt={p.name} className="w-24 h-24 object-contain mx-auto mb-4" referrerPolicy="no-referrer" />
<h4 className="text-sm font-black text-center text-medical-gray-900 group-hover:text-canina-blue transition-colors px-2">{p.name}</h4>
</Link>
</div>
)) : (
<div className="col-span-full py-8 text-center">
<p className="text-medical-gray-400 font-bold italic mb-3">محصولی با این ماده مؤثره یافت نشد.</p>

View File

@ -27,16 +27,11 @@ import {
AlertTriangle,
ArrowRight,
TrendingUp,
Check,
Sparkles,
Beaker,
HelpCircle,
FileText
Check
} from "lucide-react";
import { usePetStore, PetProfile as GlobalPetProfile, Reminder, HealthLog, PetConsumption } from "../lib/store/usePetStore";
import { useCartStore } from "../lib/store/cartStore";
import SafeImage from "./SafeImage";
import SmartAdvisor from "./SmartAdvisor";
import { toast } from "sonner";
import { toPersian, cn } from "../lib/utils";
@ -170,29 +165,36 @@ export default function PetProfile({ initialView, advisorNeed }: {
let picks: { product: Product, reason?: string }[] = [];
// Priority 1: Match activePet.medicalConditions directly against product.symptoms
if (activePet.medicalConditions && activePet.medicalConditions.length > 0) {
products.forEach(p => {
if (p.suitableFor !== activePet.type && p.suitableFor !== "هر دو") return;
const matchedSymptoms = (p.symptoms || []).filter(s =>
activePet.medicalConditions.some(mc => mc.includes(s) || s.includes(mc))
);
if (matchedSymptoms.length > 0 && !picks.some(x => x.product.id === p.id)) {
picks.push({
product: p,
reason: `پیشنهاد هوشمند برای تسکین ${matchedSymptoms.join(" و ")}`
});
}
});
// Priority: Medical Conditions
if (activePet.medicalConditions.includes("جراحی مفاصل")) {
const gag = products.find(p => p.id === "canhydrox-gag");
if (gag) picks.push({ product: gag, reason: "پیشنهاد ویژه بر اساس سوابق جراحی مفاصل" });
}
// Priority 2: Biological factors
if (activePet.medicalConditions.includes("زایمان اخیر") || activePet.medicalConditions.includes("بارداری")) {
const booster = products.find(p => p.id === "immun-booster");
if (booster) picks.push({ product: booster, reason: "حمایت حیاتی دوران بارداری و زایمان" });
}
if (activePet.medicalConditions.includes("مشکلات گوارشی")) {
const moor = products.find(p => p.id === "moor-tranke");
if (moor) picks.push({ product: moor, reason: "تنظیم تخصصی سیستم گوارش" });
}
if (activePet.medicalConditions.includes("ریزش موی شدید")) {
const lachs = products.find(p => p.id === "lachs-ol");
if (lachs) picks.push({ product: lachs, reason: "تقویت تارهای مو و کنترل ریزش" });
}
if (activePet.medicalConditions.includes("بی‌اشتهایی")) {
const energy = products.find(p => p.id === "energy-gel");
if (energy) picks.push({ product: energy, reason: "تامین فوری انرژی و تحریک اشتها" });
}
// Biological factors
if (activePet.age > 7) {
const heart = products.find(p => p.id === "herz-vital" || p.slug?.includes("herz"));
if (heart && !picks.some(x => x.product.id === heart.id)) {
picks.push({ product: heart, reason: "محافظت از قلب پت‌های مسن" });
}
const heart = products.find(p => p.id === "herz-vital");
if (heart && !picks.some(x => x.product.id === heart.id)) picks.push({ product: heart, reason: "محافظت از قلب پت‌های مسن" });
}
// Fill defaults if needed
@ -381,24 +383,6 @@ export default function PetProfile({ initialView, advisorNeed }: {
// 2. Add / Edit Pet Steps
if (view === "add" || view === "edit") {
if (view === "add" && pets.length === 0) {
return (
<div className="min-h-screen bg-medical-gray-50 py-8 px-4 font-vazir" dir="rtl">
<div className="max-w-5xl mx-auto space-y-6">
{/* Render SmartAdvisor directly without redundant top banner */}
<SmartAdvisor
onComplete={(data) => {
const newPet: any = { ...data, id: Date.now().toString(), reminders: [], logs: [], consumptions: [] };
addPet(newPet);
setActivePet(newPet.id);
toast.success(`شناسنامه سلامت ${data.name} با موفقیت صادر شد`);
setView("detail");
}}
/>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-medical-gray-50 py-20 px-4 font-vazir" dir="rtl">
<div className="max-w-2xl mx-auto">

View File

@ -109,42 +109,38 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
const apiProduct = allProducts.find(p => p.id === product.id) || product;
if (!apiProduct) return null;
const staticProduct = PRODUCTS.find(p => p.id === apiProduct.id || p.artNo === apiProduct.artNo);
if (staticProduct && typeof staticProduct.calculateDosage === 'function') {
if (staticProduct) {
return {
...apiProduct,
calculateDosage: staticProduct.calculateDosage,
packageSize: apiProduct.packageSize || staticProduct.packageSize || 100
calculateDosage: staticProduct.calculateDosage
};
}
// Dynamic weight-based calculation fallback for any product without custom logic
return {
...apiProduct,
packageSize: apiProduct.packageSize || 100,
calculateDosage: (w: number, isYoung: boolean) => {
const factor = isYoung ? 0.2 : 0.1;
const baseQty = Math.max(1, Math.round(w * factor));
return {
quantity: baseQty,
unit: apiProduct.unit?.includes('ml') ? "میلی‌لیتر" : apiProduct.unit?.includes('g') ? "گرم" : "قرص",
description: isYoung ? "دوز رشد روزانه" : "دوز نگهداری روزانه"
};
}
};
if (typeof apiProduct.calculateDosage !== 'function') {
return {
...apiProduct,
calculateDosage: (w: number, y: boolean) => {
return {
quantity: 1,
unit: apiProduct.unit || "قرص",
description: "مصرف روزانه بر اساس دستور پزشک"
};
}
};
}
return apiProduct;
}, [product, allProducts]);
useEffect(() => {
if (activePet && activePet.weight > 0) {
if (activePet) {
setWeight(activePet.weight);
setPetType(activePet.age <= 1 ? "young" : "adult");
}
}, [activePet]);
}, [activePet, setWeight, setPetType]);
const calculation = useMemo(() => {
if (!fullProduct || typeof fullProduct.calculateDosage !== 'function') return null;
const result = fullProduct.calculateDosage(weight, petType === "young");
const packageSize = fullProduct.packageSize || 100;
const duration = Math.max(1, Math.floor(packageSize / (result.quantity || 1)));
const duration = Math.floor(fullProduct.packageSize / result.quantity);
return {
dailyDose: result.quantity,
@ -340,97 +336,64 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
</section>
)}
{/* Smart Dosage Calculator Utility Section / Topical Usage Notice */}
{fullProduct.categorySlug === 'special-care' ||
fullProduct.name.toLowerCase().includes('pfotenpflege') ||
fullProduct.name.toLowerCase().includes('augenpflege') ||
fullProduct.unit?.includes('ml Lotion') ||
fullProduct.unit?.includes('Balsam') ||
fullProduct.nameFa?.includes('کرم') ||
fullProduct.nameFa?.includes('قطره تمیزکننده') ||
fullProduct.nameFa?.includes('اسپری') ? (
<section className="bg-white rounded-[2.5rem] border border-medical-gray-200 p-8 md:p-10 shadow-md space-y-6">
<div className="flex items-center gap-4">
<div className="w-12 h-12 bg-canina-blue text-white rounded-2xl flex items-center justify-center shadow-lg">
<Sparkles className="w-6 h-6" />
{/* Smart Dosage Calculator Utility Section */}
<section className="bg-white rounded-[2.5rem] border border-medical-gray-200 p-8 md:p-10 shadow-md space-y-8">
<div className="flex items-center gap-4">
<div className="w-12 h-12 bg-canina-blue text-white rounded-2xl flex items-center justify-center shadow-lg">
<Clock className="w-6 h-6" />
</div>
<div>
<h3 className="text-2xl font-black text-medical-gray-900 italic font-vazir">ماشین حساب هوشمند مصرف</h3>
<p className="text-xs text-medical-gray-400 font-bold font-vazir mt-1">تخمین دقیق دوز روزانه و ماندگاری هر بسته بر اساس مشخصات پت شما</p>
</div>
</div>
<div className="grid md:grid-cols-2 gap-8 items-center">
{/* Manual Slider Inputs */}
<div className="bg-medical-gray-50 rounded-[2rem] p-6 border border-medical-gray-100 space-y-6">
<div className="flex items-center justify-between">
<span className="text-xs font-black text-medical-gray-500 font-vazir">وزن پت (کیلوگرم)</span>
<span className="text-lg font-black text-canina-blue font-vazir">{toPersian(weight)} ک‌گ</span>
</div>
<div>
<h3 className="text-2xl font-black text-medical-gray-900 italic font-vazir">نحوه استفاده موضعی و کاربرد</h3>
<p className="text-xs text-medical-gray-400 font-bold font-vazir mt-1">محصولات بهداشتی و بهداشت موضعی نیاز به دوزبندی بر اساس وزن ندارند</p>
<input
type="range"
min="1" max="100"
value={weight}
onChange={(e) => setWeight(parseInt(e.target.value))}
className="w-full h-2 bg-medical-gray-200 rounded-lg appearance-none cursor-pointer accent-canina-blue [&::-webkit-slider-thumb]:w-6 [&::-webkit-slider-thumb]:h-6 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-canina-blue [&::-webkit-slider-thumb]:appearance-none"
/>
<div className="space-y-2">
<span className="text-[10px] font-black text-medical-gray-400 font-vazir">سن حیوان</span>
<div className="flex gap-2">
<button onClick={() => setPetType("young")} className={`flex-1 py-3 rounded-xl text-xs font-black transition-all border ${petType === "young" ? 'bg-canina-blue border-canina-blue text-white shadow-lg' : 'bg-white border-medical-gray-200 text-medical-gray-400'}`}>جوان</button>
<button onClick={() => setPetType("adult")} className={`flex-1 py-3 rounded-xl text-xs font-black transition-all border ${petType === "adult" ? 'bg-canina-blue border-canina-blue text-white shadow-lg' : 'bg-white border-medical-gray-200 text-medical-gray-400'}`}>بالغ</button>
</div>
</div>
</div>
<div className="bg-medical-gray-50 rounded-[2rem] p-6 border border-medical-gray-100 flex items-start gap-4">
<div className="w-10 h-10 rounded-xl bg-canina-blue/10 text-canina-blue flex items-center justify-center font-black flex-shrink-0">
ℹ️
{/* Calculation Results Output */}
<div className="bg-canina-blue rounded-[2rem] p-8 text-white border-b-8 border-white/20 shadow-2xl flex flex-col justify-between h-full min-h-[220px]">
<div className="flex items-center gap-2 mb-4 opacity-90 border-b border-white/10 pb-3">
<Sparkles className="w-4 h-4 text-white" />
<span className="text-[10px] font-bold uppercase tracking-widest font-vazir text-white">تخمین هوش مصنوعی (AI Advice)</span>
</div>
<div>
<h4 className="text-sm font-black text-medical-gray-900 mb-1 font-vazir">راهنمای مصرف موضعی</h4>
<p className="text-xs text-medical-gray-600 font-bold font-vazir leading-relaxed">
این محصول یک فرآورده موضعی/بهداشتی است و مقدار مصرف آن به صورت موضعی (روی پوست، چشم یا پنجه) اعمال می‌شود و وابسته به وزن بدن حیوان نمی‌باشد. ماندگاری بسته بسته به میزان و تکرار مصرف روزانه شما متفاوت خواهد بود.
</p>
<div className="flex items-center justify-between">
<div>
<div className="text-3xl font-black font-vazir text-white">{toPersian(calculation.duration)} روز</div>
<div className="text-[10px] font-bold text-white/80 font-vazir whitespace-nowrap mt-1">تخمین اتمام مصرف بسته</div>
</div>
<div className="text-left">
<div className="text-2xl font-black font-vazir text-white">{toPersian(calculation.dailyDose)}</div>
<div className="text-[10px] font-bold text-white/80 font-vazir whitespace-nowrap mt-1">دوز روزانه ({calculation.unit === 'g' ? 'گرم' : calculation.unit})</div>
</div>
</div>
<p className="text-[11px] text-white/60 font-bold font-vazir mt-6 leading-relaxed">
* دوزها تخمینی هستند؛ توصیه می‌شود برای مصرف دقیق کاتالوگ محصول را مطالعه کنید.
</p>
</div>
</section>
) : (
<section className="bg-white rounded-[2.5rem] border border-medical-gray-200 p-8 md:p-10 shadow-md space-y-8">
<div className="flex items-center gap-4">
<div className="w-12 h-12 bg-canina-blue text-white rounded-2xl flex items-center justify-center shadow-lg">
<Clock className="w-6 h-6" />
</div>
<div>
<h3 className="text-2xl font-black text-medical-gray-900 italic font-vazir">ماشین حساب هوشمند مصرف</h3>
<p className="text-xs text-medical-gray-400 font-bold font-vazir mt-1">تخمین دقیق دوز روزانه و ماندگاری هر بسته بر اساس مشخصات پت شما</p>
</div>
</div>
<div className="grid md:grid-cols-2 gap-8 items-center">
{/* Manual Slider Inputs */}
<div className="bg-medical-gray-50 rounded-[2rem] p-6 border border-medical-gray-100 space-y-6">
<div className="flex items-center justify-between">
<span className="text-xs font-black text-medical-gray-500 font-vazir">وزن پت (کیلوگرم)</span>
<span className="text-lg font-black text-canina-blue font-vazir">{toPersian(weight)} ک‌گ</span>
</div>
<input
type="range"
min="1" max="100"
value={weight}
onChange={(e) => setWeight(parseInt(e.target.value))}
className="w-full h-2 bg-medical-gray-200 rounded-lg appearance-none cursor-pointer accent-canina-blue [&::-webkit-slider-thumb]:w-6 [&::-webkit-slider-thumb]:h-6 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-canina-blue [&::-webkit-slider-thumb]:appearance-none"
/>
<div className="space-y-2">
<span className="text-[10px] font-black text-medical-gray-400 font-vazir">سن حیوان</span>
<div className="flex gap-2">
<button onClick={() => setPetType("young")} className={`flex-1 py-3 rounded-xl text-xs font-black transition-all border ${petType === "young" ? 'bg-canina-blue border-canina-blue text-white shadow-lg' : 'bg-white border-medical-gray-200 text-medical-gray-400'}`}>جوان</button>
<button onClick={() => setPetType("adult")} className={`flex-1 py-3 rounded-xl text-xs font-black transition-all border ${petType === "adult" ? 'bg-canina-blue border-canina-blue text-white shadow-lg' : 'bg-white border-medical-gray-200 text-medical-gray-400'}`}>بالغ</button>
</div>
</div>
</div>
{/* Calculation Results Output */}
<div className="bg-canina-blue rounded-[2rem] p-8 text-white border-b-8 border-white/20 shadow-2xl flex flex-col justify-between h-full min-h-[220px]">
<div className="flex items-center gap-2 mb-4 opacity-90 border-b border-white/10 pb-3">
<Sparkles className="w-4 h-4 text-white" />
<span className="text-[10px] font-bold uppercase tracking-widest font-vazir text-white">تخمین هوش مصنوعی (AI Advice)</span>
</div>
<div className="flex items-center justify-between">
<div>
<div className="text-3xl font-black font-vazir text-white">{toPersian(calculation.duration)} روز</div>
<div className="text-[10px] font-bold text-white/80 font-vazir whitespace-nowrap mt-1">تخمین اتمام مصرف بسته</div>
</div>
<div className="text-left">
<div className="text-2xl font-black font-vazir text-white">{toPersian(calculation.dailyDose)}</div>
<div className="text-[10px] font-bold text-white/80 font-vazir whitespace-nowrap mt-1">دوز روزانه ({calculation.unit === 'g' ? 'گرم' : calculation.unit})</div>
</div>
</div>
<p className="text-[11px] text-white/60 font-bold font-vazir mt-6 leading-relaxed">
* دوزها تخمینی هستند؛ توصیه می‌شود برای مصرف دقیق کاتالوگ محصول را مطالعه کنید.
</p>
</div>
</div>
</section>
)}
</div>
</section>
{/* Ingredients Section */}
<section className="space-y-8">
@ -447,17 +410,7 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
<h4 className="text-xs font-black text-medical-gray-400 uppercase tracking-widest mb-6 font-vazir">مواد تشکیل‌دهنده برتر</h4>
<div className="flex flex-wrap gap-3">
{product.main_ingredients.map((ing, idx) => {
const termKey = termKeys.find(key => {
const termObj = scientificTerms[key] || SCIENTIFIC_TERMS[key];
const termName = termObj?.term || key;
const ingClean = ing.toLowerCase();
const keyClean = key.toLowerCase();
const termNameClean = termName.toLowerCase();
return ingClean.includes(keyClean) ||
keyClean.includes(ingClean) ||
ingClean.includes(termNameClean) ||
termNameClean.includes(ingClean);
});
const termKey = termKeys.find(key => ing.includes(key));
return (
<div key={idx} className="bg-medical-gray-50 border border-medical-gray-100 px-5 py-3 rounded-2xl flex items-center gap-3 group hover:border-canina-blue/30 transition-all">
<span className="text-sm font-bold text-medical-gray-700 font-vazir">

View File

@ -6,7 +6,6 @@ import { motion } from "motion/react";
import { Product } from "../lib/data/products";
import { productService } from "../lib/services/productService";
import Link from "next/link";
import { useRouter } from 'next/navigation';
export default function SearchResultsPage({ query }: { query: string }) {
@ -65,50 +64,46 @@ export default function SearchResultsPage({ query }: { query: string }) {
<motion.div
key={product.id}
layoutId={product.id}
className="h-full"
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"
>
<Link
href={`/shop/${product.slug || product.id}`}
className="bg-white rounded-[2.5rem] p-8 border border-medical-gray-200 group hover:shadow-2xl hover:shadow-canina-blue/10 transition-all flex flex-col relative h-full block"
>
<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.nameFa || 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.nameFa || product.name}</h3>
<p className="text-xs text-medical-gray-400 line-clamp-2 leading-relaxed mb-6">{product.benefits}</p>
</div>
<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.nameFa || 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.nameFa || 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>
<div 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" />
</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>
</Link>
<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>
)
})}
@ -129,12 +124,12 @@ export default function SearchResultsPage({ query }: { query: string }) {
<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 => (
<Link key={p.id} href={`/shop/${p.slug || p.id}`} className="cursor-pointer group block">
<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>
</Link>
</div>
))}
</div>
</div>

View File

@ -12,8 +12,7 @@ import {
Bone,
Sparkles,
Pill,
Sparkle,
Check
Sparkle
} from "lucide-react";
import { toPersian, cn } from "../lib/utils";
@ -26,24 +25,13 @@ interface SmartAdvisorProps {
onComplete?: (data: any) => void;
}
const CURRENT_SYMPTOMS = [
{ id: "joint_pain", label: "درد مفاصل و لنگیدن", condition: "درد مفاصل" },
{ id: "hair_loss", label: "ریزش مو، خارش و خشکی پوست", condition: "ریزش مو" },
{ id: "diarrhea", label: "اسهال، یبوست و مشکلات گوارشی", condition: "اسهال" },
{ id: "appetite", label: "بی‌اشتهایی و کاهش وزن", condition: "بی‌اشتهایی" },
{ id: "dental", label: "جرم دندان و بوی بد دهان", condition: "جرم دندان" },
{ id: "eye", label: "ترشحات یا مشکلات چشم", condition: "چشم" },
{ id: "puppy_growth", label: "رشد سریع و تعادل استخوانی", condition: "رشد سریع توله‌سگ" },
{ id: "tendon", label: "سفتی عضلات و ضعف تاندون", condition: "تاندون" },
];
const MEDICAL_HISTORIES = [
{ id: "joint_surgery", label: "سابقه جراحی مفاصل/استخوان", condition: "جراحی مفاصل" },
{ id: "recent_birth", label: "زایمان اخیر / شیردهی فعال", condition: "زایمان اخیر" },
{ id: "pregnancy", label: "دوران بارداری", condition: "بارداری" },
{ id: "weakness", label: "ضعف بعد از بیماری / نقاهت", condition: "ضعف بعد از بیماری" },
{ id: "heart", label: "نارسایی یا حساسیت قلبی", condition: "نارسایی قلبی" },
{ id: "parasite", label: "سابقه آلودگی انگلی", condition: "آلودگی انگلی" },
const MEDICAL_OPTIONS = [
{ id: "joint_surgery", label: "جراحی مفاصل", condition: "جراحی مفاصل" },
{ id: "recent_birth", label: "زایمان اخیر", condition: "زایمان اخیر" },
{ id: "pregnancy", label: "بارداری", condition: "بارداری" },
{ id: "digestion", label: "مشکلات گوارشی", condition: "مشکلات گوارشی" },
{ id: "hair_loss", label: "ریزش موی شدید", condition: "ریزش موی شدید" },
{ id: "appetite", label: "بی‌اشتهایی", condition: "بی‌اشتهایی" },
];
export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorProps & { rules?: any[] }) {
@ -53,6 +41,17 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
const { addPet, setActivePet } = usePetStore();
const texts = useSettingsStore(state => state.texts);
const getText = useSettingsStore(state => state.getText);
const medicalOptions = React.useMemo(() => {
const jsonStr = texts['medical_options'];
if (jsonStr) {
try {
return JSON.parse(jsonStr) as typeof MEDICAL_OPTIONS;
} catch (e) {
console.error("Failed to parse medical_options JSON:", e);
}
}
return MEDICAL_OPTIONS;
}, [texts]);
const [step, setStep] = useState(1);
@ -68,8 +67,7 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
const [weight, setWeight] = useState<number>(10);
const [activityLevel, setActivityLevel] = useState<"کم" | "متوسط" | "زیاد">("متوسط");
// Step 3 Data (Symptoms) & Step 4 Data (History)
const [currentSymptoms, setCurrentSymptoms] = useState<string[]>([]);
// Step 3 Data
const [medicalConditions, setMedicalConditions] = useState<string[]>([]);
const handleNext = () => {
@ -86,9 +84,8 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
const handleBack = () => setStep(s => s - 1);
const handleSubmit = () => {
const combinedConditions = Array.from(new Set([...currentSymptoms, ...medicalConditions]));
const data = {
name, type, breed, age, weight, activityLevel, medicalConditions: combinedConditions
name, type, breed, age, weight, activityLevel, medicalConditions
};
if (onComplete) {
onComplete(data);
@ -105,87 +102,28 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
};
return (
<section id="canino-advisor" className="py-16 bg-gradient-to-b from-white via-medical-gray-50/50 to-white">
<div className="max-w-5xl mx-auto px-4">
{/* Header Title */}
<div className="text-center mb-10">
<section id="canino-advisor" className="py-12 bg-white">
<div className="max-w-4xl mx-auto px-4">
<div className="text-center mb-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
className="inline-flex items-center gap-2 px-4 py-2 bg-canina-blue/5 rounded-full mb-4 border border-canina-blue/10"
className="inline-flex items-center gap-2 px-4 py-2 bg-canina-blue/5 rounded-full mb-4"
>
<Sparkle className="w-4 h-4 text-canina-blue animate-spin" />
<span className="text-canina-blue text-xs font-black uppercase tracking-widest font-vazir">{getText('advisor_badge', "سیستم پایش و تجویز هوشمند")}</span>
<Sparkle className="w-4 h-4 text-canina-blue" />
<span className="text-canina-blue text-xs font-black uppercase tracking-widest font-vazir">{getText('advisor_badge', "تکنولوژی پایش هوشمند")}</span>
</motion.div>
<h2 className="text-4xl lg:text-5xl font-black text-medical-gray-900 mb-4 font-lalezar tracking-wide">{getText('advisor_title', "دستیار سلامت و الگوریتم پایش کانینا")}</h2>
<p className="text-medical-gray-600 font-medium text-base font-shabnam max-w-2xl mx-auto leading-relaxed">
{getText('advisor_subtitle', "با ثبت اطلاعات همدم خود در ۴ قدم ساده، از الگوریتم علمی کانینا برای تجویز دقیق مکمل و پایش سلامت بهره‌مند شوید.")}
</p>
<h2 className="text-4xl lg:text-5xl font-black text-medical-gray-900 mb-4 font-lalezar tracking-wide">{getText('advisor_title', "دستیار سلامت کانینا")}</h2>
<p className="text-medical-gray-600 font-medium text-base font-shabnam">{getText('advisor_subtitle', "فقط چند قدم تا صدور شناسنامه هوشمند و دریافت پیشنهادات تخصصی")}</p>
</div>
{/* Value Proposition Cards (Explaining Why & How) */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="grid md:grid-cols-3 gap-6 mb-12"
>
<div className="bg-white p-6 rounded-[2.5rem] border border-medical-gray-200/80 shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between">
<div>
<div className="w-12 h-12 bg-canina-blue/10 rounded-2xl flex items-center justify-center text-canina-blue mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm">
<Pill className="w-6 h-6" />
</div>
<h3 className="text-lg font-black text-medical-gray-900 mb-2 font-vazir">چرا ثبت شناسنامه؟</h3>
<p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir">
مکمل‌های دارویی کانینا غلیظ و تخصصی هستند؛ الگوریتم هوشمند ما بر اساس سن، وزن و جثه پت شما، دوز دقیق مصرفی روزانه را محاسبه می‌کند تا حداکثر اثرگذاری ایجاد شود.
</p>
</div>
<div className="mt-4 pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir">
<Check className="w-3.5 h-3.5" />
تضمین ایمنی و عدم اوردوز
</div>
</div>
<div className="bg-white p-6 rounded-[2.5rem] border border-medical-gray-200/80 shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between">
<div>
<div className="w-12 h-12 bg-canina-blue/10 rounded-2xl flex items-center justify-center text-canina-blue mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm">
<Activity className="w-6 h-6" />
</div>
<h3 className="text-lg font-black text-medical-gray-900 mb-2 font-vazir">تطبیق علائم بالینی</h3>
<p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir">
با انتخاب علائمی مثل لنگیدن، ریزش مو، یا اسهال، موتور هوشمند ما دقیق‌ترین راهکار مکمل آلمانی مرتبط را به همراه دلایل علمی به شما پیشنهاد می‌دهد.
</p>
</div>
<div className="mt-4 pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir">
<Check className="w-3.5 h-3.5" />
پیشنهاد هدفمند و تخصصی
</div>
</div>
<div className="bg-white p-6 rounded-[2.5rem] border border-medical-gray-200/80 shadow-lg hover:shadow-xl transition-all group flex flex-col justify-between">
<div>
<div className="w-12 h-12 bg-canina-blue/10 rounded-2xl flex items-center justify-center text-canina-blue mb-4 group-hover:bg-canina-blue group-hover:text-white transition-all shadow-sm">
<ShieldCheck className="w-6 h-6" />
</div>
<h3 className="text-lg font-black text-medical-gray-900 mb-2 font-vazir">پایش موجودی و یادآور</h3>
<p className="text-xs text-medical-gray-500 font-medium leading-relaxed font-vazir">
پس از ایجاد شناسنامه، زمان‌های مصرف مکمل‌ها تنظیم شده و سیستم قبل از اتمام دوره درمانی، پیام هشدار شارژ مجدد به شما ارسال خواهد کرد.
</p>
</div>
<div className="mt-4 pt-4 border-t border-medical-gray-100 text-[10px] font-black text-canina-blue flex items-center gap-1 font-vazir">
<Check className="w-3.5 h-3.5" />
تکمیل منظم دوره درمان
</div>
</div>
</motion.div>
<div className="bg-medical-gray-50 rounded-[3.5rem] border border-medical-gray-100 p-8 lg:p-12 shadow-2xl shadow-medical-gray-200/50 relative overflow-hidden">
{/* Progress Indicator */}
<div className="absolute top-0 left-0 w-full h-2 bg-medical-gray-200">
<motion.div
className="h-full bg-canina-blue"
initial={{ width: "0%" }}
animate={{ width: `${(step / 4) * 100}%` }}
animate={{ width: `${(step / 3) * 100}%` }}
transition={{ duration: 0.5 }}
/>
</div>
@ -339,7 +277,7 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
onClick={handleNext}
className="flex-[2] py-6 bg-medical-gray-900 text-white rounded-3xl font-black text-lg hover:bg-canina-blue focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all shadow-xl shadow-medical-gray-900/10 flex items-center justify-center gap-3 font-vazir min-h-[48px]"
>
{getText('advisor_next_symptoms', "بررسی علائم بالینی فعلی")}
{getText('advisor_next_final', "گام نهایی سلامت")}
<ChevronLeft className="w-5 h-5" />
</button>
</div>
@ -355,63 +293,12 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
className="space-y-8"
>
<div className="text-center">
<h3 className="text-2xl font-black text-medical-gray-900 mb-2 font-vazir uppercase italic tracking-tight">{getText('advisor_step3_symptoms_title', "گام سوم: علائم بالینی فعلی")}</h3>
<p className="text-medical-gray-600 font-bold font-vazir">{getText('advisor_step3_symptoms_desc', "آیا همدم شما در حال حاضر هیچ‌کدام از علائم زیر را تجربه می‌کند؟")}</p>
<h3 className="text-2xl font-black text-medical-gray-900 mb-2 font-vazir uppercase italic tracking-tight">{getText('advisor_step3_title', "گام آخر: سوابق و حساسیت‌ها")}</h3>
<p className="text-medical-gray-600 font-bold font-vazir">{getText('advisor_step3_desc', "در صورت وجود هر یک از موارد زیر، آن را تیک بزنید")}</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{CURRENT_SYMPTOMS.map((opt) => (
<button
key={opt.id}
onClick={() => {
setCurrentSymptoms(prev =>
prev.includes(opt.condition) ? prev.filter(c => c !== opt.condition) : [...prev, opt.condition]
);
}}
className={`flex items-center gap-4 p-5 rounded-2xl border-2 transition-all text-right focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none min-h-[48px] ${currentSymptoms.includes(opt.condition) ? 'border-canina-blue bg-white shadow-md' : 'border-transparent bg-white/50 text-medical-gray-600 hover:bg-white'}`}
>
<div className={`w-8 h-8 rounded-lg flex items-center justify-center border ${currentSymptoms.includes(opt.condition) ? 'bg-canina-blue border-canina-blue text-white' : 'border-medical-gray-300 text-transparent'}`}>
<ShieldCheck className="w-5 h-5" />
</div>
<span className={`text-sm font-black font-vazir ${currentSymptoms.includes(opt.condition) ? 'text-canina-blue' : 'text-medical-gray-900'}`}>{opt.label}</span>
</button>
))}
</div>
<div className="flex gap-4 pt-4">
<button
onClick={handleBack}
className="flex-1 py-6 bg-medical-gray-100 text-medical-gray-600 rounded-3xl font-black text-lg hover:bg-medical-gray-200 focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all flex items-center justify-center gap-3 font-vazir min-h-[48px]"
>
<ChevronRight className="w-5 h-5" />
{getText('advisor_back', "قبلی")}
</button>
<button
onClick={handleNext}
className="flex-[2] py-6 bg-medical-gray-900 text-white rounded-3xl font-black text-lg hover:bg-canina-blue focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none transition-all shadow-xl shadow-medical-gray-900/10 flex items-center justify-center gap-3 font-vazir min-h-[48px]"
>
{getText('advisor_next_history', "سوابق پزشکی و جراحی")}
<ChevronLeft className="w-5 h-5" />
</button>
</div>
</motion.div>
)}
{step === 4 && (
<motion.div
key="step4"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
className="space-y-8"
>
<div className="text-center">
<h3 className="text-2xl font-black text-medical-gray-900 mb-2 font-vazir uppercase italic tracking-tight">{getText('advisor_step4_history_title', "گام چهارم: سوابق پزشکی و جراحی")}</h3>
<p className="text-medical-gray-600 font-bold font-vazir">{getText('advisor_step4_history_desc', "در صورت وجود سابقه جراحی، بارداری یا نارسایی، آن را مشخص کنید")}</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{MEDICAL_HISTORIES.map((opt) => (
{medicalOptions.map((opt) => (
<button
key={opt.id}
onClick={() => {

View File

@ -32,31 +32,28 @@ export default function Tooltip({ termKey, children, onWikiNavigate }: TooltipPr
initial={{ opacity: 0, y: 10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.95 }}
className="absolute bottom-full left-1/2 -translate-x-1/2 mb-3 w-72 p-5 bg-white text-medical-gray-900 rounded-2xl shadow-2xl border border-medical-gray-200 z-[100] text-sm"
className="absolute bottom-full left-1/2 -translate-x-1/2 mb-3 w-64 p-4 bg-medical-gray-900 text-white rounded-2xl shadow-2xl z-[100] text-sm"
dir="rtl"
>
<div className="flex items-center gap-2 mb-2 text-canina-blue">
<Info className="w-4 h-4" />
<span className="font-black text-[11px] uppercase tracking-widest text-canina-blue">دانشنامه علمی کانینا</span>
<span className="font-black text-[10px] uppercase tracking-widest">دانشنامه علمی کانینا</span>
</div>
<h5 className="font-black text-base text-medical-gray-900 mb-2 leading-snug">{termData.term}</h5>
<p className="text-medical-gray-600 text-xs leading-relaxed mb-4 font-bold">
<h5 className="font-black mb-1">{termData.term}</h5>
<p className="text-white/70 text-xs leading-relaxed mb-3">
{termData.definition}
</p>
{termData.wikiId && (
<button
onClick={(e) => {
e.stopPropagation();
onWikiNavigate?.(termData.wikiId!);
}}
className="flex items-center justify-between text-canina-blue hover:text-blue-700 transition-colors text-xs font-black cursor-pointer pt-2 border-t border-medical-gray-100 w-full"
onClick={() => onWikiNavigate?.(termData.wikiId!)}
className="flex items-center gap-1.5 text-canina-blue hover:text-white transition-colors text-[10px] font-bold"
>
<span>مطالعه مقاله کامل</span>
<ExternalLink className="w-3.5 h-3.5" />
<ExternalLink className="w-3 h-3" />
</button>
)}
{/* Arrow */}
<div className="absolute top-full left-1/2 -translate-x-1/2 border-8 border-transparent border-t-white" />
<div className="absolute top-full left-1/2 -translate-x-1/2 border-8 border-transparent border-t-medical-gray-900" />
</motion.div>
)}
</AnimatePresence>

View File

@ -1055,9 +1055,7 @@ export const PRODUCTS: Product[] = [
symptoms: ["توله بدون مادر", "نیاز به شیردهی مصنوعی"],
suitableFor: "سگ",
calculateDosage: (weight) => {
// 1 part milk powder (approx 10g per kg of puppy weight) mixed with 4 parts water
const grams = Math.max(10, Math.round(weight * 10));
return { quantity: grams, unit: "گرم پودر", description: `مخلوط ${grams} گرم پودر با ${grams * 4} میلی‌لیتر آب ۵۰ درجه` };
return { quantity: 1, unit: "قسمت", description: "مخلوط ۱:۴ با آب ۵۰ درجه سانتی‌گراد" };
},
analysis: { "پروتئین": "۳۰٪" },
feedingAdvice: "هر ۲ ساعت در هفته اول، سپس هر ۴ ساعت.",
@ -1088,10 +1086,8 @@ export const PRODUCTS: Product[] = [
symptoms: ["توله ۳ هفته", "شروع غذای کمکی"],
suitableFor: "سگ",
contraindications: ["مصرف همزمان ویتامین D2 مجاز نیست"],
calculateDosage: (weight) => {
// 1 part porridge powder (approx 15g per kg of puppy weight) mixed with 3 parts warm water
const grams = Math.max(15, Math.round(weight * 15));
return { quantity: grams, unit: "گرم فرنی", description: `مخلوط ${grams} گرم فرنی با ${grams * 3} میلی‌لیتر آب ۵۰ درجه` };
calculateDosage: () => {
return { quantity: 1, unit: "قسمت", description: "مخلوط ۱:۳ با آب ۵۰ درجه" };
},
analysis: { "پروتئین": "۲۷٪" },
feedingAdvice: "کم‌کم از هفته سوم به عنوان غذای کمکی معرفی شود.",

View File

@ -125,106 +125,6 @@ export const SCIENTIFIC_TERMS: Record<string, ScientificTerm> = {
definition: "روغن‌های تسکین‌دهنده، ضدباکتری و التیام‌بخش پوست جهت بهبود سوزش، زخم‌های سطحی و خشکی پنجه‌ها.",
wikiId: "calendula-sea-buckthorn"
},
"نقره کلوئیدی": {
term: "نقره کلوئیدی (Colloidal Silver)",
definition: "محلول استریل حاوی ذرات ریز نقره با اثرات قوی آنتی‌باکتریال و ضدالتهاب طبیعی جهت پاکسازی ملتحمه چشم و جلوگیری از عفونت‌های چشمی و پوستی.",
wikiId: "colloidal-silver"
},
"رتینول پالمیتات": {
term: "رتینول پالمیتات / ویتامین A (Retinol Palmitate)",
definition: "فرمی پایدار از ویتامین A دارویی که باعث ترمیم لایه‌های مخاطی قرنیه چشم، بازسازی اپیتلیوم آسیب‌دیده و محافظت در برابر خشکی قرنیه می‌شود.",
wikiId: "retinol-palmitate"
},
"روغن خولان دریایی": {
term: "روغن خولان دریایی / سنجد دریایی (Sea Buckthorn Oil)",
definition: "روغن غنی از ویتامین E، امگا ۷ و آنتی‌اکسیدان‌های کمیاب برای ترمیم سریع بافت‌های آسیب‌دیده، التهابات پوستی و ترک‌های پنجه.",
wikiId: "calendula-sea-buckthorn"
},
"سنجد دریایی": {
term: "روغن سنجد دریایی (Sea Buckthorn Oil)",
definition: "روغن غنی از ویتامین E، امگا ۷ و آنتی‌اکسیدان‌های کمیاب برای ترمیم سریع بافت‌های آسیب‌دیده و ترک‌خورده پوست.",
wikiId: "calendula-sea-buckthorn"
},
"وازلین": {
term: "وازلین با گرید دارویی (Medical Vaseline)",
definition: "پوشش نرم‌کننده و ایزوله‌کننده پوست که مانع تبخیر رطوبت لایه‌های اپیدرم شده و از پنجه‌ها در برابر نمک، یخ و زبری زمین محافظت می‌کند.",
wikiId: "vaseline"
},
"پانتنول": {
term: "پانتنول / پیش‌ساز ویتامین B5 (Panthenol)",
definition: "ترکیب نفوذکننده و ترمیم‌کننده عمیق پوست که سرعت بازسازی سلولی را افزایش داده و خارش و التهاب پوست و پنجه را تسکین می‌دهد.",
wikiId: "panthenol"
},
"عصاره دانه گریپ فروت": {
term: "عصاره دانه گریپ فروت (Grapefruit Seed Extract)",
definition: "منبع غنی بیوفلاونوئیدها و آنتی‌اکسیدان‌های طبیعی که با مهار باکتری‌های مولد پلاک، از تشکیل تارتار و جرم دندان جلوگیری می‌کند.",
wikiId: "grapefruit-seed"
},
"گریپ فروت": {
term: "عصاره دانه گریپ فروت (Grapefruit Seed Extract)",
definition: "منبع غنی بیوفلاونوئیدها و آنتی‌اکسیدان‌های طبیعی که با مهار باکتری‌های مولد پلاک، از تشکیل تارتار و جرم دندان جلوگیری می‌کند.",
wikiId: "grapefruit-seed"
},
"عصاره مارگوسا": {
term: "عصاره طبیعی درخت نیم / مارگوسا (Margosa Extract)",
definition: "عصاره گیاهی دورکننده حشرات با خواص ضد انگل، کک و کنه بدون استفاده از سموم شیمیایی آفت‌کش.",
wikiId: "dodecanoic"
},
"خاک دیاتومه": {
term: "خاک دیاتومه ارگانیک (Diatomaceous Earth)",
definition: "ماده طبیعی حاصل از سنگ‌های رسوبی جلبک‌های تک‌سلولی جهت کنترل ارگانیک انگل‌ها و انباشت مواد سمی در روده.",
wikiId: "diatomaceous"
},
"کنگر فرنگی": {
term: "عصاره کنگر فرنگی (Artichoke Extract)",
definition: "ترکیب گیاهی محافظ کبد و بهبوددهنده ترشح صفرا جهت ارتقای هضم چربی‌ها و پاکسازی متابولیک.",
wikiId: "artichoke"
},
"مخمر آبجو": {
term: "مخمر آبجو (Brewer's Yeast)",
definition: "منبع غنی ویتامین‌های گروه B، اسیدهای آمینه و مواد معدنی برای سلامت پوست، درخشندگی موها و تقویت اشتهای حیوان.",
wikiId: "brewers-yeast"
},
"پوست درخت بید": {
term: "عصاره پوست درخت بید (Willow Bark Extract)",
definition: "منبع طبیعی سالیسین با خواص ضدالتهابی و تسکین‌دهنده دردهای مفصلی و تاندون‌ها در حیوانات مسن.",
wikiId: "willow-bark"
},
"زنجبیل": {
term: "عصاره زنجبیل (Ginger Extract)",
definition: "آنتی‌اکسیدان گیاهی قوی برای مهار رادیکال‌های آزاد، بهبود گردش خون در مفاصل و تسکین حالت تهوع و مشکلات گوارشی.",
wikiId: "ginger"
},
"سرم طبیعی گاو": {
term: "سرم طبیعی خون گاو (Bovine Blood Serum)",
definition: "کنسانتره غنی از پروتئین‌ها و بیواکتیواتورهای طبیعی برای تحریک اشتها و تسریع بازگشت انرژی در دوره نقاهت.",
wikiId: "bovine-serum"
},
"پودر خون گاو": {
term: "پودر خون گاو / هموگلوبین (Bovine Blood Powder)",
definition: "منبع ۱۰۰٪ طبیعی آهن آلی (هموگلوبین) و اسیدهای آمینه ضروری جهت اکسیژن‌رسانی به خون و عضله‌سازی در رژیم خام BARF.",
wikiId: "bovine-blood"
},
"چربی گوشت گاو": {
term: "چربی تصفیه شده گاو (Beef Fat Powder)",
definition: "سوخت انرژی‌بخش حیوانی زودجذب جهت وزن‌گیری سریع، تامین کالری سگ‌های کاری و افزایش اشتهای سگ‌های بدخوراک.",
wikiId: "beef-fat"
},
"روغن آرگان": {
term: "روغن آرگان خالص (Pure Argan Oil)",
definition: "روغن نایاب سرشار از فیتواسترول‌ها، اسکوالن و ویتامین E طبیعی برای درخشندگی موها، تغذیه پوست و تقویت غشاهای سلولی.",
wikiId: "argan-oil"
},
"روغن دانه گل رز": {
term: "روغن دانه گل رز (Rosehip Seed Oil)",
definition: "منبع طبیعی اسیدهای چرب امگا ۳ و پروویتامین A برای ترمیم مخاط‌ها و لایه‌های محافظ پوست.",
wikiId: "rosehip-oil"
},
"روغن گردو": {
term: "روغن گردو (Walnut Oil)",
definition: "روغن گیاهی غنی از اسیدهای چرب غیراشباع و آنتی‌اکسیدان‌ها برای سلامت سیستم عصبی و پوست.",
wikiId: "walnut-oil"
},
"تورین": {
term: "تورین (Taurine)",
definition: "آمینو اسید بسیار ضروری برای گربه‌ها جهت جلوگیری از دژنراسیون شبکیه (نابینایی) و نارسایی قلبی.",
@ -234,171 +134,6 @@ export const SCIENTIFIC_TERMS: Record<string, ScientificTerm> = {
term: "گرید دارویی (Pharmaceutical Grade)",
definition: "بالاترین سطح خلوص مواد اولیه؛ تهیه شده با استانداردهای دارویی انسان و کاملاً بی‌خطر در صورت لیسیده شدن توسط حیوان."
},
"پلی گلیسریل-4-کاپرات": {
term: "پلی گلیسریل-4-کاپرات (Polyglyceryl-4 Caprate)",
definition: "امولسیفایر گیاهی ملایم و زیست‌تخریب‌پذیر حاصل از گلیسرول و اسیدهای چرب نارگیل جهت انحلال یکنواخت ترکیبات فعال بدون ایجاد سوزش پوست.",
wikiId: "polyglyceryl-caprate"
},
"کربومر": {
term: "کربومر دارویی (Carbomer / Polyacrylic Acid)",
definition: "پلیمر قوام‌دهنده و ویسکوزکننده فرمولاسیون‌های ژل که پایداری مواد فعال بر سطح دندان و پوست را افزایش می‌دهد.",
wikiId: "carbomer"
},
"پلی گلیسریل": {
term: "پلی گلیسریل-4-کاپرات (Polyglyceryl-4 Caprate)",
definition: "امولسیفایر گیاهی ملایم حاصل از گلیسرول و اسیدهای چرب نارگیل جهت انحلال یکنواخت ترکیبات فعال بدون ایجاد سوزش.",
wikiId: "polyglyceryl-caprate"
},
"اتانول": {
term: "اتانول دارویی (Pharmaceutical Ethanol)",
definition: "حلال استریل و عصاره‌گیر پایه حیوانی و گیاهی با غلظت استاندارد و ایمن برای نگهداری محلول‌های بیواکتیو.",
wikiId: "ethanol"
},
"دکستروز": {
term: "دکستروز / گلوکز مونوهیدرات (Dextrose)",
definition: "قند ساده زودجذب و منبع فوری انرژی سلولی برای حیوانات ضعیف، بیمار یا دچار افت قند خون.",
wikiId: "dextrose"
},
"آب پنیر": {
term: "پودر آب پنیر (Whey Powder)",
definition: "منبع غنی پروتئین‌های زودهضم، کلسیم ارگانیک و اسیدهای آمینه شاخه‌دار جهت رشد و بالانس غذایی.",
wikiId: "whey-powder"
},
"نشاسته سیب زمینی": {
term: "نشاسته سیب زمینی (Potato Starch)",
definition: "کربوهیدرات پیچیده و عامل اتصال‌دهنده طبیعی در قرص‌ها و ژل‌ها بدون ایجاد حساسیت گوارشی.",
wikiId: "potato-starch"
},
"لاکتوز": {
term: "لاکتوز دارویی (Lactose)",
definition: "قند طبیعی شیر به عنوان پایه انرژی‌بخش و طعم‌دهنده طبیعی در مکمل‌های جایگزین شیر مادر.",
wikiId: "lactose"
},
"چربی نارگیل": {
term: "چربی و روغن نارگیل (Coconut Fat)",
definition: "منبع اسیدهای چرب زنجیره متوسط (MCT) جهت تامین انرژی سریع مغز و دستگاه گوارش.",
wikiId: "coconut-fat"
},
"پروتئین ایزوله سویا": {
term: "پروتئین ایزوله سویا (Soy Protein Isolate)",
definition: "پروتئین خالص گیاهی با قابلیت هضم فوق‌العاده بالا برای ساخت بافت‌های عضلانی.",
wikiId: "soy-protein"
},
"پودر تخم مرغ کامل": {
term: "پودر تخم مرغ کامل (Whole Egg Powder)",
definition: "غذای کامل سرشار از پروتئین‌های با ارزش بیولوژیک بالا، لسیپین و اسیدهای چرب ضروری.",
wikiId: "egg-powder"
},
"کربنات کلسیم": {
term: "کربنات کلسیم (Calcium Carbonate)",
definition: "منبع آلی معدنی کلسیم برای استحکام استخوان‌ها، دندان‌ها و عملکرد صحیح انقباض عضلانی.",
wikiId: "calcium-carbonate"
},
"تری کلسیم فسفات": {
term: "تری کلسیم فسفات (Tricalcium Phosphate)",
definition: "ترکیب معدنی فوق‌العاده شکل‌دهنده ساختار استخوان‌ها (بیش از ۵۰٪) و مینای دندان‌ها (بیش از ۹۰٪).",
wikiId: "tricalcium-phosphate"
},
"ویتامین A": {
term: "ویتامین A (Retinol)",
definition: "ویتامین محلول در چربی ضروری برای سلامت بینایی، بازسازی مخاط و سیستم ایمنی.",
wikiId: "vitamins"
},
"ویتامین E": {
term: "ویتامین E (Tocopherol)",
definition: "آنتی‌اکسیدان قوی محافظ غشای سلولی در برابر استرس اکسیداتیو و رادیکال‌های آزاد.",
wikiId: "vitamins"
},
"ویتامین C": {
term: "ویتامین C (L-Ascorbic Acid)",
definition: "ویتامین تقویت‌کننده ساخت کلاژن، لثه‌ها و سیستم ایمنی بدن.",
wikiId: "vitamins"
},
"ویتامین B1": {
term: "ویتامین B1 (Thiamine)",
definition: "ویتامین حیاتی برای سیستم عصبی و متابولیسم کربوهیدرات‌ها.",
wikiId: "vitamins"
},
"ویتامین B2": {
term: "ویتامین B2 (Riboflavin)",
definition: "کوآنزیم فاکتورهای رشد، درخشندگی پوشش مویی و سلامت پوست.",
wikiId: "vitamins"
},
"ویتامین B6": {
term: "ویتامین B6 (Pyridoxine)",
definition: "ویتامین ضروری برای سنتز اسیدهای آمینه و هموگلوبین خون.",
wikiId: "vitamins"
},
"ویتامین B12": {
term: "ویتامین B12 (Cobalamin)",
definition: "فاکتور کلیدی خون‌سازی و حفظ سلامت رشته‌های عصبی.",
wikiId: "vitamins"
},
"اسید نیکوتینیک": {
term: "اسید نیکوتینیک / نیاسین (Niacin / Vitamin B3)",
definition: "ویتامین تنظیم‌کننده پیگمنت‌های پوست و متابولیسم چربی‌ها.",
wikiId: "vitamins"
},
"سلنیوم": {
term: "سلنیوم (Selenium)",
definition: "عنصر کمیاب آنتی‌اکسیدانی که همراه با ویتامین E از تخریب سلولی جلوگیری می‌کند.",
wikiId: "selenium"
},
"روی": {
term: "روی (Zinc)",
definition: "عنصر ضروری برای ترمیم زخم‌ها، رشد موها و تقویت سیستم ایمنی.",
wikiId: "zinc"
},
"مس": {
term: "مس (Copper)",
definition: "عنصر کاتالیزور در سنتز هموگلوبین و پیگمنت‌های رنگی مو.",
wikiId: "copper"
},
"آهن": {
term: "آهن (Iron)",
definition: "ماده معدنی اصلی ساخت هموگلوبین و اکسیژن‌رسانی به بافت‌ها.",
wikiId: "iron"
},
"متیونین": {
term: "متیونین (DL-Methionine)",
definition: "اسید آمینه گوگرددار ضروری برای سلامت مفاصل، کبد و رشد مو.",
wikiId: "methionine"
},
"کیک بذر کتان": {
term: "کیک بذر کتان (Flaxseed Cake)",
definition: "منبع غنی فیبرهای حل‌شونده و اسید چرب آلفا لینولنیک برای دستگاه گوارش.",
wikiId: "flaxseed"
},
"تفاله سیب": {
term: "تفاله سیب ارگانیک (Apple Pomace)",
definition: "منبع پری‌بیوتیک طبیعی پکتین جهت تنظیم فلور و حرکات روده.",
wikiId: "apple-pomace"
},
"پودر یونجه سبز": {
term: "پودر یونجه سبز (Alfalfa Powder)",
definition: "گیاه غنی از کلروفیل، ویتامین K و مواد معدنی قلیایی‌کننده.",
wikiId: "alfalfa"
},
"فیبر نخود فرنگی": {
term: "فیبر نخود فرنگی (Pea Fiber)",
definition: "فیبر غیرقابل هضم پاک‌کننده مکانیکی جدار روده و حجم‌دهنده مدفوع.",
wikiId: "pea-fiber"
},
"تفاله هویج": {
term: "تفاله هویج (Carrot Pomace)",
definition: "سرشار از بتاکاروتن و فیبر برای سلامت بینایی و گوارش.",
wikiId: "carrot-pomace"
},
"روغن کلزا": {
term: "روغن کلزا (Rapeseed Oil)",
definition: "روغن گیاهی غنی از امگا ۳ و ۶ برای لطافت و نرمی پوست.",
wikiId: "rapeseed-oil"
},
"روغن ماهی": {
term: "روغن خالص ماهی (Fish Oil)",
definition: "منبع اسیدهای چرب امگا ۳ برای سلامت قلب، مفاصل و پوست.",
wikiId: "salmon-oil"
},
"IFS": {
term: "استاندارد بین‌المللی IFS",
definition: "سخت‌گیرانه‌ترین استاندارد ایمنی و کیفیت محصولات دارویی و غذایی در اروپا."