canina/frontend/application/components/ProductPage.tsx

1203 lines
69 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* eslint-disable @next/next/no-img-element */
"use client";
import { useState, useMemo, useEffect } from "react";
import { motion, AnimatePresence } from "motion/react";
import {
ArrowRight,
ChevronLeft,
ShieldCheck,
CheckCircle2,
Clock,
FlaskConical,
Stethoscope,
Info,
CalendarDays,
X,
Plus,
Minus,
Bell,
Sparkles,
ChevronDown,
Activity,
Heart,
Zap,
Star,
ShoppingBag,
Thermometer,
AlertTriangle,
Share2,
Phone,
Download,
Play
} from "lucide-react";
const ICON_MAP: Record<string, React.ComponentType<{ className?: string }>> = {
Sparkles,
Activity,
ShieldCheck,
Heart,
Zap,
Star
};
import { toPersian } from "../lib/utils";
import { Product } 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";
import api from "../lib/services/api";
import { DosageConfig } from "../lib/types";
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 dynamic from 'next/dynamic';
import Tooltip from "./Tooltip";
import SafeImage from "./SafeImage";
import PodcastInlinePlayer from "./PodcastInlinePlayer";
const VideoModalPlayer = dynamic(() => import("./VideoModalPlayer"), { ssr: false });
const ProductReviews = dynamic(() => import("./ProductReviews"), { ssr: false });
import { useRouter } from 'next/navigation';
export default function ProductPage({ productSlug }: { productSlug: string }) {
const router = useRouter();
const [product, setProduct] = useState<Product | null>(null);
const [allProducts, setAllProducts] = useState<Product[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [itemQuantity, setItemQuantity] = useState(1);
const [showRefillModal, setShowRefillModal] = useState(false);
const [isMounted, setIsMounted] = useState(false);
const [dosageConfig, setDosageConfig] = useState<DosageConfig | null>(null);
const [activeImage, setActiveImage] = useState<string | null>(null);
const [isImageZoomOpen, setIsImageZoomOpen] = useState(false);
const [isVideoModalOpen, setIsVideoModalOpen] = useState(false);
useEffect(() => {
Promise.resolve().then(() => setIsMounted(true));
}, []);
useEffect(() => {
productService.getProductBySlug(productSlug)
.then(p => {
setProduct(p);
if (p?.image) setActiveImage(p.image);
return productService.getProducts({ limit: 50 });
})
.then(res => {
if (res && res.data) setAllProducts(res.data);
})
.catch(err => console.error("Error fetching products in ProductPage:", err))
.finally(() => setIsLoading(false));
}, [productSlug]);
useEffect(() => {
if (product && product.id) {
api.get(`/products/${product.id}/dosage-config`)
.then(res => {
if (res.data) {
setDosageConfig(res.data);
}
})
.catch(err => console.error('[ProductPage] Dosage config fetch error:', err));
}
}, [product]);
const { pets, getActivePet } = usePetStore();
const activePet = getActivePet();
const { petType, weight, setPetType, setWeight } = useCalculatorStore();
const { addItem } = useCartStore();
const getText = useSettingsStore((state) => state.getText);
const isCatalogMode = getText('catalog_mode', 'false') === 'true';
const isCatalogHidePrices = getText('catalog_hide_prices', 'false') === 'true';
const isCatalogDisableCart = getText('catalog_disable_cart', 'false') === 'true';
const isCartDisabled = isCatalogMode || isCatalogDisableCart;
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
const fullProduct = useMemo(() => {
if (!product) return null;
const apiProduct = allProducts.find(p => p.id === product.id) || product;
if (!apiProduct) return null;
return {
...apiProduct,
packageSize: apiProduct.packageSize !== undefined && apiProduct.packageSize !== null ? Number(apiProduct.packageSize) : 100,
};
}, [product, allProducts]);
useEffect(() => {
if (activePet && activePet.weight > 0) {
setWeight(activePet.weight);
setPetType(activePet.age <= 1 ? "young" : "adult");
}
}, [activePet, setPetType, setWeight]);
const calculation = useMemo(() => {
if (!fullProduct) return null;
let dailyDose = 1;
let unit = fullProduct.unit?.includes('ml') ? "میلی‌لیتر" : fullProduct.unit?.includes('g') ? "گرم" : "قرص";
let description = petType === "young" ? "دوز رشد روزانه" : "دوز نگهداری روزانه";
if (dosageConfig) {
const baseW = dosageConfig.baseWeightKg || 10;
const baseD = dosageConfig.baseDose || 1;
const calculated = Math.round((weight / baseW) * baseD);
dailyDose = Math.max(1, calculated);
if (dosageConfig.maxDailyDose && dosageConfig.maxDailyDose > 0) {
dailyDose = Math.min(dailyDose, dosageConfig.maxDailyDose);
}
if (dosageConfig.unit) {
unit = dosageConfig.unit;
}
if (dosageConfig.instructions) {
description = dosageConfig.instructions;
}
} else if (typeof fullProduct.calculateDosage === 'function') {
const result = fullProduct.calculateDosage(weight, petType === "young");
dailyDose = result.quantity;
unit = result.unit;
description = result.description;
} else {
const factor = petType === "young" ? 0.2 : 0.1;
dailyDose = Math.max(1, Math.round(weight * factor));
}
const packageSize = fullProduct.packageSize !== undefined && fullProduct.packageSize !== null ? Number(fullProduct.packageSize) : 100;
const duration = Math.max(1, Math.floor((packageSize || 100) / (dailyDose || 1)));
return {
dailyDose,
duration: duration || 0,
unit,
description
};
}, [fullProduct, dosageConfig, 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;
Promise.resolve().then(() => 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>;
const isOutOfStock = (product.packageSize !== undefined && product.packageSize !== null ? Number(product.packageSize) : 100) <= 0;
return (
<div className="min-h-screen bg-medical-gray-50 pt-2 pb-36 sm:pb-20 px-4 sm:px-6 lg:px-8 font-vazir" dir="rtl">
<div className="max-w-7xl mx-auto w-full">
{/* Standard Breadcrumb Navigation & Integrated Back Button */}
<div className="flex flex-col sm:flex-row items-stretch sm:items-center justify-between gap-4 mb-8 pb-4 border-b border-medical-gray-200/60 font-vazir" dir="rtl">
<div className="flex items-center gap-2 text-xs font-bold text-medical-gray-400 overflow-x-auto py-1 sleek-hscroll">
<span className="cursor-pointer hover:text-canina-blue transition-colors shrink-0" onClick={() => router.push('/')}>خانه</span>
<ChevronLeft className="w-3 h-3 text-medical-gray-300 shrink-0" />
<span className="cursor-pointer hover:text-canina-blue transition-colors shrink-0" onClick={() => router.push('/shop')}>فروشگاه</span>
<ChevronLeft className="w-3 h-3 text-medical-gray-300 shrink-0" />
<span className="text-canina-blue font-black truncate">{product.nameFa || product.name}</span>
</div>
<div className="flex items-center justify-end gap-2 shrink-0">
<button
onClick={() => {
if (typeof window !== 'undefined' && navigator.clipboard) {
navigator.clipboard.writeText(window.location.href);
toast.success("لینک مشخصات و دوز دارویی محصول کپی شد");
}
}}
className="flex-1 sm:flex-none px-3.5 py-2 bg-canina-blue/5 border border-canina-blue/20 rounded-xl text-canina-blue shadow-xs flex items-center justify-center gap-1.5 font-bold text-xs hover:bg-canina-blue hover:text-white transition-all whitespace-nowrap"
>
<Share2 className="w-3.5 h-3.5" />
<span>اشتراکگذاری دوز</span>
</button>
<button
onClick={() => router.back()}
className="flex-1 sm:flex-none px-3.5 py-2 bg-white border border-medical-gray-200 rounded-xl text-medical-gray-600 shadow-xs flex items-center justify-center gap-1.5 font-bold text-xs hover:border-canina-blue hover:text-canina-blue transition-all whitespace-nowrap"
>
<span>بازگشت</span>
<ChevronLeft className="w-3.5 h-3.5" />
</button>
</div>
</div>
{/* Global Single Grid Layout - Aligns Sticky Buy Box at the Top */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 items-start w-full">
{/* Right/Main Content Column (lg:col-span-8) */}
<div className="w-full lg:col-span-8 space-y-12">
{/* Above the Fold Grid */}
<div className="grid grid-cols-1 md:grid-cols-12 gap-8 md:gap-12 items-center md:items-start text-center md:text-right font-vazir w-full" dir="rtl">
{/* Right Column: Product Image Frame & Gallery (md:col-span-5) */}
<div className="md:col-span-5 w-full mx-auto max-w-sm md:max-w-none space-y-4">
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
onClick={() => setIsImageZoomOpen(true)}
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 cursor-zoom-in"
title="کلیک برای مشاهده تصویر بزرگتر"
>
<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">
{isOutOfStock ? (
<div className="px-2.5 py-1 bg-rose-500 text-white rounded-full text-[9px] sm:text-[10px] font-black uppercase tracking-widest flex items-center gap-1 font-vazir shadow-sm">
ناموجود در انبار
</div>
) : (
<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={activeImage || product.image}
alt={product.name}
priority={true}
sizes="(max-width: 768px) 100vw, 400px"
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"
/>
<div className="absolute bottom-4 right-4 bg-black/60 backdrop-blur-md text-white px-3 py-1 rounded-full text-[10px] font-black flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<span>بزرگنمایی</span>
</div>
</motion.div>
{/* Multiple Gallery Thumbnails Strip */}
{(() => {
const allGalleryImages = [
product.image,
...(product.images || []).filter(img => img && img !== product.image)
].filter(Boolean);
if (allGalleryImages.length <= 1) return null;
return (
<div className="flex items-center gap-2 overflow-x-auto py-2 px-1 sleek-hscroll">
{allGalleryImages.map((imgUrl, idx) => {
const isSelected = (activeImage || product.image) === imgUrl;
return (
<button
key={idx}
type="button"
onClick={() => setActiveImage(imgUrl)}
className={`relative w-16 h-16 rounded-2xl overflow-hidden bg-white border-2 transition-all shrink-0 p-1 cursor-pointer ${
isSelected
? 'border-canina-blue shadow-md scale-105 ring-2 ring-canina-blue/20'
: 'border-medical-gray-200 hover:border-canina-blue/50 opacity-70 hover:opacity-100'
}`}
>
<SafeImage
src={imgUrl}
alt={`${product.name} - ${idx + 1}`}
className="w-full h-full"
imgClassName="w-full h-full object-contain"
/>
</button>
);
})}
</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>
<div className="px-3 py-1 bg-amber-500 text-white rounded-full text-[10px] font-black uppercase tracking-widest font-vazir whitespace-nowrap shadow-sm flex items-center gap-1">
<ShieldCheck className="w-3 h-3" />
کاتالوگ رسمی Canina
</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 && (
<div className="w-full text-right bg-canina-blue/5 border-r-4 border-canina-blue p-4 sm:p-6 rounded-2xl">
<p className="text-sm sm:text-lg text-medical-gray-600 font-bold leading-relaxed font-vazir">
{fullProduct.shortDescription}
</p>
</div>
)}
</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 (Only for oral/dosed products, not topical products) */}
{!(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-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">
{activePet ? `ماشین حساب هوشمند مصرف برای ${activePet.name}` : 'ماشین حساب هوشمند مصرف'}
</h3>
<p className="text-[11px] sm:text-xs text-medical-gray-400 font-bold font-vazir mt-0.5 sm:mt-1">
{activePet
? `تخمین دقیق دوز روزانه و ماندگاری هر بسته بر اساس وزن و سن ${activePet.name}`
: 'تخمین دقیق دوز روزانه و ماندگاری هر بسته بر اساس مشخصات پت شما'}
</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">
{activePet ? `وزن ${activePet.name} (کیلوگرم)` : 'وزن پت (کیلوگرم)'}
</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">
{activePet ? `سن ${activePet.name}` : 'سن حیوان'}
</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-[240px]">
<div className="flex items-center justify-between mb-4 opacity-90 border-b border-white/10 pb-3">
<div className="flex items-center gap-2">
<Sparkles className="w-4 h-4 text-amber-300" />
<span className="text-[10px] font-bold uppercase tracking-widest font-vazir text-white">
{activePet ? `محاسبه‌گر دوره درمانی برای ${activePet.name}` : 'محاسبه‌گر دوره درمانی (AI Calculator)'}
</span>
</div>
<span className="text-[10px] bg-white/20 px-2.5 py-0.5 rounded-full font-black">
پیشنهاد: {toPersian(itemQuantity)} بسته
</span>
</div>
<div className="grid grid-cols-2 gap-4">
<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>
<div className="mt-4 pt-4 border-t border-white/10 flex items-center justify-between">
<span className="text-[11px] font-bold text-white/80">تخمین هزینه دوره ۳۰ روزه:</span>
<span className="text-sm font-black text-amber-300">
{toPersian((Math.ceil(30 / (calculation.duration || 30)) * (product.priceValue || 0)).toLocaleString())} تومان
</span>
</div>
<p className="text-[10px] text-white/60 font-bold font-vazir mt-3 leading-relaxed">
{activePet
? `* دوز دقیق بر اساس وزن ${activePet.name} (${toPersian(weight)} ک‌گ) محاسبه شده است؛ برای ماندگاری کامل دوره، خرید تعداد بسته‌های پیشنهادی توصیه می‌شود.`
: '* دوز دقیق بر اساس وزن پت محاسبه شده است؛ برای ماندگاری کامل دوره، خرید تعداد بسته‌های پیشنهادی توصیه می‌شود.'}
</p>
</div>
</div>
</section>
)}
{/* Ingredients 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 border-b border-medical-gray-100 pb-6">
<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-2xl md:text-3xl font-black text-medical-gray-900 italic font-vazir">ترکیبات و آنالیز علمی</h3>
</div>
<div className="space-y-8">
{/* Ingredients Cards */}
{product.main_ingredients && product.main_ingredients.length > 0 && (
<div>
<h4 className="text-xs font-black text-medical-gray-400 uppercase tracking-widest mb-4 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>
)}
{/* Quantitative Analysis Cards Grid (Strict chemical percentages, not duplicate ingredients) */}
{(() => {
const filteredAnalysis = Object.entries(product.analysis || {}).filter(([key, val]) => {
const cleanKey = key.toLowerCase();
// Don't show duplicate ingredients here if already listed above
if (cleanKey.includes('مواد') || cleanKey.includes('ترکیبات') || cleanKey.includes('ingredient')) return false;
return Boolean(val);
});
if (filteredAnalysis.length === 0) return null;
return (
<div className="space-y-4 pt-4 border-t border-medical-gray-100">
<div className="text-xs font-black text-medical-gray-500 uppercase tracking-widest font-vazir">جدول آنالیز و آنالیتیکال ترکیبات</div>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4">
{filteredAnalysis.map(([key, value]) => (
<div key={key} className="bg-medical-gray-50 border border-medical-gray-200 p-5 rounded-2xl shadow-xs group hover:border-canina-blue/30 transition-all">
<div className="text-[11px] font-bold text-medical-gray-400 mb-1 font-vazir truncate">{key}</div>
<div className="text-xl font-black font-vazir tracking-tight text-canina-blue">{toPersian(value)}</div>
</div>
))}
</div>
</div>
);
})()}
</div>
</section>
{/* Feeding / Usage Instructions Section (Merged and enriched) */}
<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-6">
<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>
<div>
<h3 className="text-2xl font-black text-medical-gray-900 italic font-vazir">
{fullProduct.categorySlug === 'special-care' || fullProduct.name.toLowerCase().includes('pfotenpflege') || fullProduct.name.toLowerCase().includes('augenpflege')
? 'دستورالعمل و نحوه استفاده موضعی'
: 'توصیه غذایی و نحوه مصرف'}
</h3>
<p className="text-xs text-medical-gray-400 font-bold font-vazir mt-0.5">
{fullProduct.categorySlug === 'special-care' || fullProduct.name.toLowerCase().includes('pfotenpflege') || fullProduct.name.toLowerCase().includes('augenpflege')
? 'فرآورده بهداشتی / موضعی (اعمال روی موضع بدون وابستگی به وزن بدن)'
: 'راهنمای استاندارد تغذیه بر اساس کاتالوگ دارویی آلمان'}
</p>
</div>
</div>
{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('اسپری') ? (
<div className="bg-canina-blue/5 border border-canina-blue/20 rounded-2xl p-5 mb-6 text-sm text-medical-gray-700 font-bold leading-relaxed">
<p className="mb-2">
{product.feedingAdvice || "چندین بار در روز روی نواحی آسیب‌دیده مالیده و به آرامی ماساژ داده شود."}
</p>
<p className="text-xs text-medical-gray-500 font-medium pt-2 border-t border-canina-blue/10">
این محصول یک فرآورده موضعی/بهداشتی است و مقدار مصرف آن به صورت موضعی (روی پوست، چشم یا پنجه) اعمال میشود و وابسته به وزن بدن حیوان نمیباشد. ماندگاری بسته بسته به میزان و تکرار مصرف روزانه شما متفاوت خواهد بود.
</p>
</div>
) : (
<p className="text-medical-gray-700 leading-relaxed text-sm md:text-base font-medium font-vazir mb-6">
{product.feedingAdvice}
</p>
)}
{/* Storage & Precautions Bar */}
<div className="grid md:grid-cols-2 gap-4 pt-6 border-t border-medical-gray-100">
<div className="p-4 bg-medical-gray-50 rounded-2xl border border-medical-gray-100 flex items-start gap-3">
<Thermometer className="w-5 h-5 text-canina-blue flex-shrink-0 mt-0.5" />
<div>
<h5 className="text-xs font-black text-medical-gray-900 mb-1">شرایط نگهداری کاتالوگ آلمان</h5>
<p className="text-[11px] text-medical-gray-500 font-bold leading-normal">
{product.storage || "در جای خشک، خنک (زیر ۲۵ درجه سانتی‌گراد) و دور از تابش مستقیم نور خورشید نگهداری شود."}
</p>
</div>
</div>
<div className="p-4 bg-amber-50/60 rounded-2xl border border-amber-100 flex items-start gap-3">
<AlertTriangle className="w-5 h-5 text-amber-600 flex-shrink-0 mt-0.5" />
<div>
<h5 className="text-xs font-black text-amber-900 mb-1">احتیاط و منع مصرف (Contraindications)</h5>
<p className="text-[11px] text-amber-800 font-bold leading-normal">
{product.contraindications?.join(" • ") || "دور از دسترس کودکان نگهداری شود. پس از هربار مصرف درب قوطی را محکم ببندید."}
</p>
</div>
</div>
</div>
</section>
{/* Multimedia & Scientific Documentation (Video, Podcast & PDF Catalog) */}
{(product.videoUrl || product.podcastUrl || product.pdfUrl) && (
<section className="bg-white border border-medical-gray-200 rounded-[2.5rem] p-8 md:p-10 shadow-md space-y-8 font-vazir" dir="rtl">
<div className="flex items-center gap-4 border-b border-medical-gray-100 pb-6">
<div className="w-12 h-12 bg-purple-600 text-white rounded-2xl flex items-center justify-center shadow-lg">
<Sparkles className="w-6 h-6" />
</div>
<div>
<h3 className="text-2xl md:text-3xl font-black text-medical-gray-900 italic">رسانه و مستندات تخصصی محصول</h3>
<p className="text-xs text-medical-gray-400 font-bold mt-1">ویدیو آموزشی، پادکست تحلیل علمی و دفترچه کاتالوگ کارخانه Canina</p>
</div>
</div>
<div className="space-y-6">
{/* Video Player Row */}
{product.videoUrl && (
<div className="bg-gradient-to-br from-neutral-900 via-neutral-900 to-black rounded-3xl overflow-hidden shadow-xl border border-white/10 p-6 flex flex-col md:flex-row items-center gap-6 text-white group">
{/* Thumbnail / Cover Box */}
<div
onClick={() => setIsVideoModalOpen(true)}
className="relative w-full md:w-80 aspect-video rounded-2xl overflow-hidden cursor-pointer bg-neutral-800 border border-white/10 group-hover:border-canina-blue/50 transition-all flex items-center justify-center shrink-0"
>
<SafeImage
src={product.videoCover || product.image}
alt={product.videoTitle || product.name}
className="w-full h-full"
imgClassName="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-black/40 group-hover:bg-black/20 transition-colors flex items-center justify-center">
<div className="w-12 h-12 rounded-full bg-canina-blue text-white flex items-center justify-center shadow-xl shadow-canina-blue/50 group-hover:scale-110 transition-transform">
<Play className="w-5 h-5 fill-white ml-0.5" />
</div>
</div>
</div>
<div className="flex-1 text-center md:text-right space-y-2">
<div className="flex items-center justify-center md:justify-start gap-2">
<span className="text-xs font-black text-blue-400 bg-blue-500/10 px-3 py-1 rounded-full border border-blue-500/20 flex items-center gap-1.5">
<span className="w-2 h-2 rounded-full bg-red-500 animate-pulse" />
ویدیو راهنما و معرفی
</span>
</div>
<h4 className="text-lg font-black text-white">
{product.videoTitle || "ویدیو راهنمای مصرف و معرفی مکمل"}
</h4>
<p className="text-xs text-white/70 font-medium leading-relaxed">
{product.videoDescription || "مشاهده ویدیوی آموزشی، دوزبندی دقیق و نحوه مصرف مکمل توسط کادر علمی کنینا"}
</p>
<div className="pt-2 flex justify-center md:justify-start">
<button
type="button"
onClick={() => setIsVideoModalOpen(true)}
className="py-2.5 px-6 bg-canina-blue hover:bg-blue-600 text-white rounded-xl font-bold text-xs transition-all flex items-center gap-2 cursor-pointer shadow-md shadow-canina-blue/20"
>
<Play className="w-4 h-4 fill-white" />
<span>پخش ویدیو</span>
</button>
</div>
</div>
</div>
)}
{/* Podcast Player Row (Inline Direct Player with Soundcloud Waveform) */}
{product.podcastUrl && (
<PodcastInlinePlayer
podcast={{
audioUrl: product.podcastUrl,
title: product.podcastTitle || "پادکست و بررسی صوتی بالینی مکمل",
description: product.podcastDescription || product.description,
cover: product.podcastCover,
productName: product.name,
fallbackCover: product.image,
}}
/>
)}
{/* PDF Catalog Download Row */}
{product.pdfUrl && (
<div className="bg-medical-gray-50 border border-medical-gray-200 rounded-3xl p-6 flex flex-col sm:flex-row items-center justify-between gap-4 shadow-xs">
<div className="flex items-center gap-4 text-center sm:text-right">
<div className="w-14 h-14 rounded-2xl bg-red-100 text-red-600 flex items-center justify-center font-black text-sm shadow-sm shrink-0">
PDF
</div>
<div>
<h4 className="text-base font-black text-gray-900">
{product.pdfTitle || "بروشور و کاتالوگ علمی Canina آلمان"}
</h4>
<p className="text-xs text-gray-500 font-medium mt-0.5">
{product.pdfDescription || "شامل مقالات رفرنس، جدول ترکیبات دقیق و سرتیفیکیت رسمی"}
</p>
</div>
</div>
<a
href={product.pdfUrl}
target="_blank"
rel="noreferrer"
className="bg-canina-blue hover:bg-blue-700 text-white px-6 py-3.5 rounded-2xl font-black text-xs transition-all shadow-md shadow-canina-blue/20 flex items-center gap-2 shrink-0 cursor-pointer"
>
<Download className="w-4 h-4" />
<span>دانلود مستقیم فایل PDF</span>
</a>
</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>
)}
{/* Customer Reviews & Ratings Section */}
{product && (
<div className="pt-8">
<ProductReviews productId={product.id} productName={product.name} />
</div>
)}
</div>
{/* Sticky Sidebar (Clean Buy Box) - Hidden on mobile as mobile floating buy bar is active */}
<div className="hidden lg:block lg:col-span-4 lg:sticky lg:top-48 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-3xl sm:text-4xl font-black font-vazir text-medical-gray-900 tracking-tighter mb-8">
{isCatalogHidePrices ? "استعلام قیمت" : toPersian(product.price)}
</div>
{isCartDisabled ? (
<div className="space-y-3">
<a
href={getText('contact_phone_link', `tel:${getText('contact_phone', '02188884444').replace(/[^0-9+]/g, '')}`)}
className="w-full flex items-center justify-center gap-2 px-4 bg-canina-blue text-white rounded-2xl h-16 font-black text-sm hover:bg-canina-dark transition-all shadow-xl shadow-canina-blue/20 font-vazir"
>
<Phone className="w-5 h-5" />
<span>مشاوره و استعلام خرید ({getText('contact_phone', '۰۲۱-۸۸۸۸۴۴۴۴')})</span>
</a>
<p className="text-[11px] text-medical-gray-400 font-bold text-center">
خرید آنلاین در حالت کاتالوگ موقتاً غیرفعال است
</p>
</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 / Preorder / Stock Notify Button - 60% Width */}
{(product as unknown as { isPreorder?: boolean; preorderDeposit?: number }).isPreorder ? (
<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-purple-700 text-white rounded-2xl h-16 font-black text-sm hover:bg-purple-800 transition-all shadow-xl shadow-purple-900/20 font-vazir whitespace-nowrap"
>
<Sparkles className="w-5 h-5 mb-1 text-amber-300" />
{((product as unknown as { preorderDeposit?: number }).preorderDeposit || 0) > 0 ? `پیش‌خرید (بیعانه: ${((product as unknown as { preorderDeposit?: number }).preorderDeposit || 0).toLocaleString('fa-IR')} تومان)` : 'ثبت پیش‌خرید (رایگان)'}
</button>
) : (product.packageSize || 0) <= 0 ? (
<button
data-testid="product-desktop-out-of-stock-btn"
onClick={() => toast.info("درخواست اطلاع‌رسانی موجودی ثبت شد")}
className="w-[60%] flex items-center justify-center gap-1.5 px-4 bg-amber-500 text-white rounded-2xl h-16 font-black text-sm hover:bg-amber-600 transition-all shadow-xl shadow-amber-500/20 font-vazir whitespace-nowrap"
>
<Bell className="w-5 h-5 mb-1" />
موجود شد اطلاع بده
</button>
) : (
<button
data-testid="product-desktop-add-to-cart-btn"
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-base sm:text-lg font-black text-medical-gray-900 font-vazir">
{isCatalogHidePrices ? "استعلام قیمت" : toPersian(product.price)}
</span>
</div>
<div className="flex items-center gap-2 flex-1 max-w-[240px]">
{isCartDisabled ? (
<a
href={getText('contact_phone_link', `tel:${getText('contact_phone', '02188884444').replace(/[^0-9+]/g, '')}`)}
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"
>
<Phone className="w-4 h-4" />
استعلام و مشاوره
</a>
) : isOutOfStock ? (
<button
data-testid="product-out-of-stock-btn"
onClick={() => toast.info("درخواست اطلاع‌رسانی موجودی ثبت شد")}
className="flex-1 bg-amber-500 text-white py-2.5 px-3 rounded-xl font-black text-xs hover:bg-amber-600 transition-all flex items-center justify-center gap-1.5 shadow-md shadow-amber-500/20 whitespace-nowrap"
>
<Bell className="w-4 h-4" />
موجود شد خبر بده
</button>
) : (
<>
<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
data-testid="product-add-to-cart-btn"
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>
{/* Fullscreen High-Resolution Image Zoom / Lightbox Modal */}
<AnimatePresence>
{isImageZoomOpen && (
<div
className="fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-8 bg-black/85 backdrop-blur-md"
onClick={() => setIsImageZoomOpen(false)}
>
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
className="relative max-w-4xl max-h-[90vh] w-full flex flex-col items-center justify-center"
onClick={(e) => e.stopPropagation()}
>
{/* Close Button */}
<button
onClick={() => setIsImageZoomOpen(false)}
className="absolute -top-12 left-0 sm:left-auto sm:-right-12 w-10 h-10 rounded-full bg-white/20 hover:bg-white/30 text-white flex items-center justify-center transition-colors cursor-pointer"
title="بستن"
>
<X className="w-6 h-6" />
</button>
{/* Main Zoomed Image Container */}
<div className="bg-white rounded-3xl p-6 sm:p-10 shadow-2xl border border-white/20 max-h-[75vh] flex items-center justify-center overflow-hidden w-full">
<SafeImage
src={activeImage || product.image}
alt={product.name}
className="max-h-[60vh] max-w-full w-full"
imgClassName="max-h-[60vh] max-w-full w-auto object-contain drop-shadow-2xl mx-auto"
/>
</div>
{/* Lightbox Thumbnails Navigation */}
{(() => {
const allImgs = [
product.image,
...(product.images || []).filter(img => img && img !== product.image)
].filter(Boolean);
if (allImgs.length <= 1) return null;
return (
<div className="flex items-center gap-3 mt-4 overflow-x-auto py-2 px-4 max-w-full">
{allImgs.map((img, idx) => (
<button
key={idx}
onClick={() => setActiveImage(img)}
className={`w-14 h-14 rounded-xl overflow-hidden bg-white/10 border-2 transition-all p-1 shrink-0 ${
(activeImage || product.image) === img
? 'border-white scale-110 shadow-lg'
: 'border-transparent opacity-60 hover:opacity-100'
}`}
>
<SafeImage src={img} alt="Thumbnail" className="w-full h-full" imgClassName="w-full h-full object-contain" />
</button>
))}
</div>
);
})()}
</motion.div>
</div>
)}
</AnimatePresence>
{/* Dedicated Video Modal Player */}
<VideoModalPlayer
isOpen={isVideoModalOpen}
onClose={() => setIsVideoModalOpen(false)}
video={product.videoUrl ? {
title: product.videoTitle || product.name,
doctor: "کادر علمی و تخصصی کنینا",
videoUrl: product.videoUrl,
thumbnail: product.videoCover || product.image,
description: product.videoDescription || product.description,
} : null}
/>
</div>
);
}