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 } 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"; 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(); return (
{!isOwner && (
)} {/* Desktop breadcrumb */} {/* Top section: gallery | info (two columns on desktop) */}
{/* Gallery */}
{/* Info */}
{/* Seller (desktop) */} {!isOwner && product.seller?.username ? ( {product.seller.username} ) : null} {/* Inactive Product Status */} {!productActiveStatus && isOwner ? (
این محصول در حال حاضر غیرفعال است
) : !isOwner ? ( ) : null} {hasAnyStock ? ( <> {hasColorVariants && ( (color.info as unknown as { detail: string })?.detail )} selectedColor={selectedColor} setSelectedColor={setSelectedColor} /> )} {hasSizeVariants && ( size.value || "")} selectedSize={selectedSize} setSelectedSize={setSelectedSize} /> )} ) : (

موجود نیست

)} {isOwner && (
)} {/* Buy box (fixed bottom bar on mobile, inline on desktop) */} {!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: "در حال حاضر محصول مشابهی برای نمایش وجود ندارد", }} />
)} )}
); });