Compare commits
23 Commits
fd45910387
...
0d805b0eb9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d805b0eb9 | ||
|
|
b0c9e9e88c | ||
|
|
320ddad1e5 | ||
|
|
d560eb79a3 | ||
|
|
3e3372d01b | ||
|
|
99ebfea8d0 | ||
|
|
587407c39a | ||
|
|
6618a81f33 | ||
|
|
88b2fdbb14 | ||
|
|
56515f24c6 | ||
|
|
67444aa251 | ||
|
|
fb23831640 | ||
|
|
24ce3a44bf | ||
|
|
57cab6cbc2 | ||
|
|
53d239b2f2 | ||
|
|
78f0727e92 | ||
|
|
6d568d56ae | ||
|
|
ffa6da7978 | ||
|
|
22dc217534 | ||
|
|
dc8a1bb079 | ||
|
|
1b914d25a5 | ||
|
|
fcc738f25c | ||
|
|
f762105f09 |
@ -75,16 +75,16 @@ export default function HomeClient() {
|
|||||||
{/* About Section Teaser */}
|
{/* About Section Teaser */}
|
||||||
<section className="py-16 bg-white overflow-hidden font-sans">
|
<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="max-w-7xl mx-auto px-4 grid lg:grid-cols-2 gap-16 lg:gap-24 items-center">
|
||||||
<div className="relative">
|
<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" />
|
<div className="absolute -top-10 -right-10 w-40 h-40 bg-canina-blue/5 rounded-full blur-3xl" />
|
||||||
<img
|
<img
|
||||||
src="/assets/images/regenerated_image_1779109861747.png"
|
src="/assets/images/regenerated_image_1779109861747.png"
|
||||||
alt="آزمایشگاه تخصصی تولید مکملهای دارویی کانینا آلمان"
|
alt="آزمایشگاه تخصصی تولید مکملهای دارویی کانینا آلمان"
|
||||||
className="rounded-[3rem] shadow-2xl relative z-10 grayscale-[10%]"
|
className="rounded-[3rem] shadow-2xl relative z-10 grayscale-[10%] w-full"
|
||||||
/>
|
/>
|
||||||
<div className="absolute -bottom-6 -left-6 bg-canina-blue text-white p-8 rounded-[2rem] shadow-xl z-20">
|
<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-4xl font-black mb-1 font-lalezar tracking-wide">{getText('quality', "کیفیت")}</div>
|
<div className="text-2xl sm: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 className="text-[10px] sm:text-xs uppercase tracking-widest font-bold opacity-90 font-vazir">{getText('ultra_pharmaceutical', "استانداردهای فوقدارویی")}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -104,7 +104,7 @@
|
|||||||
|
|
||||||
html, body, #root {
|
html, body, #root {
|
||||||
font-family: "Vazirmatn" !important;
|
font-family: "Vazirmatn" !important;
|
||||||
@apply bg-medical-gray-50 text-medical-gray-900 antialiased;
|
@apply bg-medical-gray-50 text-medical-gray-900 antialiased overflow-x-hidden max-w-[100vw];
|
||||||
font-feature-settings: "ss01", "ss02", "cv01", "cv11";
|
font-feature-settings: "ss01", "ss02", "cv01", "cv11";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import React, { useState, useMemo, useEffect } from "react";
|
|||||||
import { Product, PetType } from "../lib/data/products";
|
import { Product, PetType } from "../lib/data/products";
|
||||||
import { usePetStore } from "../lib/store/usePetStore";
|
import { usePetStore } from "../lib/store/usePetStore";
|
||||||
import { productService } from "../lib/services/productService";
|
import { productService } from "../lib/services/productService";
|
||||||
|
import Link from "next/link";
|
||||||
import { motion, AnimatePresence } from "motion/react";
|
import { motion, AnimatePresence } from "motion/react";
|
||||||
import { ProductCardSkeleton } from "./Skeleton";
|
import { ProductCardSkeleton } from "./Skeleton";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@ -66,12 +67,14 @@ const CATEGORY_MAP: Record<string, { category?: string; query?: string; symptoms
|
|||||||
"شستشوی چشم": { category: "special-care", query: "چشم" },
|
"شستشوی چشم": { category: "special-care", query: "چشم" },
|
||||||
};
|
};
|
||||||
|
|
||||||
const ArchiveProductCard: React.FC<{ product: Product, onClick: (p: Product) => void }> = ({ product, onClick }) => {
|
const ArchiveProductCard: React.FC<{ product: Product }> = ({ product }) => {
|
||||||
const { addItem } = useCartStore();
|
const { addItem } = useCartStore();
|
||||||
const [isAdding, setIsAdding] = useState(false);
|
const [isAdding, setIsAdding] = useState(false);
|
||||||
const activePet = usePetStore(state => state.pets.find(p => p.id === state.activePetId) || null);
|
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) => {
|
const handleAddToCart = (e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setIsAdding(true);
|
setIsAdding(true);
|
||||||
addItem(product, 1);
|
addItem(product, 1);
|
||||||
@ -83,10 +86,12 @@ const ArchiveProductCard: React.FC<{ product: Product, onClick: (p: Product) =>
|
|||||||
if (!activePet) return null;
|
if (!activePet) return null;
|
||||||
|
|
||||||
const sameSpecies = product.suitableFor === activePet.type || product.suitableFor === "هر دو";
|
const sameSpecies = product.suitableFor === activePet.type || product.suitableFor === "هر دو";
|
||||||
const helpsSymptom = (activePet.medicalConditions || []).some(s => product.symptoms.includes(s));
|
const matchedSymptom = (activePet.medicalConditions || []).find(mc =>
|
||||||
|
(product.symptoms || []).some(s => mc.includes(s) || s.includes(mc))
|
||||||
|
);
|
||||||
|
|
||||||
if (!sameSpecies) return { type: 'alert', text: `مخصوص ${product.suitableFor}` };
|
if (!sameSpecies) return { type: 'alert', text: `مخصوص ${product.suitableFor}` };
|
||||||
if (helpsSymptom) return { type: 'success', text: `توصیه شده برای راهکار ${activePet.name}` };
|
if (matchedSymptom) return { type: 'success', text: `توصیه شده برای ${matchedSymptom} ${activePet.name}` };
|
||||||
if (sameSpecies) return { type: 'neutral', text: `مناسب برای ${activePet.name}` };
|
if (sameSpecies) return { type: 'neutral', text: `مناسب برای ${activePet.name}` };
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -97,8 +102,11 @@ const ArchiveProductCard: React.FC<{ product: Product, onClick: (p: Product) =>
|
|||||||
layout
|
layout
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
onClick={() => onClick(product)}
|
className="h-full"
|
||||||
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 && (
|
{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">
|
<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">
|
||||||
@ -163,9 +171,10 @@ const ArchiveProductCard: React.FC<{ product: Product, onClick: (p: Product) =>
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Link>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
@ -277,28 +286,24 @@ export default function ArchivePage({
|
|||||||
router.push(newUrl, { scroll: false });
|
router.push(newUrl, { scroll: false });
|
||||||
}, [selectedCategory, selectedPet, searchQuery, activeSymptoms]);
|
}, [selectedCategory, selectedPet, searchQuery, activeSymptoms]);
|
||||||
|
|
||||||
const fetchProducts = async (resetPage = true) => {
|
const fetchProducts = async () => {
|
||||||
setIsUpdating(true);
|
setIsUpdating(true);
|
||||||
try {
|
try {
|
||||||
const res = await productService.getProducts({
|
const res = await productService.getProducts({
|
||||||
category: selectedCategory,
|
category: selectedCategory,
|
||||||
petType: selectedPet,
|
petType: selectedPet,
|
||||||
query: searchQuery,
|
query: searchQuery,
|
||||||
// Send the first active symptom to the backend for server-side filtering
|
limit: 999,
|
||||||
symptom: activeSymptoms.length === 1 ? activeSymptoms[0] : undefined,
|
|
||||||
page: resetPage ? 1 : page,
|
|
||||||
limit: 20,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = res.data;
|
let result = res.data;
|
||||||
// If multiple symptoms selected, do client-side intersection (rare case)
|
// Filter with OR logic (union of symptoms) if symptoms are active
|
||||||
if (activeSymptoms.length > 1) {
|
if (activeSymptoms.length > 0) {
|
||||||
result = result.filter(p => activeSymptoms.every(s => p.symptoms.includes(s)));
|
result = result.filter(p => p.symptoms && p.symptoms.some(s => activeSymptoms.includes(s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilteredProducts(result);
|
setFilteredProducts(result);
|
||||||
setMeta(res.meta);
|
setMeta(res.meta);
|
||||||
if (resetPage) setPage(1);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch products:", error);
|
console.error("Failed to fetch products:", error);
|
||||||
toast.error("خطا در دریافت لیست محصولات از سرور. لطفاً صفحه را رفرش کنید.");
|
toast.error("خطا در دریافت لیست محصولات از سرور. لطفاً صفحه را رفرش کنید.");
|
||||||
@ -308,32 +313,9 @@ export default function ArchivePage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchProducts(true);
|
fetchProducts();
|
||||||
}, [selectedCategory, selectedPet, searchQuery, activeSymptoms]);
|
}, [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) => {
|
const toggleSymptom = (s: string) => {
|
||||||
setActiveSymptoms(prev => prev.includes(s) ? prev.filter(item => item !== s) : [...prev, s]);
|
setActiveSymptoms(prev => prev.includes(s) ? prev.filter(item => item !== s) : [...prev, s]);
|
||||||
};
|
};
|
||||||
@ -491,20 +473,10 @@ export default function ArchivePage({
|
|||||||
<div key="grid" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
|
<div key="grid" className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
|
||||||
<AnimatePresence mode="popLayout">
|
<AnimatePresence mode="popLayout">
|
||||||
{filteredProducts.map((p: Product) => (
|
{filteredProducts.map((p: Product) => (
|
||||||
<ArchiveProductCard key={p.id} product={p} onClick={(p) => router.push(`/shop/${p.slug || p.id}`)} />
|
<ArchiveProductCard key={p.id} product={p} />
|
||||||
))}
|
))}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</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">
|
<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">
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import { useCartStore } from "../lib/store/cartStore";
|
|||||||
import { Product } from "../lib/data/products";
|
import { Product } from "../lib/data/products";
|
||||||
import { productService } from "../lib/services/productService";
|
import { productService } from "../lib/services/productService";
|
||||||
import SafeImage from "./SafeImage";
|
import SafeImage from "./SafeImage";
|
||||||
|
import { useUserStore } from "../lib/store/userStore";
|
||||||
|
import AuthModal from "./AuthModal";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
export default function CartDrawer({ isOpen, onClose, onCheckout, onShopNavigate }: {
|
export default function CartDrawer({ isOpen, onClose, onCheckout, onShopNavigate }: {
|
||||||
@ -16,6 +18,8 @@ export default function CartDrawer({ isOpen, onClose, onCheckout, onShopNavigate
|
|||||||
onShopNavigate?: () => void;
|
onShopNavigate?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { items, updateQuantity, removeItem, isSubscribed, toggleSubscription, getTotal, getSubtotal, getDiscount, coupon, applyCoupon, addItem } = useCartStore();
|
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 [couponCode, setCouponCode] = useState("");
|
||||||
const [couponStatus, setCouponStatus] = useState<"idle" | "success" | "error">("idle");
|
const [couponStatus, setCouponStatus] = useState<"idle" | "success" | "error">("idle");
|
||||||
const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null);
|
const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null);
|
||||||
@ -256,7 +260,14 @@ export default function CartDrawer({ isOpen, onClose, onCheckout, onShopNavigate
|
|||||||
|
|
||||||
{/* Subscription Toggle */}
|
{/* Subscription Toggle */}
|
||||||
<div
|
<div
|
||||||
onClick={toggleSubscription}
|
onClick={() => {
|
||||||
|
if (!isLoggedIn && !isSubscribed) {
|
||||||
|
toast.info("برای فعالسازی سرویس تمدید خودکار و دریافت یادآورها، لطفاً ابتدا وارد حساب کاربری خود شوید.");
|
||||||
|
setIsAuthModalOpen(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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'}`}
|
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">
|
<div className="flex items-center gap-3">
|
||||||
@ -265,7 +276,8 @@ export default function CartDrawer({ isOpen, onClose, onCheckout, onShopNavigate
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h5 className="text-xs font-black text-medical-gray-900">فعالسازی سرویس تمدید خودکار (Refill)</h5>
|
<h5 className="text-xs font-black text-medical-gray-900">فعالسازی سرویس تمدید خودکار (Refill)</h5>
|
||||||
<p className="text-[10px] text-medical-gray-500 font-medium">۵٪ تخفیف روی تمام فاکتور + ارسال اولویتدار</p>
|
<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>
|
||||||
</div>
|
</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'}`}>
|
<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" />}
|
{isSubscribed && <X className="w-3 h-3 text-white" />}
|
||||||
@ -273,6 +285,8 @@ export default function CartDrawer({ isOpen, onClose, onCheckout, onShopNavigate
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<AuthModal isOpen={isAuthModalOpen} onClose={() => setIsAuthModalOpen(false)} />
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between text-sm px-2">
|
<div className="flex items-center justify-between text-sm px-2">
|
||||||
<span className="text-medical-gray-500 font-bold">مجموع اقلام</span>
|
<span className="text-medical-gray-500 font-bold">مجموع اقلام</span>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Phone, Mail, MapPin, Instagram, ShieldCheck, Globe } from "lucide-react";
|
import { Phone, Mail, MapPin, Instagram, ShieldCheck, Globe, ArrowUp } from "lucide-react";
|
||||||
import { useSettingsStore } from "../lib/store/settingsStore";
|
import { useSettingsStore } from "../lib/store/settingsStore";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
@ -10,8 +10,38 @@ export default function Footer({ onNavigate, onShopNavigate, onB2BOpen }: {
|
|||||||
onB2BOpen?: () => void
|
onB2BOpen?: () => void
|
||||||
}) {
|
}) {
|
||||||
const getText = useSettingsStore(state => state.getText);
|
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 (
|
return (
|
||||||
<footer className="bg-medical-gray-900 text-white pt-24 pb-12 font-shabnam" dir="rtl">
|
<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>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="max-w-7xl mx-auto px-6">
|
<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">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-16 mb-20">
|
||||||
|
|
||||||
@ -125,10 +155,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="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="text-slate-400 font-shabnam">{getText('footer_copyright', "© ۲۰۲۶ کانینا ایران. تمامی حقوق محفوظ است.")}</div>
|
||||||
<div className="flex gap-8 font-shabnam text-slate-400">
|
<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">{getText('footer_about', "درباره ما")}</Link>
|
<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">{getText('footer_contact', "تماس با ما")}</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">{getText('footer_privacy', "حریم خصوصی")}</Link>
|
<Link href="/privacy" className="font-extrabold hover:text-white transition-colors whitespace-nowrap">{getText('footer_privacy', "حریم خصوصی")}</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -47,8 +47,32 @@ export default function Header({
|
|||||||
title: string;
|
title: string;
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
solutions: string[];
|
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 petMenuRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const { getTotalItems } = useCartStore();
|
const { getTotalItems } = useCartStore();
|
||||||
@ -61,13 +85,15 @@ export default function Header({
|
|||||||
const loadNavFilters = async () => {
|
const loadNavFilters = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await productService.getNavigationFilters();
|
const data = await productService.getNavigationFilters();
|
||||||
|
if (data && data.length > 0) {
|
||||||
const mapped = data.map(item => ({
|
const mapped = data.map(item => ({
|
||||||
id: item.slug,
|
id: item.slug,
|
||||||
title: item.name,
|
title: item.name,
|
||||||
icon: MENU_ICONS[item.slug] || <Pill className="w-5 h-5" />,
|
icon: MENU_ICONS[item.slug] || <Pill className="w-5 h-5" />,
|
||||||
solutions: item.symptoms
|
solutions: item.symptoms || []
|
||||||
}));
|
}));
|
||||||
setMenuItems(mapped);
|
setMenuItems(mapped);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to load navigation filters:", e);
|
console.error("Failed to load navigation filters:", e);
|
||||||
}
|
}
|
||||||
@ -104,12 +130,14 @@ export default function Header({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<header className="sticky top-0 z-50 bg-white/95 backdrop-blur-md border-b border-medical-gray-100 shadow-sm">
|
<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 h-24 flex items-center justify-between gap-4 px-4 sm:px-6 lg:px-8" dir="rtl">
|
<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">
|
<div className="flex items-center gap-2">
|
||||||
{/* Mobile Menu Toggle */}
|
{/* Mobile Menu Toggle (visible below 800px) */}
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||||
className="lg:hidden p-2 rounded-xl text-medical-gray-600 hover:bg-medical-gray-50 transition-all"
|
className="min-[800px]:hidden p-2 rounded-xl text-medical-gray-600 hover:bg-medical-gray-50 transition-all flex-shrink-0"
|
||||||
aria-label="منو"
|
aria-label="منو"
|
||||||
>
|
>
|
||||||
{isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
|
{isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
|
||||||
@ -118,32 +146,33 @@ export default function Header({
|
|||||||
{/* Logo Section */}
|
{/* Logo Section */}
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
className="flex-shrink-0 flex items-center gap-3 cursor-pointer group"
|
className="flex items-center gap-2 sm:gap-3 cursor-pointer group"
|
||||||
onClick={() => setIsMobileMenuOpen(false)}
|
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="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-1">
|
<div className="flex flex-col justify-center gap-0.5 sm:gap-1">
|
||||||
<span className="text-canina-blue font-black text-2xl tracking-tighter italic font-sans flex items-center gap-2 leading-none">
|
<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
|
Canina
|
||||||
<span className="text-sm not-italic font-medium border-r-2 border-medical-gray-200 pr-2 font-vazir">
|
<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', "ایران")}
|
{getText('brand_name_fa', "ایران")}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span className="text-[9px] text-medical-gray-400 font-bold uppercase tracking-wider font-vazir leading-none">
|
<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 آلمان")}
|
{getText('brand_subtitle', "نماینده رسمی Canina Pharma GmbH آلمان")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Navigation Items */}
|
{/* Navigation Items: Centered on XL screens (1224px+) */}
|
||||||
<nav className="hidden lg:flex items-center gap-1 flex-grow justify-center h-full">
|
<nav className="hidden xl:flex items-center gap-1 flex-grow justify-center h-full">
|
||||||
<div
|
<div
|
||||||
className="relative h-full flex items-center group"
|
className="relative h-full flex items-center group"
|
||||||
onMouseEnter={() => setIsMegaMenuOpen(true)}
|
onMouseEnter={() => setIsMegaMenuOpen(true)}
|
||||||
onMouseLeave={() => setIsMegaMenuOpen(false)}
|
onMouseLeave={() => setIsMegaMenuOpen(false)}
|
||||||
>
|
>
|
||||||
<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`}>
|
<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`}>
|
||||||
{getText('nav_solutions', "دسته بندی درمانی")}
|
{getText('nav_solutions', "دسته بندی درمانی")}
|
||||||
<ChevronDown className={`w-4 h-4 transition-transform duration-300 ${isMegaMenuOpen ? 'rotate-180' : ''}`} />
|
<ChevronDown className={`w-4 h-4 transition-transform duration-300 ${isMegaMenuOpen ? 'rotate-180' : ''}`} />
|
||||||
</button>
|
</button>
|
||||||
@ -209,7 +238,7 @@ export default function Header({
|
|||||||
<Link
|
<Link
|
||||||
key={item.id}
|
key={item.id}
|
||||||
href={item.href}
|
href={item.href}
|
||||||
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'}`}
|
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'}`}
|
||||||
>
|
>
|
||||||
{item.label}
|
{item.label}
|
||||||
</Link>
|
</Link>
|
||||||
@ -217,7 +246,7 @@ export default function Header({
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Action Area */}
|
{/* Action Area */}
|
||||||
<div className="flex items-center gap-4 flex-shrink-0 justify-end h-16 pl-2">
|
<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">
|
||||||
|
|
||||||
{/* Elastic Search */}
|
{/* Elastic Search */}
|
||||||
<div className="relative flex items-center justify-end z-20">
|
<div className="relative flex items-center justify-end z-20">
|
||||||
@ -225,10 +254,10 @@ export default function Header({
|
|||||||
{isSearchFocused && (
|
{isSearchFocused && (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ width: 0, opacity: 0 }}
|
initial={{ width: 0, opacity: 0 }}
|
||||||
animate={{ width: 320, opacity: 1 }}
|
animate={{ width: 260, opacity: 1 }}
|
||||||
exit={{ width: 0, opacity: 0 }}
|
exit={{ width: 0, opacity: 0 }}
|
||||||
transition={{ type: "spring", damping: 25, stiffness: 220 }}
|
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-12 flex items-center pr-12 pl-4 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-10 sm:h-12 flex items-center pr-10 pl-3 z-25"
|
||||||
>
|
>
|
||||||
<form onSubmit={handleSearch} className="w-full flex items-center">
|
<form onSubmit={handleSearch} className="w-full flex items-center">
|
||||||
<input
|
<input
|
||||||
@ -237,9 +266,9 @@ export default function Header({
|
|||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={e => setSearchQuery(e.target.value)}
|
onChange={e => setSearchQuery(e.target.value)}
|
||||||
onBlur={() => !searchQuery && setIsSearchFocused(false)}
|
onBlur={() => !searchQuery && setIsSearchFocused(false)}
|
||||||
placeholder="جستجوی محصولات، مقالات سلامت و غیره…"
|
placeholder="جستجو…"
|
||||||
dir="rtl"
|
dir="rtl"
|
||||||
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"
|
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"
|
||||||
/>
|
/>
|
||||||
<input type="submit" className="hidden" />
|
<input type="submit" className="hidden" />
|
||||||
</form>
|
</form>
|
||||||
@ -250,7 +279,7 @@ export default function Header({
|
|||||||
<HeaderButton
|
<HeaderButton
|
||||||
variant={isSearchFocused ? "primary" : "secondary"}
|
variant={isSearchFocused ? "primary" : "secondary"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-12 h-12 transition-all duration-300",
|
"w-10 h-10 sm:w-12 sm:h-12 transition-all duration-300",
|
||||||
isSearchFocused
|
isSearchFocused
|
||||||
? "z-30 bg-canina-blue text-white rounded-r-2xl rounded-l-none border border-canina-blue border-l-0 shadow-none"
|
? "z-30 bg-canina-blue text-white rounded-r-2xl rounded-l-none border border-canina-blue border-l-0 shadow-none"
|
||||||
: "rounded-2xl"
|
: "rounded-2xl"
|
||||||
@ -264,45 +293,45 @@ export default function Header({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Search className={cn("w-5 h-5", isSearchFocused ? "text-white" : "text-canina-blue")} />
|
<Search className={cn("w-4 h-4 sm:w-5 sm:h-5", isSearchFocused ? "text-white" : "text-canina-blue")} />
|
||||||
</HeaderButton>
|
</HeaderButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2 items-center h-12">
|
<div className="flex gap-1.5 sm:gap-2 items-center h-12">
|
||||||
{isLoggedIn ? (
|
{isLoggedIn ? (
|
||||||
<div className="flex items-center h-full gap-2 p-1.5 bg-medical-gray-50/50 border border-medical-gray-100 rounded-3xl">
|
<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">
|
||||||
{/* User Name (Direct link to dashboard) */}
|
{/* User Name (Direct link to dashboard) */}
|
||||||
<div
|
<div
|
||||||
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"
|
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"
|
||||||
onClick={() => onNavigate("user-dashboard")}
|
onClick={() => onNavigate("user-dashboard")}
|
||||||
>
|
>
|
||||||
<User className="w-5 h-5 text-canina-blue" />
|
<User className="w-4 h-4 sm:w-5 sm:h-5 text-canina-blue" />
|
||||||
<span className="text-[14px] font-black font-vazir text-medical-gray-900 leading-none">
|
<span className="text-[12px] sm:text-[14px] font-black font-vazir text-medical-gray-900 leading-none">
|
||||||
{profile.firstName}
|
{profile.firstName}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Separator Line */}
|
{/* Separator Line */}
|
||||||
<div className="w-[1.5px] h-5 bg-medical-gray-200" />
|
<div className="w-[1.5px] h-4 sm:h-5 bg-medical-gray-200" />
|
||||||
|
|
||||||
{/* Pet Name & Switcher */}
|
{/* Pet Name & Switcher */}
|
||||||
<div className="relative h-full flex items-center" ref={petMenuRef}>
|
<div className="relative h-full flex items-center" ref={petMenuRef}>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-2.5 px-4 py-2 rounded-2xl transition-all cursor-pointer",
|
"flex items-center gap-1.5 sm:gap-2.5 px-2.5 sm: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"
|
isPetSwitcherOpen ? "bg-canina-blue text-white shadow-lg shadow-canina-blue/20" : "text-canina-blue hover:bg-white"
|
||||||
)}
|
)}
|
||||||
onClick={() => setIsPetSwitcherOpen(!isPetSwitcherOpen)}
|
onClick={() => setIsPetSwitcherOpen(!isPetSwitcherOpen)}
|
||||||
>
|
>
|
||||||
{activePet?.type === "گربه" ? (
|
{activePet?.type === "گربه" ? (
|
||||||
<Cat className={cn("w-5 h-5", isPetSwitcherOpen ? "text-white" : "text-canina-blue/60")} />
|
<Cat 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")} />
|
<Dog className={cn("w-4 h-4 sm:w-5 sm:h-5", isPetSwitcherOpen ? "text-white" : "text-canina-blue/60")} />
|
||||||
)}
|
)}
|
||||||
<span className={cn("text-[14px] font-black font-vazir leading-none", isPetSwitcherOpen ? "text-white" : "text-canina-blue")}>
|
<span className={cn("text-[12px] sm:text-[14px] font-black font-vazir leading-none", isPetSwitcherOpen ? "text-white" : "text-canina-blue")}>
|
||||||
{activePet?.name || "بدون پت"}
|
{activePet?.name || "بدون پت"}
|
||||||
</span>
|
</span>
|
||||||
<ChevronDown className={cn("w-4 h-4 transition-transform", isPetSwitcherOpen ? "rotate-180" : "")} />
|
<ChevronDown className={cn("w-3.5 h-3.5 sm:w-4 sm:h-4 transition-transform", isPetSwitcherOpen ? "rotate-180" : "")} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
@ -391,26 +420,95 @@ export default function Header({
|
|||||||
<HeaderButton
|
<HeaderButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
layout="row"
|
layout="row"
|
||||||
className="px-6 h-12 text-sm font-black flex items-center justify-center"
|
className="px-3 sm:px-6 h-10 sm:h-12 text-xs sm:text-sm font-black flex items-center justify-center"
|
||||||
onClick={() => setIsAuthModalOpen(true)}
|
onClick={() => setIsAuthModalOpen(true)}
|
||||||
icon={<LogIn className="w-5 h-5 text-canina-blue" />}
|
icon={<LogIn className="w-4 h-4 sm:w-5 sm:h-5 text-canina-blue" />}
|
||||||
>
|
>
|
||||||
{getText('nav_login', "ورود / ثبتنام")}
|
<span className="hidden xs:inline">{getText('nav_login', "ورود / ثبتنام")}</span>
|
||||||
|
<span className="xs:hidden">ورود</span>
|
||||||
</HeaderButton>
|
</HeaderButton>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<HeaderButton
|
<HeaderButton
|
||||||
variant="primary"
|
variant="primary"
|
||||||
className="w-16 h-12"
|
className="w-12 sm:w-16 h-10 sm:h-12"
|
||||||
onClick={onCartOpen}
|
onClick={onCartOpen}
|
||||||
badge={getTotalItems()}
|
badge={getTotalItems()}
|
||||||
icon={<ShoppingBag className="w-6 h-6" />}
|
icon={<ShoppingBag className="w-5 h-5 sm:w-6 sm:h-6" />}
|
||||||
>
|
>
|
||||||
<div className="text-[8px] font-black tracking-tight whitespace-nowrap mt-0.5">{getText('nav_cart', "سبد خرید")}</div>
|
<div className="hidden sm:block text-[8px] font-black tracking-tight whitespace-nowrap mt-0.5">{getText('nav_cart', "سبد خرید")}</div>
|
||||||
</HeaderButton>
|
</HeaderButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</header>
|
||||||
|
|
||||||
{/* Mobile Drawer Menu */}
|
{/* Mobile Drawer Menu */}
|
||||||
@ -423,7 +521,7 @@ export default function Header({
|
|||||||
animate={{ opacity: 0.5 }}
|
animate={{ opacity: 0.5 }}
|
||||||
exit={{ opacity: 0 }}
|
exit={{ opacity: 0 }}
|
||||||
onClick={() => setIsMobileMenuOpen(false)}
|
onClick={() => setIsMobileMenuOpen(false)}
|
||||||
className="fixed inset-0 bg-black z-40 lg:hidden"
|
className="fixed inset-0 bg-black z-40 min-[800px]:hidden"
|
||||||
/>
|
/>
|
||||||
{/* Drawer */}
|
{/* Drawer */}
|
||||||
<motion.div
|
<motion.div
|
||||||
@ -431,7 +529,7 @@ export default function Header({
|
|||||||
animate={{ x: 0 }}
|
animate={{ x: 0 }}
|
||||||
exit={{ x: "100%" }}
|
exit={{ x: "100%" }}
|
||||||
transition={{ type: "spring", damping: 25, stiffness: 200 }}
|
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 lg: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 min-[800px]:hidden"
|
||||||
dir="rtl"
|
dir="rtl"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between border-b border-medical-gray-100 pb-4">
|
<div className="flex items-center justify-between border-b border-medical-gray-100 pb-4">
|
||||||
|
|||||||
@ -168,46 +168,46 @@ export default function Hero({ banners = [] }: { banners?: any[] }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats - Centered, full-width row under Hero content to decrease cognitive load */}
|
{/* Stats - 3 side-by-side cards with centered text on all screens */}
|
||||||
<div className="max-w-7xl mx-auto px-4 relative">
|
<div className="max-w-7xl mx-auto px-2 sm:px-4 relative">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: 0.8, duration: 0.6 }}
|
transition={{ delay: 0.8, duration: 0.6 }}
|
||||||
className="grid grid-cols-1 md:grid-cols-3 gap-8 py-10 border-t border-medical-gray-150"
|
className="grid grid-cols-3 gap-2 sm:gap-6 py-6 sm:py-10 border-t border-medical-gray-150"
|
||||||
>
|
>
|
||||||
{/* Stat 1: Founded Year */}
|
{/* Stat 1: Founded Year */}
|
||||||
<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="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-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">
|
<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-7 h-7" />
|
<Calendar className="w-4 h-4 sm:w-7 sm:h-7" />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="flex flex-col items-center sm:items-start text-center sm:text-right">
|
||||||
<div className="text-3xl font-black text-canina-blue font-vazir leading-none">۱۹۸۴</div>
|
<div className="text-base sm: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 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stat 2: Active Agencies */}
|
{/* Stat 2: Active Agencies */}
|
||||||
<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="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-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">
|
<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-7 h-7" />
|
<Globe className="w-4 h-4 sm:w-7 sm:h-7" />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="flex flex-col items-center sm:items-start text-center sm:text-right">
|
||||||
<div className="flex items-baseline">
|
<div className="flex items-baseline justify-center sm:justify-start">
|
||||||
<StatCounter target={40} />
|
<StatCounter target={40} />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-medical-gray-500 font-extrabold uppercase tracking-tight mt-2 font-vazir">{getText('hero_stat_agencies', "نمایندگی فعال جهانی")}</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stat 3: German Formula */}
|
{/* Stat 3: German Formula */}
|
||||||
<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="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-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">
|
<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-7 h-7" />
|
<ShieldCheck className="w-4 h-4 sm:w-7 sm:h-7" />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="flex flex-col items-center sm:items-start text-center sm:text-right">
|
||||||
<div className="text-3xl font-black text-canina-blue font-vazir leading-none">۱۰۰٪</div>
|
<div className="text-base sm: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 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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@ -6,10 +6,14 @@ import { FlaskConical, CheckCircle2, ChevronRight, ChevronLeft, Beaker, Search }
|
|||||||
import { useSettingsStore } from "../lib/store/settingsStore";
|
import { useSettingsStore } from "../lib/store/settingsStore";
|
||||||
import { productService } from "../lib/services/productService";
|
import { productService } from "../lib/services/productService";
|
||||||
|
|
||||||
import { useRouter } from 'next/navigation';
|
import Link from "next/link";
|
||||||
|
import { SCIENTIFIC_TERMS } from "../lib/data/scientificTerms";
|
||||||
|
import { useRouter, useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
export default function IngredientWiki() {
|
export default function IngredientWiki() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const termParam = searchParams.get('term');
|
||||||
const texts = useSettingsStore(state => state.texts);
|
const texts = useSettingsStore(state => state.texts);
|
||||||
const scientificTerms = useSettingsStore(state => state.scientificTerms);
|
const scientificTerms = useSettingsStore(state => state.scientificTerms);
|
||||||
const [ingredientsWiki, setIngredientsWiki] = useState<IngredientInfo[]>([]);
|
const [ingredientsWiki, setIngredientsWiki] = useState<IngredientInfo[]>([]);
|
||||||
@ -21,43 +25,52 @@ export default function IngredientWiki() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let list: IngredientInfo[] = [];
|
||||||
const raw = texts.ingredients_wiki || texts.ingredients_wiki_text;
|
const raw = texts.ingredients_wiki || texts.ingredients_wiki_text;
|
||||||
if (raw) {
|
if (raw) {
|
||||||
try {
|
try {
|
||||||
const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
||||||
if (Array.isArray(parsed)) {
|
if (Array.isArray(parsed)) {
|
||||||
setIngredientsWiki(parsed.map((item: any) => ({
|
list = parsed.map((item: any) => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
benefits: Array.isArray(item.benefits) ? item.benefits : []
|
benefits: Array.isArray(item.benefits) ? item.benefits : []
|
||||||
})));
|
}));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to parse ingredients_wiki:", e);
|
console.error("Failed to parse ingredients_wiki:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const terms = Object.values(scientificTerms);
|
|
||||||
if (terms.length > 0) {
|
// Merge terms from scientificTerms and static SCIENTIFIC_TERMS dictionary
|
||||||
setIngredientsWiki(terms.map((t: any) => {
|
const mergedTerms = { ...SCIENTIFIC_TERMS, ...scientificTerms };
|
||||||
let benefits: string[] = [];
|
Object.entries(mergedTerms).forEach(([key, t]: [string, any]) => {
|
||||||
if (t.keywords) {
|
const wikiId = t.wikiId || t.key || key;
|
||||||
try {
|
if (!list.some(item => item.id === wikiId || item.name.includes(t.term))) {
|
||||||
const parsed = typeof t.keywords === 'string' ? JSON.parse(t.keywords) : t.keywords;
|
list.push({
|
||||||
if (Array.isArray(parsed)) benefits = parsed;
|
id: wikiId,
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
id: t.key,
|
|
||||||
name: t.term,
|
name: t.term,
|
||||||
description: t.definition,
|
description: t.definition,
|
||||||
benefits
|
benefits: []
|
||||||
};
|
});
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setIngredientsWiki(list);
|
||||||
}, [texts, scientificTerms]);
|
}, [texts, scientificTerms]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (termParam && ingredientsWiki.length > 0) {
|
||||||
|
const el = document.getElementById(`wiki-${termParam}`);
|
||||||
|
if (el) {
|
||||||
|
setTimeout(() => {
|
||||||
|
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [termParam, ingredientsWiki]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white py-12 px-4 overflow-hidden font-vazir" dir="rtl">
|
<div className="bg-white py-12 px-4 overflow-hidden font-vazir" dir="rtl">
|
||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
@ -150,13 +163,36 @@ export default function IngredientWiki() {
|
|||||||
(ing.id === 'l-carnitine' && (miClean.includes('کارنیتین') || miClean.includes('carnitine'))) ||
|
(ing.id === 'l-carnitine' && (miClean.includes('کارنیتین') || miClean.includes('carnitine'))) ||
|
||||||
(ing.id === 'seaweed' && (miClean.includes('جلبک') || miClean.includes('seaweed'))) ||
|
(ing.id === 'seaweed' && (miClean.includes('جلبک') || miClean.includes('seaweed'))) ||
|
||||||
(ing.id === 'hyaluronic' && (miClean.includes('هیالورونات') || miClean.includes('hyaluron'))) ||
|
(ing.id === 'hyaluronic' && (miClean.includes('هیالورونات') || miClean.includes('hyaluron'))) ||
|
||||||
(ing.id === 'calendula-sea-buckthorn' && (miClean.includes('کالاندولا') || miClean.includes('سنجد') || miClean.includes('calendula'))) ||
|
(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 === 'dodecanoic' && (miClean.includes('دودکانوئیک') || miClean.includes('مارگوسا') || miClean.includes('insect')));
|
(ing.id === 'dodecanoic' && (miClean.includes('دودکانوئیک') || miClean.includes('مارگوسا') || miClean.includes('insect')));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isTargeted = termParam === ing.id;
|
||||||
return (
|
return (
|
||||||
<div key={ing.id} className={`flex flex-col lg:flex-row items-center gap-16 ${idx % 2 === 1 ? 'lg:flex-row-reverse' : ''}`}>
|
<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' : ''}`}
|
||||||
|
>
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="lg:w-1/2 space-y-8">
|
<div className="lg:w-1/2 space-y-8">
|
||||||
<motion.div
|
<motion.div
|
||||||
@ -201,14 +237,14 @@ export default function IngredientWiki() {
|
|||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 relative z-10">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 relative z-10">
|
||||||
{hasIngredient.length > 0 ? hasIngredient.map(p => (
|
{hasIngredient.length > 0 ? hasIngredient.map(p => (
|
||||||
<div
|
<Link
|
||||||
key={p.id}
|
key={p.id}
|
||||||
onClick={() => router.push(`/shop/${p.slug || 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"
|
className="bg-white rounded-[2.5rem] p-6 shadow-xl hover:scale-105 transition-transform cursor-pointer group block"
|
||||||
>
|
>
|
||||||
<img src={p.image} alt={p.name} className="w-24 h-24 object-contain mx-auto mb-4" referrerPolicy="no-referrer" />
|
<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>
|
<h4 className="text-sm font-black text-center text-medical-gray-900 group-hover:text-canina-blue transition-colors px-2">{p.name}</h4>
|
||||||
</div>
|
</Link>
|
||||||
)) : (
|
)) : (
|
||||||
<div className="col-span-full py-8 text-center">
|
<div className="col-span-full py-8 text-center">
|
||||||
<p className="text-medical-gray-400 font-bold italic mb-3">محصولی با این ماده مؤثره یافت نشد.</p>
|
<p className="text-medical-gray-400 font-bold italic mb-3">محصولی با این ماده مؤثره یافت نشد.</p>
|
||||||
|
|||||||
@ -27,11 +27,16 @@ import {
|
|||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
Check
|
Check,
|
||||||
|
Sparkles,
|
||||||
|
Beaker,
|
||||||
|
HelpCircle,
|
||||||
|
FileText
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { usePetStore, PetProfile as GlobalPetProfile, Reminder, HealthLog, PetConsumption } from "../lib/store/usePetStore";
|
import { usePetStore, PetProfile as GlobalPetProfile, Reminder, HealthLog, PetConsumption } from "../lib/store/usePetStore";
|
||||||
import { useCartStore } from "../lib/store/cartStore";
|
import { useCartStore } from "../lib/store/cartStore";
|
||||||
import SafeImage from "./SafeImage";
|
import SafeImage from "./SafeImage";
|
||||||
|
import SmartAdvisor from "./SmartAdvisor";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { toPersian, cn } from "../lib/utils";
|
import { toPersian, cn } from "../lib/utils";
|
||||||
|
|
||||||
@ -165,36 +170,29 @@ export default function PetProfile({ initialView, advisorNeed }: {
|
|||||||
|
|
||||||
let picks: { product: Product, reason?: string }[] = [];
|
let picks: { product: Product, reason?: string }[] = [];
|
||||||
|
|
||||||
// Priority: Medical Conditions
|
// Priority 1: Match activePet.medicalConditions directly against product.symptoms
|
||||||
if (activePet.medicalConditions.includes("جراحی مفاصل")) {
|
if (activePet.medicalConditions && activePet.medicalConditions.length > 0) {
|
||||||
const gag = products.find(p => p.id === "canhydrox-gag");
|
products.forEach(p => {
|
||||||
if (gag) picks.push({ product: gag, reason: "پیشنهاد ویژه بر اساس سوابق جراحی مفاصل" });
|
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(" و ")}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activePet.medicalConditions.includes("زایمان اخیر") || activePet.medicalConditions.includes("بارداری")) {
|
// Priority 2: Biological factors
|
||||||
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) {
|
if (activePet.age > 7) {
|
||||||
const heart = products.find(p => p.id === "herz-vital");
|
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: "محافظت از قلب پتهای مسن" });
|
if (heart && !picks.some(x => x.product.id === heart.id)) {
|
||||||
|
picks.push({ product: heart, reason: "محافظت از قلب پتهای مسن" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill defaults if needed
|
// Fill defaults if needed
|
||||||
@ -383,6 +381,24 @@ export default function PetProfile({ initialView, advisorNeed }: {
|
|||||||
|
|
||||||
// 2. Add / Edit Pet Steps
|
// 2. Add / Edit Pet Steps
|
||||||
if (view === "add" || view === "edit") {
|
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 (
|
return (
|
||||||
<div className="min-h-screen bg-medical-gray-50 py-20 px-4 font-vazir" dir="rtl">
|
<div className="min-h-screen bg-medical-gray-50 py-20 px-4 font-vazir" dir="rtl">
|
||||||
<div className="max-w-2xl mx-auto">
|
<div className="max-w-2xl mx-auto">
|
||||||
|
|||||||
@ -109,38 +109,42 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
|
|||||||
const apiProduct = allProducts.find(p => p.id === product.id) || product;
|
const apiProduct = allProducts.find(p => p.id === product.id) || product;
|
||||||
if (!apiProduct) return null;
|
if (!apiProduct) return null;
|
||||||
const staticProduct = PRODUCTS.find(p => p.id === apiProduct.id || p.artNo === apiProduct.artNo);
|
const staticProduct = PRODUCTS.find(p => p.id === apiProduct.id || p.artNo === apiProduct.artNo);
|
||||||
if (staticProduct) {
|
if (staticProduct && typeof staticProduct.calculateDosage === 'function') {
|
||||||
return {
|
return {
|
||||||
...apiProduct,
|
...apiProduct,
|
||||||
calculateDosage: staticProduct.calculateDosage
|
calculateDosage: staticProduct.calculateDosage,
|
||||||
|
packageSize: apiProduct.packageSize || staticProduct.packageSize || 100
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (typeof apiProduct.calculateDosage !== 'function') {
|
|
||||||
|
// Dynamic weight-based calculation fallback for any product without custom logic
|
||||||
return {
|
return {
|
||||||
...apiProduct,
|
...apiProduct,
|
||||||
calculateDosage: (w: number, y: boolean) => {
|
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 {
|
return {
|
||||||
quantity: 1,
|
quantity: baseQty,
|
||||||
unit: apiProduct.unit || "قرص",
|
unit: apiProduct.unit?.includes('ml') ? "میلیلیتر" : apiProduct.unit?.includes('g') ? "گرم" : "قرص",
|
||||||
description: "مصرف روزانه بر اساس دستور پزشک"
|
description: isYoung ? "دوز رشد روزانه" : "دوز نگهداری روزانه"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
return apiProduct;
|
|
||||||
}, [product, allProducts]);
|
}, [product, allProducts]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activePet) {
|
if (activePet && activePet.weight > 0) {
|
||||||
setWeight(activePet.weight);
|
setWeight(activePet.weight);
|
||||||
setPetType(activePet.age <= 1 ? "young" : "adult");
|
setPetType(activePet.age <= 1 ? "young" : "adult");
|
||||||
}
|
}
|
||||||
}, [activePet, setWeight, setPetType]);
|
}, [activePet]);
|
||||||
|
|
||||||
const calculation = useMemo(() => {
|
const calculation = useMemo(() => {
|
||||||
if (!fullProduct || typeof fullProduct.calculateDosage !== 'function') return null;
|
if (!fullProduct || typeof fullProduct.calculateDosage !== 'function') return null;
|
||||||
const result = fullProduct.calculateDosage(weight, petType === "young");
|
const result = fullProduct.calculateDosage(weight, petType === "young");
|
||||||
const duration = Math.floor(fullProduct.packageSize / result.quantity);
|
const packageSize = fullProduct.packageSize || 100;
|
||||||
|
const duration = Math.max(1, Math.floor(packageSize / (result.quantity || 1)));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dailyDose: result.quantity,
|
dailyDose: result.quantity,
|
||||||
@ -336,7 +340,39 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
|
|||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Smart Dosage Calculator Utility 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" />
|
||||||
|
</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="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">
|
||||||
|
ℹ️
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
) : (
|
||||||
<section className="bg-white rounded-[2.5rem] border border-medical-gray-200 p-8 md:p-10 shadow-md space-y-8">
|
<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="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">
|
<div className="w-12 h-12 bg-canina-blue text-white rounded-2xl flex items-center justify-center shadow-lg">
|
||||||
@ -394,6 +430,7 @@ export default function ProductPage({ productSlug }: { productSlug: string }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Ingredients Section */}
|
{/* Ingredients Section */}
|
||||||
<section className="space-y-8">
|
<section className="space-y-8">
|
||||||
@ -410,7 +447,17 @@ 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>
|
<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">
|
<div className="flex flex-wrap gap-3">
|
||||||
{product.main_ingredients.map((ing, idx) => {
|
{product.main_ingredients.map((ing, idx) => {
|
||||||
const termKey = termKeys.find(key => ing.includes(key));
|
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);
|
||||||
|
});
|
||||||
return (
|
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">
|
<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">
|
<span className="text-sm font-bold text-medical-gray-700 font-vazir">
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { motion } from "motion/react";
|
|||||||
import { Product } from "../lib/data/products";
|
import { Product } from "../lib/data/products";
|
||||||
import { productService } from "../lib/services/productService";
|
import { productService } from "../lib/services/productService";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
export default function SearchResultsPage({ query }: { query: string }) {
|
export default function SearchResultsPage({ query }: { query: string }) {
|
||||||
@ -64,8 +65,11 @@ export default function SearchResultsPage({ query }: { query: string }) {
|
|||||||
<motion.div
|
<motion.div
|
||||||
key={product.id}
|
key={product.id}
|
||||||
layoutId={product.id}
|
layoutId={product.id}
|
||||||
onClick={() => router.push(`/shop/${product.slug || product.id}`)}
|
className="h-full"
|
||||||
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">
|
<div className="relative aspect-square mb-8 bg-medical-gray-50 rounded-[2rem] overflow-hidden p-6 flex items-center justify-center">
|
||||||
<img
|
<img
|
||||||
@ -100,10 +104,11 @@ export default function SearchResultsPage({ query }: { query: string }) {
|
|||||||
<div>
|
<div>
|
||||||
<span className="text-lg font-black text-medical-gray-900">{product.price}</span>
|
<span className="text-lg font-black text-medical-gray-900">{product.price}</span>
|
||||||
</div>
|
</div>
|
||||||
<button className="bg-medical-gray-900 text-white w-10 h-10 rounded-xl flex items-center justify-center group-hover:bg-canina-blue transition-colors">
|
<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" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@ -124,12 +129,12 @@ export default function SearchResultsPage({ query }: { query: string }) {
|
|||||||
<h3 className="text-2xl font-black text-medical-gray-900 mb-10 italic">محصولات پیشنهادی (پرفروش)</h3>
|
<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">
|
<div className="grid md:grid-cols-3 gap-8 opacity-60 hover:opacity-100 transition-opacity">
|
||||||
{bestSellers.map(p => (
|
{bestSellers.map(p => (
|
||||||
<div key={p.id} onClick={() => router.push(`/shop/${p.id}`)} className="cursor-pointer group">
|
<Link key={p.id} href={`/shop/${p.slug || p.id}`} className="cursor-pointer group block">
|
||||||
<div className="aspect-square bg-white rounded-[2rem] p-6 mb-4 border border-medical-gray-100 flex items-center justify-center">
|
<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" />
|
<img src={p.image} className="w-40 h-40 object-contain group-hover:scale-105 transition-transform" />
|
||||||
</div>
|
</div>
|
||||||
<h4 className="font-black text-medical-gray-900 text-center">{p.name}</h4>
|
<h4 className="font-black text-medical-gray-900 text-center">{p.name}</h4>
|
||||||
</div>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,7 +12,8 @@ import {
|
|||||||
Bone,
|
Bone,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
Pill,
|
Pill,
|
||||||
Sparkle
|
Sparkle,
|
||||||
|
Check
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { toPersian, cn } from "../lib/utils";
|
import { toPersian, cn } from "../lib/utils";
|
||||||
|
|
||||||
@ -25,13 +26,24 @@ interface SmartAdvisorProps {
|
|||||||
onComplete?: (data: any) => void;
|
onComplete?: (data: any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MEDICAL_OPTIONS = [
|
const CURRENT_SYMPTOMS = [
|
||||||
{ id: "joint_surgery", label: "جراحی مفاصل", condition: "جراحی مفاصل" },
|
{ id: "joint_pain", label: "درد مفاصل و لنگیدن", condition: "درد مفاصل" },
|
||||||
{ id: "recent_birth", label: "زایمان اخیر", condition: "زایمان اخیر" },
|
{ id: "hair_loss", label: "ریزش مو، خارش و خشکی پوست", condition: "ریزش مو" },
|
||||||
{ id: "pregnancy", label: "بارداری", condition: "بارداری" },
|
{ id: "diarrhea", label: "اسهال، یبوست و مشکلات گوارشی", condition: "اسهال" },
|
||||||
{ id: "digestion", label: "مشکلات گوارشی", condition: "مشکلات گوارشی" },
|
{ id: "appetite", label: "بیاشتهایی و کاهش وزن", condition: "بیاشتهایی" },
|
||||||
{ id: "hair_loss", label: "ریزش موی شدید", condition: "ریزش موی شدید" },
|
{ id: "dental", label: "جرم دندان و بوی بد دهان", condition: "جرم دندان" },
|
||||||
{ id: "appetite", 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: "آلودگی انگلی" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorProps & { rules?: any[] }) {
|
export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorProps & { rules?: any[] }) {
|
||||||
@ -41,17 +53,6 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
|
|||||||
const { addPet, setActivePet } = usePetStore();
|
const { addPet, setActivePet } = usePetStore();
|
||||||
const texts = useSettingsStore(state => state.texts);
|
const texts = useSettingsStore(state => state.texts);
|
||||||
const getText = useSettingsStore(state => state.getText);
|
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);
|
const [step, setStep] = useState(1);
|
||||||
|
|
||||||
@ -67,7 +68,8 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
|
|||||||
const [weight, setWeight] = useState<number>(10);
|
const [weight, setWeight] = useState<number>(10);
|
||||||
const [activityLevel, setActivityLevel] = useState<"کم" | "متوسط" | "زیاد">("متوسط");
|
const [activityLevel, setActivityLevel] = useState<"کم" | "متوسط" | "زیاد">("متوسط");
|
||||||
|
|
||||||
// Step 3 Data
|
// Step 3 Data (Symptoms) & Step 4 Data (History)
|
||||||
|
const [currentSymptoms, setCurrentSymptoms] = useState<string[]>([]);
|
||||||
const [medicalConditions, setMedicalConditions] = useState<string[]>([]);
|
const [medicalConditions, setMedicalConditions] = useState<string[]>([]);
|
||||||
|
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
@ -84,8 +86,9 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
|
|||||||
const handleBack = () => setStep(s => s - 1);
|
const handleBack = () => setStep(s => s - 1);
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
|
const combinedConditions = Array.from(new Set([...currentSymptoms, ...medicalConditions]));
|
||||||
const data = {
|
const data = {
|
||||||
name, type, breed, age, weight, activityLevel, medicalConditions
|
name, type, breed, age, weight, activityLevel, medicalConditions: combinedConditions
|
||||||
};
|
};
|
||||||
if (onComplete) {
|
if (onComplete) {
|
||||||
onComplete(data);
|
onComplete(data);
|
||||||
@ -102,28 +105,87 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="canino-advisor" className="py-12 bg-white">
|
<section id="canino-advisor" className="py-16 bg-gradient-to-b from-white via-medical-gray-50/50 to-white">
|
||||||
<div className="max-w-4xl mx-auto px-4">
|
<div className="max-w-5xl mx-auto px-4">
|
||||||
<div className="text-center mb-8">
|
{/* Header Title */}
|
||||||
|
<div className="text-center mb-10">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
className="inline-flex items-center gap-2 px-4 py-2 bg-canina-blue/5 rounded-full mb-4"
|
className="inline-flex items-center gap-2 px-4 py-2 bg-canina-blue/5 rounded-full mb-4 border border-canina-blue/10"
|
||||||
>
|
>
|
||||||
<Sparkle className="w-4 h-4 text-canina-blue" />
|
<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>
|
<span className="text-canina-blue text-xs font-black uppercase tracking-widest font-vazir">{getText('advisor_badge', "سیستم پایش و تجویز هوشمند")}</span>
|
||||||
</motion.div>
|
</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>
|
<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>
|
<p className="text-medical-gray-600 font-medium text-base font-shabnam max-w-2xl mx-auto leading-relaxed">
|
||||||
|
{getText('advisor_subtitle', "با ثبت اطلاعات همدم خود در ۴ قدم ساده، از الگوریتم علمی کانینا برای تجویز دقیق مکمل و پایش سلامت بهرهمند شوید.")}
|
||||||
|
</p>
|
||||||
</div>
|
</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">
|
<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 */}
|
{/* Progress Indicator */}
|
||||||
<div className="absolute top-0 left-0 w-full h-2 bg-medical-gray-200">
|
<div className="absolute top-0 left-0 w-full h-2 bg-medical-gray-200">
|
||||||
<motion.div
|
<motion.div
|
||||||
className="h-full bg-canina-blue"
|
className="h-full bg-canina-blue"
|
||||||
initial={{ width: "0%" }}
|
initial={{ width: "0%" }}
|
||||||
animate={{ width: `${(step / 3) * 100}%` }}
|
animate={{ width: `${(step / 4) * 100}%` }}
|
||||||
transition={{ duration: 0.5 }}
|
transition={{ duration: 0.5 }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -277,7 +339,7 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
|
|||||||
onClick={handleNext}
|
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]"
|
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_final', "گام نهایی سلامت")}
|
{getText('advisor_next_symptoms', "بررسی علائم بالینی فعلی")}
|
||||||
<ChevronLeft className="w-5 h-5" />
|
<ChevronLeft className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -293,12 +355,63 @@ export default function SmartAdvisor({ onComplete, rules = [] }: SmartAdvisorPro
|
|||||||
className="space-y-8"
|
className="space-y-8"
|
||||||
>
|
>
|
||||||
<div className="text-center">
|
<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_title', "گام آخر: سوابق و حساسیتها")}</h3>
|
<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_desc', "در صورت وجود هر یک از موارد زیر، آن را تیک بزنید")}</p>
|
<p className="text-medical-gray-600 font-bold font-vazir">{getText('advisor_step3_symptoms_desc', "آیا همدم شما در حال حاضر هیچکدام از علائم زیر را تجربه میکند؟")}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
{medicalOptions.map((opt) => (
|
{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) => (
|
||||||
<button
|
<button
|
||||||
key={opt.id}
|
key={opt.id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@ -32,28 +32,31 @@ export default function Tooltip({ termKey, children, onWikiNavigate }: TooltipPr
|
|||||||
initial={{ opacity: 0, y: 10, scale: 0.95 }}
|
initial={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||||
exit={{ opacity: 0, y: 10, scale: 0.95 }}
|
exit={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||||
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"
|
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"
|
||||||
dir="rtl"
|
dir="rtl"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2 mb-2 text-canina-blue">
|
<div className="flex items-center gap-2 mb-2 text-canina-blue">
|
||||||
<Info className="w-4 h-4" />
|
<Info className="w-4 h-4" />
|
||||||
<span className="font-black text-[10px] uppercase tracking-widest">دانشنامه علمی کانینا</span>
|
<span className="font-black text-[11px] uppercase tracking-widest text-canina-blue">دانشنامه علمی کانینا</span>
|
||||||
</div>
|
</div>
|
||||||
<h5 className="font-black mb-1">{termData.term}</h5>
|
<h5 className="font-black text-base text-medical-gray-900 mb-2 leading-snug">{termData.term}</h5>
|
||||||
<p className="text-white/70 text-xs leading-relaxed mb-3">
|
<p className="text-medical-gray-600 text-xs leading-relaxed mb-4 font-bold">
|
||||||
{termData.definition}
|
{termData.definition}
|
||||||
</p>
|
</p>
|
||||||
{termData.wikiId && (
|
{termData.wikiId && (
|
||||||
<button
|
<button
|
||||||
onClick={() => onWikiNavigate?.(termData.wikiId!)}
|
onClick={(e) => {
|
||||||
className="flex items-center gap-1.5 text-canina-blue hover:text-white transition-colors text-[10px] font-bold"
|
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"
|
||||||
>
|
>
|
||||||
<span>مطالعه مقاله کامل</span>
|
<span>مطالعه مقاله کامل</span>
|
||||||
<ExternalLink className="w-3 h-3" />
|
<ExternalLink className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{/* Arrow */}
|
{/* Arrow */}
|
||||||
<div className="absolute top-full left-1/2 -translate-x-1/2 border-8 border-transparent border-t-medical-gray-900" />
|
<div className="absolute top-full left-1/2 -translate-x-1/2 border-8 border-transparent border-t-white" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|||||||
@ -1055,7 +1055,9 @@ export const PRODUCTS: Product[] = [
|
|||||||
symptoms: ["توله بدون مادر", "نیاز به شیردهی مصنوعی"],
|
symptoms: ["توله بدون مادر", "نیاز به شیردهی مصنوعی"],
|
||||||
suitableFor: "سگ",
|
suitableFor: "سگ",
|
||||||
calculateDosage: (weight) => {
|
calculateDosage: (weight) => {
|
||||||
return { quantity: 1, unit: "قسمت", description: "مخلوط ۱:۴ با آب ۵۰ درجه سانتیگراد" };
|
// 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} میلیلیتر آب ۵۰ درجه` };
|
||||||
},
|
},
|
||||||
analysis: { "پروتئین": "۳۰٪" },
|
analysis: { "پروتئین": "۳۰٪" },
|
||||||
feedingAdvice: "هر ۲ ساعت در هفته اول، سپس هر ۴ ساعت.",
|
feedingAdvice: "هر ۲ ساعت در هفته اول، سپس هر ۴ ساعت.",
|
||||||
@ -1086,8 +1088,10 @@ export const PRODUCTS: Product[] = [
|
|||||||
symptoms: ["توله ۳ هفته", "شروع غذای کمکی"],
|
symptoms: ["توله ۳ هفته", "شروع غذای کمکی"],
|
||||||
suitableFor: "سگ",
|
suitableFor: "سگ",
|
||||||
contraindications: ["مصرف همزمان ویتامین D2 مجاز نیست"],
|
contraindications: ["مصرف همزمان ویتامین D2 مجاز نیست"],
|
||||||
calculateDosage: () => {
|
calculateDosage: (weight) => {
|
||||||
return { quantity: 1, unit: "قسمت", description: "مخلوط ۱:۳ با آب ۵۰ درجه" };
|
// 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} میلیلیتر آب ۵۰ درجه` };
|
||||||
},
|
},
|
||||||
analysis: { "پروتئین": "۲۷٪" },
|
analysis: { "پروتئین": "۲۷٪" },
|
||||||
feedingAdvice: "کمکم از هفته سوم به عنوان غذای کمکی معرفی شود.",
|
feedingAdvice: "کمکم از هفته سوم به عنوان غذای کمکی معرفی شود.",
|
||||||
|
|||||||
@ -125,6 +125,106 @@ export const SCIENTIFIC_TERMS: Record<string, ScientificTerm> = {
|
|||||||
definition: "روغنهای تسکیندهنده، ضدباکتری و التیامبخش پوست جهت بهبود سوزش، زخمهای سطحی و خشکی پنجهها.",
|
definition: "روغنهای تسکیندهنده، ضدباکتری و التیامبخش پوست جهت بهبود سوزش، زخمهای سطحی و خشکی پنجهها.",
|
||||||
wikiId: "calendula-sea-buckthorn"
|
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)",
|
term: "تورین (Taurine)",
|
||||||
definition: "آمینو اسید بسیار ضروری برای گربهها جهت جلوگیری از دژنراسیون شبکیه (نابینایی) و نارسایی قلبی.",
|
definition: "آمینو اسید بسیار ضروری برای گربهها جهت جلوگیری از دژنراسیون شبکیه (نابینایی) و نارسایی قلبی.",
|
||||||
@ -134,6 +234,171 @@ export const SCIENTIFIC_TERMS: Record<string, ScientificTerm> = {
|
|||||||
term: "گرید دارویی (Pharmaceutical Grade)",
|
term: "گرید دارویی (Pharmaceutical Grade)",
|
||||||
definition: "بالاترین سطح خلوص مواد اولیه؛ تهیه شده با استانداردهای دارویی انسان و کاملاً بیخطر در صورت لیسیده شدن توسط حیوان."
|
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": {
|
"IFS": {
|
||||||
term: "استاندارد بینالمللی IFS",
|
term: "استاندارد بینالمللی IFS",
|
||||||
definition: "سختگیرانهترین استاندارد ایمنی و کیفیت محصولات دارویی و غذایی در اروپا."
|
definition: "سختگیرانهترین استاندارد ایمنی و کیفیت محصولات دارویی و غذایی در اروپا."
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user