"use client"; import { useState, useEffect, useRef, useCallback, MouseEvent } from "react"; import { useRouter } from "next/navigation"; import { Heart, ShoppingCart, Plus, Minus, AlertTriangle, Zap, } from "lucide-react"; import { toast } from "sonner"; import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, type CarouselApi, } from "@/components/ui/carousel"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { useToggleFavorite, useIsFavorite } from "@/lib/hooks"; import { useAddToCart, useUpdateCartItemQuantity, useCart, } from "@/features/cart/hooks/useCart"; import { useTranslations } from "next-intl"; type ProductCardProps = { id: number; name: string; price: number | null; struct_price_text: string; discount?: number | null; discount_text?: string | null; images: string[]; labels?: { text: string; bg_color: string }[]; price_color?: string; height?: number; width?: number; button?: boolean; stock?: number; }; export default function ProductCard({ id, name, price, struct_price_text, images, labels = [], price_color = "#0A0A0A", height = 400, width = 300, button = false, stock, }: ProductCardProps) { const router = useRouter(); const t = useTranslations(); const { isFavorite, isLoading: isFavoriteLoading } = useIsFavorite(id); const { mutate: toggleFavorite, isPending: isFavoriteToggling } = useToggleFavorite(); const addToCartMutation = useAddToCart(); const updateCartMutation = useUpdateCartItemQuantity(); const { data: cartData, refetch: refetchCart } = useCart(); const [api, setApi] = useState(); const [current, setCurrent] = useState(0); const [localQuantity, setLocalQuantity] = useState(1); const [isSyncing, setIsSyncing] = useState(false); const [showStockModal, setShowStockModal] = useState(false); const [isHovered, setIsHovered] = useState(false); const autoplayRef = useRef(null); const debounceTimerRef = useRef(undefined); const isRequestInFlightRef = useRef(false); const pendingQuantityRef = useRef(null); const hasMultipleImages = images.length > 1; const cartItem = cartData?.data?.find((item: any) => item.product?.id === id); const isInCart = !!cartItem; const isOutOfStock = stock === 0; const availableStock = stock || 999; useEffect(() => { if (!api) return; setCurrent(api.selectedScrollSnap()); const onSelect = () => setCurrent(api.selectedScrollSnap()); api.on("select", onSelect); return () => { api.off("select", onSelect); }; }, [api]); useEffect(() => { if (!api || !hasMultipleImages) return; autoplayRef.current = setInterval(() => { api.canScrollNext() ? api.scrollNext() : api.scrollTo(0); }, 3000); return () => { if (autoplayRef.current) clearInterval(autoplayRef.current); }; }, [api, hasMultipleImages]); useEffect(() => { setLocalQuantity(cartItem?.product_quantity || 1); }, [cartItem]); const syncToServer = useCallback( async (quantity: number) => { if (isRequestInFlightRef.current) { pendingQuantityRef.current = quantity; return; } isRequestInFlightRef.current = true; setIsSyncing(true); try { await updateCartMutation.mutateAsync({ productId: id, quantity }); await refetchCart(); if (pendingQuantityRef.current !== null) { const nextQuantity = pendingQuantityRef.current; pendingQuantityRef.current = null; setTimeout(() => syncToServer(nextQuantity), 100); } } catch (error) { console.error("Sync failed:", error); setLocalQuantity(cartItem?.product_quantity || 1); toast.error("Failed to update quantity", { description: "Please try again", }); } finally { isRequestInFlightRef.current = false; setIsSyncing(false); } }, [id, updateCartMutation, cartItem, refetchCart], ); useEffect(() => { if (!isInCart || localQuantity === (cartItem?.product_quantity || 1)) return; if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current); debounceTimerRef.current = setTimeout(() => { syncToServer(localQuantity); }, 800); return () => { if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current); }; }, [localQuantity, isInCart, cartItem, syncToServer]); const handleFavorite = useCallback( (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); toggleFavorite( { productId: id, isFavorite }, { onSuccess: (data) => toast.success( data.wasAdded ? t("added_to_favorites") : t("removed_from_favorites"), ), onError: () => toast.error("Error. Try again"), }, ); }, [id, isFavorite, toggleFavorite], ); const handleAddToCart = useCallback( async (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); if (localQuantity > availableStock) { setShowStockModal(true); return; } setIsSyncing(true); try { await addToCartMutation.mutateAsync({ productId: id, quantity: localQuantity, }); toast.success(t("added_to_cart"), { description: `${name} ${t("added_to_cart_description")}`, }); } catch (error) { console.error("Add to cart error:", error); toast.error(t("add_to_cart_failed")); } finally { setIsSyncing(false); } }, [id, name, localQuantity, availableStock, addToCartMutation], ); const handleQuantityChange = useCallback( (e: MouseEvent, delta: number) => { e.preventDefault(); e.stopPropagation(); const newQuantity = localQuantity + delta; if (newQuantity < 1) return; if (newQuantity > availableStock) { setShowStockModal(true); return; } setLocalQuantity(newQuantity); }, [localQuantity, availableStock], ); const handleCardClick = useCallback( (e: MouseEvent) => { const target = e.target as HTMLElement; if ( target.closest("button") || target.closest('[data-carousel-control="true"]') || target.closest('[role="dialog"]') ) { e.preventDefault(); e.stopPropagation(); return; } e.preventDefault(); router.push(`/product/${id}`); }, [router, id], ); const handleNavClick = (e: MouseEvent, action: () => void) => { e.preventDefault(); e.stopPropagation(); action(); }; return ( <>
setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} className="flex justify-center cursor-pointer group" > {/* Image Section */}
{images.map((image, idx) => (
{`${name}
))}
{hasMultipleImages && ( <> handleNavClick(e, () => api?.scrollPrev())} /> handleNavClick(e, () => api?.scrollNext())} /> )}
{/* Image Dots */} {hasMultipleImages && (
{images.map((_, idx) => (
)} {/* Labels */} {labels.length > 0 && (
{labels.map((label, idx) => ( {label.text} ))}
)} {/* Out of Stock Overlay */} {isOutOfStock && (
{t("outOfStock")}
)}
{/* Content Section */} {/* Product Name */}

{name}

{/* Price */}

{struct_price_text}

{/* Favorite Button */} {/* Action Buttons */} {button && !isOutOfStock && (
{!isInCart ? ( ) : (
{isSyncing && (
)} {localQuantity}
)}
)}
{/* Stock Warning Modal */} e.stopPropagation()} >
{t("stock_limit_title")} {t("stock_limit_message", { product: name, stock: availableStock, })}
); }