814 lines
46 KiB
TypeScript
814 lines
46 KiB
TypeScript
"use client";
|
||
import { useState, useMemo, useEffect } from "react";
|
||
import { motion, AnimatePresence } from "motion/react";
|
||
import {
|
||
ArrowRight,
|
||
Calculator as CalcIcon,
|
||
ChevronLeft,
|
||
ShieldCheck,
|
||
CheckCircle2,
|
||
Clock,
|
||
FlaskConical,
|
||
Stethoscope,
|
||
Info,
|
||
CalendarDays,
|
||
Dog,
|
||
Gamepad2,
|
||
X,
|
||
Plus,
|
||
Minus,
|
||
Bell,
|
||
Sparkles,
|
||
ChevronRight,
|
||
ChevronDown,
|
||
Activity,
|
||
Heart,
|
||
Zap,
|
||
Flame,
|
||
Star,
|
||
Users,
|
||
ShoppingBag
|
||
} from "lucide-react";
|
||
|
||
const ICON_MAP: Record<string, any> = {
|
||
Sparkles,
|
||
Activity,
|
||
ShieldCheck,
|
||
Heart,
|
||
Zap,
|
||
Flame,
|
||
Star,
|
||
Users
|
||
};
|
||
import { toPersian, cn } from "../lib/utils";
|
||
import { Product, PRODUCTS } from "../lib/data/products";
|
||
import { productService } from "../lib/services/productService";
|
||
import { SCIENTIFIC_TERMS } from "../lib/data/scientificTerms";
|
||
import { useSettingsStore } from "../lib/store/settingsStore";
|
||
import { create } from "zustand";
|
||
import { useCartStore } from "../lib/store/cartStore";
|
||
import { usePetStore } from "../lib/store/usePetStore";
|
||
import { toast } from "sonner";
|
||
|
||
interface CalculatorState {
|
||
petType: "young" | "adult";
|
||
weight: number;
|
||
setPetType: (type: "young" | "adult") => void;
|
||
setWeight: (weight: number) => void;
|
||
}
|
||
|
||
const useCalculatorStore = create<CalculatorState>((set) => ({
|
||
petType: "young",
|
||
weight: 10,
|
||
setPetType: (petType) => set({ petType }),
|
||
setWeight: (weight) => set({ weight }),
|
||
}));
|
||
|
||
import Tooltip from "./Tooltip";
|
||
import SafeImage from "./SafeImage";
|
||
|
||
import { useRouter } from 'next/navigation';
|
||
|
||
export default function ProductPage({ productSlug }: { productSlug: string }) {
|
||
const router = useRouter();
|
||
const [product, setProduct] = useState<Product | null>(null);
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [activeTab, setActiveTab] = useState<"specs" | "feeding" | "notes">("specs");
|
||
const [showRefillModal, setShowRefillModal] = useState(false);
|
||
const [itemQuantity, setItemQuantity] = useState(1);
|
||
const [allProducts, setAllProducts] = useState<Product[]>([]);
|
||
const [isMounted, setIsMounted] = useState(false);
|
||
|
||
useEffect(() => {
|
||
setIsMounted(true);
|
||
productService.getProducts({ limit: 999 })
|
||
.then((res) => {
|
||
const products = res.data;
|
||
setAllProducts(products);
|
||
const found = products.find(p => p.slug === productSlug || p.id === productSlug);
|
||
setProduct(found || null);
|
||
})
|
||
.catch(err => console.error("Error fetching products in ProductPage:", err))
|
||
.finally(() => setIsLoading(false));
|
||
}, [productSlug]);
|
||
const { pets, getActivePet } = usePetStore();
|
||
const activePet = getActivePet();
|
||
const { petType, weight, setPetType, setWeight } = useCalculatorStore();
|
||
const { addItem } = useCartStore();
|
||
const scientificTerms = useSettingsStore(state => state.scientificTerms);
|
||
const termKeys = useMemo(() => {
|
||
return Array.from(new Set([
|
||
...Object.keys(scientificTerms),
|
||
...Object.keys(SCIENTIFIC_TERMS)
|
||
]));
|
||
}, [scientificTerms]);
|
||
|
||
// Re-hydrate product to ensure methods like calculateDosage exist
|
||
const fullProduct = useMemo(() => {
|
||
if (!product) return null;
|
||
const apiProduct = allProducts.find(p => p.id === product.id) || product;
|
||
if (!apiProduct) return null;
|
||
const staticProduct = PRODUCTS.find(p => p.id === apiProduct.id || p.artNo === apiProduct.artNo);
|
||
if (staticProduct && typeof staticProduct.calculateDosage === 'function') {
|
||
return {
|
||
...apiProduct,
|
||
calculateDosage: staticProduct.calculateDosage,
|
||
packageSize: apiProduct.packageSize || staticProduct.packageSize || 100
|
||
};
|
||
}
|
||
|
||
// Dynamic weight-based calculation fallback for any product without custom logic
|
||
return {
|
||
...apiProduct,
|
||
packageSize: apiProduct.packageSize || 100,
|
||
calculateDosage: (w: number, isYoung: boolean) => {
|
||
const factor = isYoung ? 0.2 : 0.1;
|
||
const baseQty = Math.max(1, Math.round(w * factor));
|
||
return {
|
||
quantity: baseQty,
|
||
unit: apiProduct.unit?.includes('ml') ? "میلیلیتر" : apiProduct.unit?.includes('g') ? "گرم" : "قرص",
|
||
description: isYoung ? "دوز رشد روزانه" : "دوز نگهداری روزانه"
|
||
};
|
||
}
|
||
};
|
||
}, [product, allProducts]);
|
||
|
||
useEffect(() => {
|
||
if (activePet && activePet.weight > 0) {
|
||
setWeight(activePet.weight);
|
||
setPetType(activePet.age <= 1 ? "young" : "adult");
|
||
}
|
||
}, [activePet]);
|
||
|
||
const calculation = useMemo(() => {
|
||
if (!fullProduct || typeof fullProduct.calculateDosage !== 'function') return null;
|
||
const result = fullProduct.calculateDosage(weight, petType === "young");
|
||
const packageSize = fullProduct.packageSize || 100;
|
||
const duration = Math.max(1, Math.floor(packageSize / (result.quantity || 1)));
|
||
|
||
return {
|
||
dailyDose: result.quantity,
|
||
duration: duration || 0,
|
||
unit: result.unit,
|
||
description: result.description
|
||
};
|
||
}, [fullProduct, petType, weight]);
|
||
|
||
// Suggest quantity inside useEffect to avoid render-phase state update
|
||
useEffect(() => {
|
||
const duration = calculation?.duration ?? 0;
|
||
const suggestedQty = (duration < 30 && duration > 0) ? 2 : 1;
|
||
setItemQuantity(suggestedQty);
|
||
}, [calculation?.duration]);
|
||
|
||
if (isLoading) return (
|
||
<div className="min-h-screen bg-medical-gray-50 p-6 max-w-7xl mx-auto font-vazir" dir="rtl">
|
||
<div className="animate-pulse space-y-6">
|
||
<div className="h-8 bg-medical-gray-200 rounded-2xl w-48" />
|
||
<div className="grid md:grid-cols-12 gap-8">
|
||
<div className="md:col-span-5 h-80 bg-white border border-medical-gray-200 rounded-[2.5rem]" />
|
||
<div className="md:col-span-7 space-y-4">
|
||
<div className="h-10 bg-medical-gray-200 rounded-2xl w-3/4" />
|
||
<div className="h-6 bg-medical-gray-100 rounded-xl w-1/2" />
|
||
<div className="h-24 bg-medical-gray-100 rounded-2xl w-full" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
if (!product || !fullProduct || !calculation) return <div className="min-h-screen flex items-center justify-center">محصول یافت نشد</div>;
|
||
|
||
return (
|
||
<div className="min-h-screen bg-medical-gray-50 pt-2 pb-32 sm:pb-20 px-4 md:px-0" dir="rtl">
|
||
<div className="max-w-7xl mx-auto">
|
||
{/* Breadcrumbs for Mobile and Desktop */}
|
||
<nav className="flex items-center gap-2 text-xs md:text-sm font-semibold text-medical-gray-600 mb-6 md:mb-10 overflow-x-auto whitespace-nowrap py-3 border-b border-medical-gray-100 font-vazir scrollbar-thin scrollbar-thumb-medical-gray-200 scrollbar-track-transparent pr-1" dir="rtl">
|
||
<button
|
||
onClick={() => router.push('/')}
|
||
className="hover:text-canina-blue transition-colors font-vazir cursor-pointer"
|
||
>
|
||
خانه
|
||
</button>
|
||
<ChevronLeft className="w-3.5 h-3.5 flex-shrink-0 text-medical-gray-300" />
|
||
<button
|
||
onClick={() => router.push(`/shop?category=${product.categorySlug}`)}
|
||
className="hover:text-canina-blue cursor-pointer font-vazir"
|
||
>
|
||
{product.category}
|
||
</button>
|
||
<ChevronLeft className="w-3.5 h-3.5 flex-shrink-0 text-medical-gray-300" />
|
||
<span className="text-canina-blue font-vazir whitespace-nowrap font-bold">{product.nameFa || product.name}</span>
|
||
</nav>
|
||
|
||
{/* Global Single Grid Layout - Aligns Sticky Buy Box at the Top */}
|
||
<div className="grid lg:grid-cols-12 gap-12 items-start">
|
||
|
||
{/* Right/Main Content Column (lg:col-span-8) */}
|
||
<div className="lg:col-span-8 space-y-12">
|
||
|
||
{/* Above the Fold Grid */}
|
||
<div className="grid md:grid-cols-12 gap-8 md:gap-12 items-center md:items-start text-center md:text-right font-vazir" dir="rtl">
|
||
{/* Right Column: Product Image Frame (md:col-span-5) */}
|
||
<div className="md:col-span-5 w-full mx-auto max-w-sm md:max-w-none">
|
||
<motion.div
|
||
initial={{ opacity: 0, scale: 0.95 }}
|
||
animate={{ opacity: 1, scale: 1 }}
|
||
className="bg-white rounded-[2.5rem] border border-medical-gray-200 shadow-xl relative group overflow-hidden h-[300px] sm:h-[380px] flex items-center justify-center p-6 sm:p-8"
|
||
>
|
||
<div className="absolute top-4 left-4 sm:top-6 sm:left-6 flex flex-col gap-1.5 sm:gap-2 z-10 items-start">
|
||
<div className="px-2.5 py-1 bg-green-500 text-white rounded-full text-[9px] sm:text-[10px] font-black uppercase tracking-widest flex items-center gap-1 font-vazir shadow-sm">
|
||
<CheckCircle2 className="w-3 h-3" />
|
||
موجود در انبار
|
||
</div>
|
||
<div className="px-2.5 py-1 bg-canina-blue text-white rounded-full text-[9px] sm:text-[10px] font-black uppercase tracking-widest font-vazir whitespace-nowrap shadow-sm">
|
||
گرید دارویی اختصاصی
|
||
</div>
|
||
</div>
|
||
<SafeImage
|
||
src={product.image}
|
||
alt={product.name}
|
||
className="w-full h-full"
|
||
imgClassName="max-h-[240px] sm:max-h-[300px] w-auto object-contain drop-shadow-2xl group-hover:scale-105 transition-transform duration-700 mx-auto my-auto"
|
||
/>
|
||
</motion.div>
|
||
</div>
|
||
|
||
{/* Left Column: Product Title & Description (md:col-span-7) */}
|
||
<div className="md:col-span-7 space-y-4 sm:space-y-6 flex flex-col items-center md:items-start">
|
||
<div className="w-full flex flex-col items-center md:items-start">
|
||
<div className="flex flex-wrap justify-center md:justify-start items-center gap-2 sm:gap-3 mb-3 sm:mb-4">
|
||
<div className="px-3 py-1 bg-canina-blue text-white rounded-full text-[10px] font-black uppercase tracking-widest font-vazir whitespace-nowrap shadow-sm">
|
||
استاندارد صنعتی آلمان
|
||
</div>
|
||
{product.specialBadge && (
|
||
<div className="px-3 py-1 bg-medical-gray-900 text-white rounded-full text-[10px] font-black uppercase tracking-widest font-vazir whitespace-nowrap shadow-sm">
|
||
{product.specialBadge}
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div className="flex flex-col gap-1.5 mb-4 text-center md:text-right w-full">
|
||
<h1 dir="rtl" className="text-xl sm:text-2xl md:text-3xl lg:text-4xl font-black text-medical-gray-900 leading-tight font-vazir">
|
||
{product.nameFa || product.name}
|
||
</h1>
|
||
{product.nameEn && (
|
||
<h2 dir="ltr" className="text-xs sm:text-base text-slate-400 font-bold italic font-sans text-center md:text-left">
|
||
{product.nameEn}
|
||
</h2>
|
||
)}
|
||
</div>
|
||
<p className="text-base sm:text-xl text-canina-blue font-bold italic opacity-80 leading-relaxed max-w-2xl font-vazir mb-4 text-center md:text-right" dir="rtl">
|
||
{isMounted && pets.length > 0 ? (
|
||
product.optimisticTemplate?.replace("[PetName]", activePet?.name || "").replace("[OnSet]", product.onSetOfAction || "به زودی")
|
||
) : (
|
||
product.scientificTagline
|
||
)}
|
||
</p>
|
||
{fullProduct.shortDescription && (
|
||
<p className="text-sm sm:text-lg text-medical-gray-600 font-bold leading-relaxed max-w-3xl font-vazir border-r-4 border-canina-blue pr-4 sm:pr-6 py-2 bg-canina-blue/5 rounded-l-2xl text-right w-full">
|
||
{fullProduct.shortDescription}
|
||
</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{/* ─── Symptoms / Therapeutic Indications Section ─── */}
|
||
{fullProduct.symptoms && fullProduct.symptoms.length > 0 && (
|
||
<section className="bg-canina-blue/[0.03] border border-canina-blue/10 rounded-[2rem] p-6 md:p-8 space-y-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-9 h-9 bg-canina-blue/10 text-canina-blue rounded-xl flex items-center justify-center">
|
||
<Stethoscope className="w-5 h-5" />
|
||
</div>
|
||
<h3 className="text-base font-black text-medical-gray-900 font-vazir">علائم و موارد درمانی مرتبط</h3>
|
||
</div>
|
||
<p className="text-[12px] text-medical-gray-400 font-vazir font-bold">این محصول برای رفع علائم زیر توسط دامپزشکان توصیه میشود:</p>
|
||
<div className="flex flex-wrap gap-2">
|
||
{fullProduct.symptoms.map((symptom: string, idx: number) => (
|
||
<a
|
||
key={idx}
|
||
href={`/shop?symptom=${encodeURIComponent(symptom)}`}
|
||
className="inline-flex items-center gap-1.5 px-3.5 py-1.5 rounded-full text-[11px] font-bold bg-white border border-canina-blue/20 text-canina-blue hover:bg-canina-blue hover:text-white hover:border-canina-blue transition-all shadow-sm cursor-pointer font-vazir whitespace-nowrap"
|
||
>
|
||
<span className="w-1.5 h-1.5 rounded-full bg-current opacity-60" />
|
||
{symptom}
|
||
</a>
|
||
))}
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
{/* Key Benefits Section */}
|
||
{fullProduct.keyBenefits && fullProduct.keyBenefits.length > 0 && (
|
||
<section className="space-y-8 pt-6 border-t border-medical-gray-100">
|
||
<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">
|
||
<Star className="w-6 h-6" />
|
||
</div>
|
||
<h3 className="text-3xl font-black text-medical-gray-900 italic font-vazir">چرا {fullProduct.name}؟</h3>
|
||
</div>
|
||
<div className="grid md:grid-cols-3 gap-6">
|
||
{fullProduct.keyBenefits.map((benefit, i) => {
|
||
const Icon = ICON_MAP[benefit.icon] || CheckCircle2;
|
||
return (
|
||
<div key={i} className="bg-white border border-medical-gray-200 rounded-[2.5rem] p-8 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all group overflow-hidden relative">
|
||
<div className="absolute -top-10 -left-10 w-24 h-24 bg-canina-blue/5 rounded-full group-hover:scale-150 transition-transform duration-700" />
|
||
<div className="w-12 h-12 bg-medical-gray-50 rounded-2xl flex items-center justify-center text-canina-blue mb-6 group-hover:bg-canina-blue group-hover:text-white transition-all">
|
||
<Icon className="w-6 h-6" />
|
||
</div>
|
||
<h4 className="text-xl font-black text-medical-gray-900 mb-2 font-vazir">{benefit.title}</h4>
|
||
<p className="text-sm text-medical-gray-500 font-bold font-vazir">{benefit.description}</p>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
{/* Comprehensive Product Description Section (Below the fold) */}
|
||
{fullProduct.description && (
|
||
<section className="bg-white border border-medical-gray-200 rounded-[2.5rem] 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">
|
||
<Info className="w-6 h-6" />
|
||
</div>
|
||
<h3 className="text-2xl font-black text-medical-gray-900 italic font-vazir">توضیحات و معرفی محصول</h3>
|
||
</div>
|
||
<div className="text-base text-medical-gray-600 font-medium leading-loose font-vazir space-y-4 pt-4 border-t border-medical-gray-100">
|
||
{fullProduct.description.split('\n').filter(Boolean).map((para, pIdx) => (
|
||
<p key={pIdx}>{para.trim()}</p>
|
||
))}
|
||
</div>
|
||
</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">
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-12 h-12 bg-canina-blue text-white rounded-2xl flex items-center justify-center shadow-lg">
|
||
<Clock className="w-6 h-6" />
|
||
</div>
|
||
<div>
|
||
<h3 className="text-lg sm:text-2xl font-black text-medical-gray-900 italic font-vazir whitespace-nowrap">ماشین حساب هوشمند مصرف</h3>
|
||
<p className="text-[11px] sm:text-xs text-medical-gray-400 font-bold font-vazir mt-0.5 sm:mt-1">تخمین دقیق دوز روزانه و ماندگاری هر بسته بر اساس مشخصات پت شما</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid md:grid-cols-2 gap-8 items-center">
|
||
{/* Manual Slider Inputs */}
|
||
<div className="bg-medical-gray-50 rounded-[2rem] p-6 border border-medical-gray-100 space-y-6">
|
||
<div className="flex items-center justify-between">
|
||
<span className="text-xs font-black text-medical-gray-500 font-vazir">وزن پت (کیلوگرم)</span>
|
||
<span className="text-lg font-black text-canina-blue font-vazir">{toPersian(weight)} کگ</span>
|
||
</div>
|
||
<input
|
||
type="range"
|
||
min="1" max="100"
|
||
value={weight}
|
||
onChange={(e) => setWeight(parseInt(e.target.value))}
|
||
className="w-full h-2 bg-medical-gray-200 rounded-lg appearance-none cursor-pointer accent-canina-blue [&::-webkit-slider-thumb]:w-6 [&::-webkit-slider-thumb]:h-6 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-canina-blue [&::-webkit-slider-thumb]:appearance-none"
|
||
/>
|
||
|
||
<div className="space-y-2">
|
||
<span className="text-[10px] font-black text-medical-gray-400 font-vazir">سن حیوان</span>
|
||
<div className="flex gap-2">
|
||
<button onClick={() => setPetType("young")} className={`flex-1 py-3 rounded-xl text-xs font-black transition-all border ${petType === "young" ? 'bg-canina-blue border-canina-blue text-white shadow-lg' : 'bg-white border-medical-gray-200 text-medical-gray-400'}`}>جوان</button>
|
||
<button onClick={() => setPetType("adult")} className={`flex-1 py-3 rounded-xl text-xs font-black transition-all border ${petType === "adult" ? 'bg-canina-blue border-canina-blue text-white shadow-lg' : 'bg-white border-medical-gray-200 text-medical-gray-400'}`}>بالغ</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Calculation Results Output */}
|
||
<div className="bg-canina-blue rounded-[2rem] p-8 text-white border-b-8 border-white/20 shadow-2xl flex flex-col justify-between h-full min-h-[220px]">
|
||
<div className="flex items-center gap-2 mb-4 opacity-90 border-b border-white/10 pb-3">
|
||
<Sparkles className="w-4 h-4 text-white" />
|
||
<span className="text-[10px] font-bold uppercase tracking-widest font-vazir text-white">تخمین هوش مصنوعی (AI Advice)</span>
|
||
</div>
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<div className="text-3xl font-black font-vazir text-white">{toPersian(calculation.duration)} روز</div>
|
||
<div className="text-[10px] font-bold text-white/80 font-vazir whitespace-nowrap mt-1">تخمین اتمام مصرف بسته</div>
|
||
</div>
|
||
<div className="text-left">
|
||
<div className="text-2xl font-black font-vazir text-white">{toPersian(calculation.dailyDose)}</div>
|
||
<div className="text-[10px] font-bold text-white/80 font-vazir whitespace-nowrap mt-1">دوز روزانه ({calculation.unit === 'g' ? 'گرم' : calculation.unit})</div>
|
||
</div>
|
||
</div>
|
||
<p className="text-[11px] text-white/60 font-bold font-vazir mt-6 leading-relaxed">
|
||
* دوزها تخمینی هستند؛ توصیه میشود برای مصرف دقیق کاتالوگ محصول را مطالعه کنید.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
{/* Ingredients Section */}
|
||
<section className="space-y-8">
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-12 h-12 bg-canina-blue text-white rounded-2xl flex items-center justify-center shadow-lg">
|
||
<FlaskConical className="w-6 h-6" />
|
||
</div>
|
||
<h3 className="text-3xl font-black text-medical-gray-900 italic font-vazir">ترکیبات و آنالیز علمی</h3>
|
||
</div>
|
||
|
||
<div className="space-y-10">
|
||
{/* Ingredients Cards */}
|
||
<div className="bg-white rounded-[2.5rem] p-8 md:p-10 border border-medical-gray-200 shadow-md">
|
||
<h4 className="text-xs font-black text-medical-gray-400 uppercase tracking-widest mb-6 font-vazir">مواد تشکیلدهنده برتر</h4>
|
||
<div className="flex flex-wrap gap-3">
|
||
{product.main_ingredients.map((ing, idx) => {
|
||
const termKey = termKeys.find(key => {
|
||
const termObj = scientificTerms[key] || SCIENTIFIC_TERMS[key];
|
||
const termName = termObj?.term || key;
|
||
const ingClean = ing.toLowerCase();
|
||
const keyClean = key.toLowerCase();
|
||
const termNameClean = termName.toLowerCase();
|
||
return ingClean.includes(keyClean) ||
|
||
keyClean.includes(ingClean) ||
|
||
ingClean.includes(termNameClean) ||
|
||
termNameClean.includes(ingClean);
|
||
});
|
||
return (
|
||
<div key={idx} className="bg-medical-gray-50 border border-medical-gray-100 px-5 py-3 rounded-2xl flex items-center gap-3 group hover:border-canina-blue/30 transition-all">
|
||
<span className="text-sm font-bold text-medical-gray-700 font-vazir">
|
||
{termKey ? (
|
||
<Tooltip termKey={termKey} onWikiNavigate={(id) => router.push(`/wiki?term=${id}`)}>
|
||
{ing}
|
||
</Tooltip>
|
||
) : ing}
|
||
</span>
|
||
<Sparkles className="w-3 h-3 text-canina-blue opacity-40 group-hover:opacity-100 transition-opacity" />
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Analysis Cards Grid */}
|
||
{Object.keys(product.analysis).length > 0 && (
|
||
<div className="space-y-6">
|
||
<div className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-4 font-vazir">تخمین اتمام بسته (هوش مصنوعی)</div>
|
||
<div className="grid grid-cols-2 gap-4">
|
||
{Object.entries(product.analysis).map(([key, value]) => (
|
||
<div key={key} className="bg-canina-blue text-white p-8 rounded-[2.5rem] border-b-2 border-white/20 shadow-xl group hover:scale-[1.02] transition-all duration-300">
|
||
<div className="text-[10px] font-black text-white/80 uppercase tracking-widest mb-3 font-vazir whitespace-nowrap">{key}</div>
|
||
<div className="text-3xl font-black font-vazir tracking-tighter text-white">{toPersian(value)}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</section>
|
||
|
||
{/* Feeding / Usage Instructions Section (Dynamic Header based on product category) */}
|
||
<section className="bg-white border border-medical-gray-200 rounded-[2.5rem] p-8 md:p-10 shadow-md">
|
||
<div className="flex items-center gap-4 mb-8">
|
||
<div className="w-12 h-12 bg-canina-blue text-white rounded-2xl flex items-center justify-center shadow-lg">
|
||
<CalendarDays className="w-6 h-6" />
|
||
</div>
|
||
<h3 className="text-2xl font-black text-medical-gray-900 italic font-vazir">
|
||
{product.categorySlug === 'special-care' || product.name.toLowerCase().includes('pfotenpflege') || product.name.toLowerCase().includes('augenpflege')
|
||
? 'دستورالعمل و نحوه استفاده موضعی'
|
||
: 'توصیه غذایی و نحوه مصرف'}
|
||
</h3>
|
||
</div>
|
||
<p className="text-medical-gray-700 leading-relaxed text-sm md:text-base font-medium font-vazir">
|
||
{product.feedingAdvice}
|
||
</p>
|
||
</section>
|
||
|
||
{/* Specialist Note */}
|
||
{product.specialist && (
|
||
<section className="bg-white rounded-[2.5rem] p-8 md:p-10 border border-medical-gray-200 shadow-md">
|
||
<div className="flex flex-col md:flex-row gap-10 items-start">
|
||
<img
|
||
src={product.specialist.image}
|
||
alt={product.specialist.name}
|
||
className="w-32 h-32 rounded-[2rem] object-cover border-4 border-medical-gray-50 shadow-xl"
|
||
/>
|
||
<div className="flex-1">
|
||
<div className="inline-flex items-center gap-2 px-3 py-1 bg-medical-gray-900 text-white rounded-full text-[10px] font-black uppercase tracking-widest mb-4 font-vazir whitespace-nowrap">
|
||
<Stethoscope className="w-3 h-3" />
|
||
مورد تأیید دامپزشکان
|
||
</div>
|
||
<h4 className="text-2xl font-black text-medical-gray-900 mb-1 font-vazir">{product.specialist.name}</h4>
|
||
<p className="text-sm font-bold text-canina-blue mb-6 font-vazir">{product.specialist.title}</p>
|
||
<p className="text-base text-medical-gray-600 leading-relaxed italic font-vazir">
|
||
"{product.specialist.message}"
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
)}
|
||
|
||
{/* Specialized Landing Banner */}
|
||
<section>
|
||
<div
|
||
className="relative bg-canina-blue rounded-[2.5rem] p-12 overflow-hidden group cursor-pointer border border-medical-gray-200 shadow-md"
|
||
onClick={() => toast.info("بزودی: صفحه فرود تخصصی این محصول در حال آمادهسازی است")}
|
||
>
|
||
<div className="absolute top-0 right-0 w-full h-full bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-10" />
|
||
<div className="absolute -bottom-20 -left-20 w-80 h-80 bg-white/10 rounded-full blur-3xl group-hover:bg-white/20 transition-all duration-700" />
|
||
|
||
<div className="relative z-10 flex flex-col md:flex-row items-center justify-between gap-8">
|
||
<div className="text-center md:text-right">
|
||
<h3 className="text-3xl lg:text-4xl font-black text-white mb-4 italic">مشاهده بررسی تخصصی و نتایج درمانی</h3>
|
||
<p className="text-white/80 font-bold font-vazir max-w-lg">
|
||
گزارشهای علمی، ویدیوهای آموزشی و تجربیات واقعی دیگر صاحبان پت در لندینگپیج اختصاصی این محصول.
|
||
</p>
|
||
</div>
|
||
<div className="bg-white text-canina-blue px-8 py-4 rounded-2xl font-black shadow-2xl hover:scale-105 transition-transform flex items-center gap-3">
|
||
ورود به آزمایشگاه علمی
|
||
<ArrowRight className="w-5 h-5 rotate-180" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* FAQ Section */}
|
||
{product.faqs && product.faqs.length > 0 && (
|
||
<section className="pt-10">
|
||
<div className="flex items-center gap-4 mb-10">
|
||
<div className="w-12 h-12 bg-blue-100 text-canina-blue rounded-2xl flex items-center justify-center">
|
||
<Info className="w-6 h-6" />
|
||
</div>
|
||
<h3 className="text-3xl font-black text-medical-gray-900 italic font-vazir">پاسخ به ابهامات شما</h3>
|
||
</div>
|
||
<div className="grid md:grid-cols-1 gap-4">
|
||
{product.faqs.map((faq, i) => (
|
||
<details key={i} className="group bg-white border border-medical-gray-200 rounded-[2rem] overflow-hidden hover:border-canina-blue/30 transition-all">
|
||
<summary className="flex items-center justify-between p-6 cursor-pointer list-none">
|
||
<span className="font-bold text-medical-gray-900 font-vazir">{faq.question}</span>
|
||
<ChevronDown className="w-5 h-5 text-canina-blue group-open:rotate-180 transition-transform" />
|
||
</summary>
|
||
<div className="px-6 pb-6 text-sm text-medical-gray-500 font-medium leading-relaxed font-vazir border-t border-medical-gray-50 pt-4">
|
||
{faq.answer}
|
||
</div>
|
||
</details>
|
||
))}
|
||
</div>
|
||
</section>
|
||
)}
|
||
</div>
|
||
|
||
{/* Sticky Sidebar (Clean Buy Box) - Sticky starts at the top, level with the title */}
|
||
<div className="lg:col-span-4 lg:sticky lg:top-28 space-y-8">
|
||
<div className="bg-white rounded-[2.5rem] border-2 border-medical-gray-100 p-8 shadow-2xl relative overflow-hidden">
|
||
<div className="absolute top-0 right-0 w-24 h-24 bg-canina-blue/5 rounded-full -mr-12 -mt-12" />
|
||
|
||
<div className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-1 font-vazir">قیمت نهایی محصول</div>
|
||
<div className="text-4xl font-black font-vazir text-medical-gray-900 tracking-tighter mb-8">
|
||
{toPersian(product.price)}
|
||
</div>
|
||
|
||
<div className="flex gap-4 mb-6">
|
||
{/* Quantity Selector - 40% Width */}
|
||
<div className="flex items-center gap-4 bg-medical-gray-50 border border-medical-gray-100 rounded-2xl p-2 h-16 w-[40%]">
|
||
<button
|
||
onClick={() => setItemQuantity(Math.max(1, itemQuantity - 1))}
|
||
className="w-10 h-10 flex items-center justify-center rounded-xl bg-white text-medical-gray-900 border border-medical-gray-200 hover:bg-canina-blue hover:text-white hover:border-canina-blue transition-all shadow-sm"
|
||
>
|
||
<Minus className="w-4 h-4" />
|
||
</button>
|
||
<span className="text-xl font-black text-medical-gray-900 flex-1 text-center font-vazir">
|
||
{toPersian(itemQuantity)}
|
||
</span>
|
||
<button
|
||
onClick={() => setItemQuantity(itemQuantity + 1)}
|
||
className="w-10 h-10 flex items-center justify-center rounded-xl bg-white text-medical-gray-900 border border-medical-gray-200 hover:bg-canina-blue hover:text-white hover:border-canina-blue transition-all shadow-sm"
|
||
>
|
||
<Plus className="w-4 h-4" />
|
||
</button>
|
||
</div>
|
||
|
||
{/* Add to Cart Button - 60% Width */}
|
||
<button
|
||
onClick={() => {
|
||
addItem(product, itemQuantity, { quantity: calculation.dailyDose, unit: calculation.unit });
|
||
toast.success(`${toPersian(itemQuantity)} عدد ${product.name} به سبد خرید اضافه شد`);
|
||
}}
|
||
className="w-[60%] flex items-center justify-center gap-1.5 px-4 bg-medical-gray-900 text-white rounded-2xl h-16 font-black text-sm hover:bg-canina-blue transition-all shadow-xl shadow-black/10 font-vazir whitespace-nowrap"
|
||
>
|
||
<ShoppingBag className="w-5 h-5 mb-1" />
|
||
افزودن به سبد خرید
|
||
</button>
|
||
</div>
|
||
|
||
<div className="mt-6 flex flex-col gap-3 border-t border-medical-gray-50 pt-6">
|
||
<div className="flex items-center gap-2 text-xs font-bold text-medical-gray-500 font-vazir">
|
||
<ShieldCheck className="w-4 h-4 text-green-500" />
|
||
ضمانت اصالت محصول (Made in Germany)
|
||
</div>
|
||
<div className="flex items-center gap-2 text-xs font-bold text-medical-gray-500 font-vazir">
|
||
<Clock className="w-4 h-4 text-canina-blue" />
|
||
آماده ارسال (تحویل حداکثر ۴۸ ساعت)
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* expected results box */}
|
||
{product.expectedResults && product.expectedResults.length > 0 && (
|
||
<div className="bg-white rounded-[2.5rem] border border-medical-gray-200 p-8 shadow-md">
|
||
<h4 className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-6 font-vazir">تغییرات قابل انتظار</h4>
|
||
<div className="space-y-4">
|
||
{product.expectedResults.map((res, i) => {
|
||
const Icon = ICON_MAP[res.icon] || Sparkles;
|
||
return (
|
||
<div key={i} className="flex items-center gap-4 group">
|
||
<div className="w-10 h-10 bg-medical-gray-50 rounded-xl flex items-center justify-center text-canina-blue group-hover:bg-canina-blue group-hover:text-white transition-all">
|
||
<Icon className="w-5 h-5" />
|
||
</div>
|
||
<span className="text-xs font-bold text-medical-gray-700 font-vazir">{res.text}</span>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Smart Cross-sell: Recommended Combinations */}
|
||
{product.relatedProducts && product.relatedProducts.length > 0 && (
|
||
<section className="mt-32 pt-20 border-t border-medical-gray-200">
|
||
<div className="flex flex-col items-center text-center mb-16 px-4">
|
||
<div className="inline-flex items-center gap-2 px-4 py-2 bg-canina-blue/5 border border-canina-blue/10 rounded-full text-canina-blue text-xs font-black uppercase tracking-widest mb-6 font-vazir whitespace-nowrap">
|
||
<Sparkles className="w-4 h-4" />
|
||
فرمولاسیون ترکیبی همافزا (Synergy Blend)
|
||
</div>
|
||
<h2 className="text-3xl lg:text-5xl font-black text-medical-gray-900 leading-tight">
|
||
اثربخشی <span className="text-canina-blue italic">دوبرابر</span> با ترکیب هوشمند
|
||
</h2>
|
||
</div>
|
||
|
||
<div className="grid lg:grid-cols-2 gap-8">
|
||
{product.relatedProducts.map(relId => {
|
||
const relProduct = allProducts.find(p => p.id === relId);
|
||
if (!relProduct) return null;
|
||
return (
|
||
<motion.div
|
||
key={relId}
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
className="bg-white rounded-[3rem] p-8 border border-medical-gray-200 flex flex-col md:flex-row gap-8 items-center cursor-pointer hover:shadow-2xl transition-all"
|
||
onClick={() => router.push(`/shop/${relProduct.id}`)}
|
||
>
|
||
<div className="w-40 h-40 bg-medical-gray-50 rounded-[2rem] p-4 flex items-center justify-center">
|
||
<SafeImage src={relProduct.image} alt={relProduct.name} className="w-full h-full drop-shadow-xl" imgClassName="object-contain" />
|
||
</div>
|
||
<div className="flex-1 text-center md:text-right">
|
||
<h4 className="text-xl font-black text-medical-gray-900 mb-2">{relProduct.name}</h4>
|
||
<p className="text-sm text-medical-gray-500 mb-6">{relProduct.description}</p>
|
||
<div className="flex items-center justify-center md:justify-start gap-4">
|
||
<span className="text-lg font-black text-canina-blue">{relProduct.price}</span>
|
||
<ArrowRight className="w-4 h-4 text-medical-gray-300" />
|
||
</div>
|
||
</div>
|
||
<div className="w-px h-24 bg-medical-gray-100 hidden md:block" />
|
||
<div className="flex items-center justify-center gap-1 group">
|
||
<span className="text-xs font-black text-medical-gray-400 group-hover:text-canina-blue transition-colors">مشاهده محصول مکمل</span>
|
||
<ChevronLeft className="w-4 h-4 text-medical-gray-300 group-hover:text-canina-blue group-hover:-translate-x-1 transition-all rtl:group-hover:translate-x-1" />
|
||
</div>
|
||
</motion.div>
|
||
);
|
||
})}
|
||
</div>
|
||
</section>
|
||
)}
|
||
</div>
|
||
|
||
{/* Refill Automation Popup */}
|
||
<AnimatePresence>
|
||
{showRefillModal && (
|
||
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
exit={{ opacity: 0 }}
|
||
onClick={() => setShowRefillModal(false)}
|
||
className="absolute inset-0 bg-medical-gray-900/60 backdrop-blur-md"
|
||
/>
|
||
<motion.div
|
||
initial={{ opacity: 0, scale: 0.9, y: 20 }}
|
||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||
exit={{ opacity: 0, scale: 0.9, y: 20 }}
|
||
className="bg-white w-full max-w-lg rounded-[3rem] p-10 relative z-10 shadow-2xl overflow-hidden"
|
||
>
|
||
<div className="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-canina-blue via-blue-400 to-canina-blue" />
|
||
<button
|
||
onClick={() => setShowRefillModal(false)}
|
||
className="absolute top-6 left-6 text-medical-gray-400 hover:text-medical-gray-600 transition-colors"
|
||
>
|
||
<X className="w-6 h-6" />
|
||
</button>
|
||
|
||
<div className="text-center">
|
||
<div className="w-20 h-20 bg-canina-blue/10 rounded-[2rem] flex items-center justify-center mx-auto mb-8">
|
||
<Bell className="w-10 h-10 text-canina-blue animate-bounce" />
|
||
</div>
|
||
<h3 className="text-3xl font-black text-medical-gray-900 mb-4 leading-tight italic">سیستم یادآوری هوشمند</h3>
|
||
<div className="bg-medical-gray-50 border border-medical-gray-100 rounded-3xl p-6 mb-8 text-right">
|
||
<p className="text-medical-gray-700 leading-relaxed font-medium font-vazir">
|
||
بر اساس وزن <span className="text-canina-blue font-bold">{toPersian(weight)} کیلوگرمی</span> {activePet ? `برای ${activePet.name}` : 'سگ شما'}، این بسته {toPersian(product.packageSize)} تایی دقیقاً <span className="text-canina-blue font-bold">{toPersian(calculation.duration)} روز</span> دیگر تمام میشود.
|
||
</p>
|
||
<div className="mt-4 flex items-center gap-2 text-xs font-bold text-medical-gray-500 font-vazir">
|
||
<ShieldCheck className="w-4 h-4 text-green-500" />
|
||
آیا مایلید سیستم ۵ روز قبل از اتمام، به شما پیامک یادآوری ارسال کند؟
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex flex-col gap-3">
|
||
<button
|
||
onClick={() => setShowRefillModal(false)}
|
||
className="w-full bg-canina-blue text-white py-5 rounded-2xl font-black text-lg hover:shadow-xl hover:shadow-canina-blue/20 transition-all flex items-center justify-center gap-2"
|
||
>
|
||
<CheckCircle2 className="w-5 h-5" />
|
||
بله، پیامک یادآوری فعال شود
|
||
</button>
|
||
<button
|
||
onClick={() => setShowRefillModal(false)}
|
||
className="w-full py-4 text-medical-gray-400 font-bold text-sm hover:text-medical-gray-600 transition-colors"
|
||
>
|
||
خیر، فقط محصول را به سبد خرید اضافه کن
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
</div>
|
||
)}
|
||
</AnimatePresence>
|
||
|
||
{/* Mobile Floating Sticky Bottom Buy Bar */}
|
||
<div className="lg:hidden fixed bottom-0 left-0 right-0 z-40 bg-white border-t border-medical-gray-200 p-3 shadow-2xl flex items-center justify-between gap-3 font-vazir" dir="rtl">
|
||
<div>
|
||
<span className="text-[10px] text-medical-gray-400 font-bold block">قیمت نهایی</span>
|
||
<span className="text-lg font-black text-medical-gray-900 font-vazir">{toPersian(product.price)}</span>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-2 flex-1 max-w-[240px]">
|
||
<div className="flex items-center gap-1 bg-medical-gray-50 border border-medical-gray-200 rounded-xl p-1 h-11">
|
||
<button
|
||
onClick={() => setItemQuantity(Math.max(1, itemQuantity - 1))}
|
||
className="w-7 h-7 flex items-center justify-center rounded-lg bg-white text-medical-gray-900 border border-medical-gray-200 font-bold text-xs"
|
||
>
|
||
-
|
||
</button>
|
||
<span className="w-5 text-center font-black text-medical-gray-900 text-xs font-vazir">
|
||
{toPersian(itemQuantity)}
|
||
</span>
|
||
<button
|
||
onClick={() => setItemQuantity(itemQuantity + 1)}
|
||
className="w-7 h-7 flex items-center justify-center rounded-lg bg-white text-medical-gray-900 border border-medical-gray-200 font-bold text-xs"
|
||
>
|
||
+
|
||
</button>
|
||
</div>
|
||
|
||
<button
|
||
onClick={() => {
|
||
addItem(product, itemQuantity, { quantity: calculation.dailyDose, unit: calculation.unit });
|
||
toast.success(`${toPersian(itemQuantity)} عدد به سبد خرید اضافه شد`);
|
||
}}
|
||
className="flex-1 bg-canina-blue text-white py-2.5 px-3 rounded-xl font-black text-xs hover:bg-canina-dark transition-all flex items-center justify-center gap-1.5 shadow-md shadow-canina-blue/20 whitespace-nowrap"
|
||
>
|
||
<ShoppingBag className="w-4 h-4" />
|
||
افزودن به سبد
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|