"use client"; import { useState, MouseEvent, useEffect, useRef, useCallback } from "react"; import { Heart, ShoppingCart, Loader2, Plus, Minus } 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 { 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, discount, discount_text, images, labels = [], price_color = "#005bff", height = 360, width = 280, button = false, stock, }: ProductCardProps) { 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 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 !== undefined && stock === 0; const availableStock = stock || 999; const t = useTranslations(); useEffect(() => { if (!api) return; setCurrent(api.selectedScrollSnap()); api.on("select", () => { setCurrent(api.selectedScrollSnap()); }); }, [api]); useEffect(() => { if (!api || !hasMultipleImages) return; const startAutoplay = () => { autoplayRef.current = setInterval(() => { if (api.canScrollNext()) { api.scrollNext(); } else { api.scrollTo(0); } }, 3000); }; startAutoplay(); return () => { if (autoplayRef.current) { clearInterval(autoplayRef.current); } }; }, [api, hasMultipleImages]); // Sync localQuantity with cart useEffect(() => { if (cartItem?.product_quantity) { setLocalQuantity(cartItem.product_quantity); } else { setLocalQuantity(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: quantity, }); isRequestInFlightRef.current = false; setIsSyncing(false); await refetchCart(); if (pendingQuantityRef.current !== null) { const nextQuantity = pendingQuantityRef.current; pendingQuantityRef.current = null; setTimeout(() => syncToServer(nextQuantity), 100); } } catch (error) { console.error("Sync failed:", error); isRequestInFlightRef.current = false; setIsSyncing(false); setLocalQuantity(cartItem?.product_quantity || 1); } }, [id, updateCartMutation, cartItem, refetchCart] ); // Debounced sync useEffect(() => { if (!isInCart) return; if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current); } if (localQuantity === (cartItem?.product_quantity || 1)) { return; } 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 ? "Added to favorites" : "Removed from favorites" ); }, onError: () => { toast.error("Error. Try again"); }, } ); }, [id, isFavorite, toggleFavorite] ); const handleAddToCart = useCallback( async (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); setIsSyncing(true); try { await addToCartMutation.mutateAsync({ productId: id, quantity: localQuantity, }); await refetchCart(); setIsSyncing(false); toast.success("Added to cart", { description: `${name} has been added to your cart`, }); } catch (error) { console.error("Add to cart error:", error); setIsSyncing(false); toast.error("Failed to add to cart"); } }, [id, name, localQuantity, addToCartMutation, refetchCart] ); const handleQuantityIncrease = useCallback( (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); if (localQuantity >= availableStock) { toast.error("Stock limit reached", { description: `Only ${availableStock} items available`, }); return; } setLocalQuantity((prev) => prev + 1); }, [localQuantity, availableStock] ); const handleQuantityDecrease = useCallback( (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); if (localQuantity <= 1) return; setLocalQuantity((prev) => prev - 1); }, [localQuantity] ); const handleCardClick = (e: MouseEvent) => { const target = e.target as HTMLElement; if ( target.closest("button") || target.closest('[data-carousel-control="true"]') ) { e.preventDefault(); } }; const handleNavClick = (e: MouseEvent, action: () => void) => { e.preventDefault(); e.stopPropagation(); action(); }; return (
{images.map((image, index) => (
{`${name}
))}
{hasMultipleImages && ( <> handleNavClick(e, () => api?.scrollPrev())} /> handleNavClick(e, () => api?.scrollNext())} /> )}
{/* Favorite button */} {hasMultipleImages && (
{images.map((_, index) => (
)} {labels?.length > 0 && (
{labels.map((label, idx) => ( {label.text} ))}
)} {/* Out of Stock Overlay */} {isOutOfStock && (
Out of Stock
)}

{struct_price_text}

{name}

{/* Cart controls - show on hover if button enabled */} {button && !isOutOfStock && (
{!isInCart ? ( ) : (
{localQuantity} {isSyncing && ( )}
)}
)}
); }