1203 lines
69 KiB
TypeScript
1203 lines
69 KiB
TypeScript
/* 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>
|
||
);
|
||
}
|
||
|