import { useState, useEffect } from "react"; import styles from "./ProductCard.module.scss"; import { IoMdHeartEmpty, IoMdHeart } from "react-icons/io"; import { FaShoppingCart } from "react-icons/fa"; import { useNavigate } from "react-router-dom"; import { debounce } from "lodash"; import { useAddFavoriteMutation, useRemoveFavoriteMutation, } from "../../app/api/favoritesApi"; import { useGetFavoritesQuery } from "../../app/api/favoritesApi"; import { useAddToCartMutation, useUpdateCartItemMutation, useRemoveFromCartMutation, useGetCartQuery, } from "../../app/api/cartApi"; import { Modal } from "antd"; import { useTranslation } from "react-i18next"; import { DecreaseIcon, IncreaseIcon } from "../Icons"; import ImageCarousel from "./imageCarousel/index"; // Helper function to strip HTML tags and truncate text const truncateDescription = (htmlString, maxLength = 80) => { const tempDiv = document.createElement("div"); tempDiv.innerHTML = htmlString; const textContent = tempDiv.textContent || tempDiv.innerText || ""; const truncatedText = textContent.length > maxLength ? textContent.substring(0, maxLength).trim() + "..." : textContent; return truncatedText; }; import { useCart } from "../../app/api/useCart"; const ProductCard = ({ product, showAddToCart = true, showFavoriteButton = true, onAddToCart, onToggleFavorite, isFavorite = false, descriptionMaxLength = 85, }) => { const { t } = useTranslation(); const navigate = useNavigate(); const [stockErrorModalVisible, setStockErrorModalVisible] = useState(false); const [addFavorite] = useAddFavoriteMutation(); const [removeFavorite] = useRemoveFavoriteMutation(); const { data: favoriteProducts = [] } = useGetFavoritesQuery(); const [isLoading, setIsLoading] = useState(false); const [localIsFavorite, setLocalIsFavorite] = useState( favoriteProducts.some((fav) => fav.product?.id === product.id), ); // const [isHovered, setIsHovered] = useState(false); const truncatedDesc = truncateDescription( product.description, descriptionMaxLength, ); const { getCartItem } = useCart(); const [addToCart] = useAddToCartMutation(); const [updateCartItem] = useUpdateCartItemMutation(); const [removeFromCart] = useRemoveFromCartMutation(); const cartItem = getCartItem(product.id); const [localQuantity, setLocalQuantity] = useState(0); const [pendingQuantity, setPendingQuantity] = useState(0); // ✅ Cart item değiştiğinde local state'i güncelle useEffect(() => { const qty = parseInt( cartItem?.quantity || cartItem?.product_quantity || 0, 10, ); setLocalQuantity(qty); setPendingQuantity(qty); }, [cartItem]); // ✅ Favorite state'i güncelle useEffect(() => { if (Array.isArray(favoriteProducts)) { const isFav = favoriteProducts.some( (fav) => fav.product?.id === product.id, ); setLocalIsFavorite(isFav); } }, [favoriteProducts, product.id]); const handleAddToCart = async (event) => { event.preventDefault(); event.stopPropagation(); if (product.stock <= 0) { setStockErrorModalVisible(true); return; } // ✅ Optimistic update setLocalQuantity((prev) => prev + 1); setPendingQuantity((prev) => prev + 1); try { await addToCart({ productId: product.id, quantity: 1 }).unwrap(); // ✅ Başarılı - RTK Query otomatik cache'i güncelleyecek } catch (error) { console.error("Failed to add to cart:", error); // ✅ Hata varsa geri al setLocalQuantity((prev) => prev - 1); setPendingQuantity((prev) => prev - 1); } }; // ✅ Debounced update - sadece mutation, refetch yok useEffect(() => { const serverQty = parseInt( cartItem?.quantity || cartItem?.product_quantity || 0, 10, ); if (pendingQuantity === serverQty || pendingQuantity <= 0) { return; } const handler = setTimeout(async () => { try { setIsLoading(true); await updateCartItem({ productId: product.id, quantity: pendingQuantity, }).unwrap(); } catch (error) { console.error("Failed to update cart item:", error); setLocalQuantity(serverQty); setPendingQuantity(serverQty); } finally { setIsLoading(false); } }, 500); return () => clearTimeout(handler); }, [pendingQuantity, cartItem, product.id, updateCartItem]); const handleQuantityIncrease = (event) => { event.preventDefault(); event.stopPropagation(); if (isLoading) return; if (localQuantity >= product.stock) { setStockErrorModalVisible(true); return; } setLocalQuantity((prev) => prev + 1); setPendingQuantity((prev) => prev + 1); }; const handleQuantityDecrease = (event) => { event.preventDefault(); event.stopPropagation(); if (isLoading) return; if (pendingQuantity <= 1) { // ✅ Sıfıra düşünce direkt sil setPendingQuantity(0); setLocalQuantity(0); setIsLoading(true); removeFromCart({ productId: product.id }) .unwrap() .then(() => { // ✅ Başarılı - RTK Query cache'i güncelleyecek }) .catch(() => { // ✅ Hata varsa geri al setLocalQuantity(1); setPendingQuantity(1); }) .finally(() => { setIsLoading(false); }); } else { setLocalQuantity((prev) => prev - 1); setPendingQuantity((prev) => prev - 1); } }; const handleToggleFavorite = async (event) => { event.preventDefault(); event.stopPropagation(); if (isLoading) return; setIsLoading(true); // ✅ Optimistic update setLocalIsFavorite(!localIsFavorite); try { if (localIsFavorite) { const result = await removeFavorite(product.id).unwrap(); // ✅ Başarılı - RTK Query otomatik güncelleyecek } else { const result = await addFavorite(product.id).unwrap(); // ✅ Başarılı - RTK Query otomatik güncelleyecek } } catch (error) { console.error("Failed to toggle favorite:", error); // ✅ Hata varsa geri al setLocalIsFavorite(localIsFavorite); } finally { setIsLoading(false); } }; const handleCardClick = () => { navigate(`/product/${product.id}`); }; const [isHovered, setIsHovered] = useState(false); const { name, price_amount, old_price_amount, media = [], reviews } = product; // Hesaplanmış indirim oranı let calculatedDiscount = null; if (!product.discount && old_price_amount && price_amount && old_price_amount > price_amount) { calculatedDiscount = Math.round(((old_price_amount - price_amount) / old_price_amount) * 100); } return ( <>
setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} >
{(product.discount || calculatedDiscount) && ( -{product.discount ? product.discount : calculatedDiscount}% )} {product.stock === 0 && ( {t("common.out_of_stock")} )}

{name}

{truncatedDesc}

{price_amount} m. {old_price_amount && ( {old_price_amount} m. )}
{showFavoriteButton && ( )} {showAddToCart && ( <> {localQuantity > 0 ? (
{localQuantity}
) : ( )} )}
setStockErrorModalVisible(false)} onCancel={() => setStockErrorModalVisible(false)} okText={t("common.ok")} footer={[ , ]} >

{t("common.not_enough_stock", { available: product.stock, requested: localQuantity + 1, })}

); }; export default ProductCard;