import { useState, useEffect, useMemo, memo } from "react"; import { ProductHeader, ProductImageCarousel, ProductActions, ProductInfo, ColorSelector, SizeSelector, ContactButton, ReviewSection, BottomBar, } from "./index"; import { ProductDetail as ProductDetailType } from "../../../src/api/types"; import { Eye, EyeOff, Heart, Sparkles, Star, ShoppingCart, Truck, RotateCcw, ShieldCheck, MessageCircle, } from "lucide-react"; import { Button } from "../ui/button"; import { useNavigate, Link } from "@remix-run/react"; import { useSimilarProductsInfinite } from "~/requestHandler/use-product-hooks"; import MondrianProductList from "../MondrianProductList"; import SellerLogo from "../SellerLogo"; import { useServiceGetProductRating, useServiceGetComments, useServiceCheckBookmark, useServiceBookmarkProduct, useServiceDeleteBookmark, } from "~/utils/RequestHandler"; import { useAddToCart } from "~/requestHandler/use-cart-hooks"; import { useCreateChatThread } from "~/requestHandler/use-chat-hooks"; import { findVariantId, formatPrice } from "~/utils/helpers"; import { useToast } from "~/hooks/use-toast"; import { useRootData } from "~/hooks/use-root-data"; import { LoginRequiredDialog } from "../LoginRequiredDialog"; import AIPromoteModal from "../AIPromoteModal"; interface ProductDetailViewProps { product: ProductDetailType; isOwner: boolean; } export const ProductDetailView = memo(function ProductDetailView({ product, isOwner, }: ProductDetailViewProps) { // Local state for product active status (to handle real-time updates) const [productActiveStatus, setProductActiveStatus] = useState( product.isActive || false ); const [isChangeStatusDialogOpen, setIsChangeStatusDialogOpen] = useState(false); // Fetch similar products with infinite scroll const { data: similarProductsData, isLoading: isLoadingSimilar, isError: isErrorSimilar, refetch: refetchSimilar, fetchNextPage, hasNextPage, isFetchingNextPage, } = useSimilarProductsInfinite(product.id || "", 20); // Sync local state when product prop changes (e.g., on navigation) useEffect(() => { setProductActiveStatus(product.isActive || false); }, [product.isActive]); // Memoize available colors and sizes const { availableColors, availableSizes } = useMemo( () => ({ availableColors: product.attributes?.COLOR?.filter((p) => p.value !== "UNSELECTED") || [], availableSizes: product.attributes?.SIZE?.filter((p) => p.value !== "UNSELECTED") || [], }), [product.attributes] ); // Get variants with stock const stockedVariants = useMemo( () => product.variants?.filter( (variant) => variant.stock && variant.stock > 0 ) || [], [product.variants] ); // Memoize in-stock colors and sizes const { inStockColors, inStockSizes } = useMemo(() => { const colors = availableColors.filter((color) => { return stockedVariants.some( (variant) => variant.resolvedAttributes?.COLOR === color.value ); }); const sizes = availableSizes.filter((size) => { return stockedVariants.some( (variant) => variant.resolvedAttributes?.SIZE === size.value ); }); return { inStockColors: colors, inStockSizes: sizes }; }, [availableColors, availableSizes, stockedVariants]); // Memoize stock checks const { hasAnyStock, hasColorVariants, hasSizeVariants } = useMemo( () => ({ hasAnyStock: stockedVariants.length > 0, hasColorVariants: inStockColors.length > 0, hasSizeVariants: inStockSizes.length > 0, }), [stockedVariants.length, inStockColors.length, inStockSizes.length] ); const [selectedColor, setSelectedColor] = useState( hasColorVariants && inStockColors.length > 0 ? (inStockColors[0].info as unknown as { detail: string })?.detail : null ); const [selectedSize, setSelectedSize] = useState( hasSizeVariants && inStockSizes.length > 0 ? inStockSizes[0].value : null ); // Handle product status change const handleProductStatusChange = (newStatus: boolean) => { setProductActiveStatus(newStatus); }; const [currentPrice, setCurrentPrice] = useState(product.basePrice); const [currentDiscountPrice, setCurrentDiscountPrice] = useState< string | null >(null); const [currentStock, setCurrentStock] = useState(null); useEffect(() => { const matchingVariant = product.variants?.find((variant) => { let colorMatch = true; let sizeMatch = true; // Check color match only if product has color variants if (hasColorVariants) { if (selectedColor) { // Find the color value that matches the selected color detail const selectedColorValue = inStockColors.find( (color) => (color.info as unknown as { detail: string })?.detail === selectedColor )?.value; colorMatch = variant.resolvedAttributes?.COLOR === selectedColorValue; } else { colorMatch = false; // No color selected but product has color variants } } // Check size match only if product has size variants if (hasSizeVariants) { if (selectedSize) { sizeMatch = variant.resolvedAttributes?.SIZE === selectedSize; } else { sizeMatch = false; // No size selected but product has size variants } } return colorMatch && sizeMatch; }); if (matchingVariant) { setCurrentPrice(matchingVariant.price || product.basePrice); setCurrentDiscountPrice( matchingVariant.discountPrice && matchingVariant.discountPrice !== "0" && Number(matchingVariant.discountPrice) !== Number(matchingVariant.price) ? matchingVariant.discountPrice : null ); setCurrentStock(matchingVariant.stock || null); } else { // If no matching variant found, but we have variants and no color/size requirements // Use the first available variant (for products with no color/size variants) if (!hasColorVariants && !hasSizeVariants && stockedVariants.length > 0) { const firstVariant = stockedVariants[0]; setCurrentPrice(firstVariant.price || product.basePrice); setCurrentDiscountPrice( firstVariant.discountPrice && firstVariant.discountPrice !== "0" && Number(firstVariant.discountPrice) !== Number(firstVariant.price) ? firstVariant.discountPrice : null ); setCurrentStock(firstVariant.stock || null); } else { setCurrentPrice(product.basePrice); setCurrentDiscountPrice(null); setCurrentStock(null); } } }, [ selectedColor, selectedSize, product, inStockColors, hasColorVariants, hasSizeVariants, stockedVariants, ]); const navigate = useNavigate(); // --- Desktop buy/actions state & data (overlays + info column) --- const { user } = useRootData(); const { toast } = useToast(); const [qty, setQty] = useState(1); const [isAIOpen, setIsAIOpen] = useState(false); const [isLoginOpen, setIsLoginOpen] = useState(false); const { data: ratingData } = useServiceGetProductRating(product.id || ""); const { data: commentsData } = useServiceGetComments(1, product.id || ""); const { data: bookmarkData } = useServiceCheckBookmark(product.id || ""); const { mutate: bookmarkProduct } = useServiceBookmarkProduct(); const { mutate: deleteBookmark } = useServiceDeleteBookmark(); const addToCartMutation = useAddToCart(); const { mutate: createChatThread } = useCreateChatThread(); const isBookmarked = bookmarkData?.bookmarked === true; const avgRating = ratingData?.averageRating || 0; const reviewCount = commentsData?.count || 0; const isOutOfStock = currentStock === null || currentStock <= 0; const selectedColorValue = inStockColors.find( (color) => (color.info as unknown as { detail: string })?.detail === selectedColor )?.value || null; const handleToggleBookmark = () => { if (!user?.access_token) { setIsLoginOpen(true); return; } if (isBookmarked) deleteBookmark(product.id || ""); else bookmarkProduct(product.id || ""); }; const handleDesktopAddToCart = async () => { if (!user?.access_token) { setIsLoginOpen(true); return; } if (isOutOfStock) return; const variantId = findVariantId(product, selectedColorValue, selectedSize); if (!variantId) { toast({ title: "خطا", description: "محصول با ویژگی‌های انتخابی شما یافت نشد", variant: "destructive", }); return; } try { await addToCartMutation.mutateAsync({ variantId, quantity: qty }); toast({ title: "سبد خرید", description: "محصول با موفقیت به سبد خرید اضافه شد", }); } catch (e) { toast({ title: "خطا", description: "افزودن به سبد خرید با مشکل مواجه شد", variant: "destructive", }); } }; const handleChat = () => { if (!user?.access_token) { setIsLoginOpen(true); return; } if (!product.seller?.id) return; createChatThread(product.seller.id, { onSuccess: (t) => navigate(`/profile/threads/${t.id}`), }); }; return (
{!isOwner && (
)} {/* Desktop breadcrumb */} {/* Top section: gallery | info (two columns on desktop) */}
{/* Gallery */}
{!isOwner && ( <> {/* Save (desktop overlay) */} {/* AI try-on (desktop overlay) */} )}
{/* Info */}
{/* Seller (desktop) */} {!isOwner && product.seller?.username ? ( {product.seller.username} ) : null} {/* Inactive Product Status */} {!productActiveStatus && isOwner ? (
این محصول در حال حاضر غیرفعال است
) : !isOwner ? (
) : null} {/* Desktop: title + rating + price */}

{product.title}

{[1, 2, 3, 4, 5].map((i) => ( ))} {avgRating > 0 ? ( {avgRating.toFixed(1)} ) : null} {reviewCount} دیدگاه
{formatPrice(currentDiscountPrice || currentPrice)} تومان {currentDiscountPrice ? ( {formatPrice(currentPrice)} ) : null}
{/* Mobile: title + description */}
{hasAnyStock ? ( <> {hasColorVariants && ( (color.info as unknown as { detail: string })?.detail )} selectedColor={selectedColor} setSelectedColor={setSelectedColor} /> )} {hasSizeVariants && ( size.value || "")} selectedSize={selectedSize} setSelectedSize={setSelectedSize} /> )} ) : (

موجود نیست

)} {isOwner && (
)} {/* Desktop buy row + assurances */} {!isOwner && (
{qty}
ارسال سریع ۲ تا ۴ روز کاری
۷ روز بازگشت ضمانت تعویض کالا
پرداخت امن تضمین اصالت کالا
)} {/* Buy box (fixed bottom bar — mobile only) */} {!isOwner && (
(color.info as unknown as { detail: string })?.detail === selectedColor )?.value || null } selectedSize={selectedSize} />
)}
{/* Full-width below the fold: contact, reviews, similar */} {productActiveStatus && ( <> {!isOwner && (
)} {/* Similar Products Section */} {!isOwner && (

محصولات مشابه

محصولات مرتبط که ممکن است بپسندید

page.results || []) .map((product) => ({ id: product?.id || "", title: product?.title || "", images: Array.isArray(product?.imageUrls) ? product.imageUrls : [], discountPercent: product?.discountPercent || 0, discountPrice: product?.discountPrice || 0, basePrice: product?.basePrice || 0, })) || [] } fetchNextPage={fetchNextPage} hasNextPage={hasNextPage} isFetchingNextPage={isFetchingNextPage} isLoading={isLoadingSimilar} isError={isErrorSimilar} refetch={refetchSimilar} emptyState={{ title: "محصول مشابهی یافت نشد", description: "در حال حاضر محصول مشابهی برای نمایش وجود ندارد", }} />
)} )} {/* Desktop overlay/buy modals */} setIsAIOpen(false)} productId={product.id || ""} />
); });