import React, { useState, useEffect } from "react"; import { useParams, useNavigate, Link } from "react-router-dom"; import styles from "./ProductPage.module.scss"; import { IoMdHeartEmpty, IoMdHeart } from "react-icons/io"; import { FaShoppingCart } from "react-icons/fa"; import ProductCard from "../../components/ProductCard/index"; import { useTranslation } from "react-i18next"; import { useGetProductByIdQuery, useGetRelatedProductsQuery, } from "../../app/api/categories"; import ReviewSection from "../../components/Review/index"; import { Modal } from "antd"; import { useAddFavoriteMutation, useRemoveFavoriteMutation, } from "../../app/api/favoritesApi"; import { useGetFavoritesQuery } from "../../app/api/favoritesApi"; import { useCart } from "../../app/api/useCart"; import { useAddToCartMutation, useUpdateCartItemMutation, useRemoveFromCartMutation, } from "../../app/api/cartApi"; import ImageCarousel from "../../components/ProductCard/imageCarousel/index"; import Loader from "../../components/Loader/index"; import { Result, Button } from "antd"; import { div } from "framer-motion/client"; import PendingPriceBadge from "../../components/PendingPriceBadge"; const isPriceZero = (price) => !price || parseFloat(price) === 0; const ProductPage = ({ productProp, showAddToCart = true, showFavoriteButton = true, onAddToCart, onToggleFavorite, isFavorite = false, }) => { const navigate = useNavigate(); const { productId } = useParams(); const { t } = useTranslation(); const { data: productResponse, error: productError, isLoading: productLoading, } = useGetProductByIdQuery(productId); const { data: similarProductsResponse, error: similarProductsError, isLoading: similarProductsLoading, } = useGetRelatedProductsQuery(productId); const product = productResponse?.data; const similarProducts = similarProductsResponse?.data; 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 [isDescExpanded, setIsDescExpanded] = useState(false); const [showReadMore, setShowReadMore] = useState(false); const descRef = React.useRef(null); const productInfoRef = React.useRef(null); useEffect(() => { if (!product?.description) return; const plainText = product.description.replace(/<[^>]*>/g, "").trim(); setShowReadMore(plainText.length > 300); }, [product?.description]); const { getCartItem } = useCart(); const [addToCart] = useAddToCartMutation(); const [updateCartItem] = useUpdateCartItemMutation(); const [removeFromCart] = useRemoveFromCartMutation(); const [localQuantity, setLocalQuantity] = useState(0); const [pendingQuantity, setPendingQuantity] = useState(0); const cartItem = getCartItem(product?.id || productId); // ✅ Sync local state with server cart useEffect(() => { const qty = parseInt( cartItem?.quantity || cartItem?.product_quantity || 0, 10, ); setLocalQuantity(qty); setPendingQuantity(qty); }, [cartItem]); // ✅ Sync favorite status useEffect(() => { if (Array.isArray(favoriteProducts)) { const isFav = favoriteProducts.some( (fav) => fav.product?.id === product?.id, ); setLocalIsFavorite(isFav); } }, [favoriteProducts, product?.id]); // ✅ Toggle Favorite const handleToggleFavorite = async (event) => { event.preventDefault(); event.stopPropagation(); if (isLoading) return; setIsLoading(true); const originalState = localIsFavorite; setLocalIsFavorite(!originalState); // Optimistic Update try { if (originalState) { await removeFavorite(product.id).unwrap(); } else { await addFavorite(product.id).unwrap(); } } catch (error) { console.error("Failed to toggle favorite:", error); setLocalIsFavorite(originalState); // Rollback } finally { setIsLoading(false); } }; // ✅ Add to Cart (Initial) const handleAddToCart = async (event) => { event.preventDefault(); event.stopPropagation(); if (product.stock <= 0) { setStockErrorModalVisible(true); return; } setLocalQuantity(1); setPendingQuantity(1); try { await addToCart({ productId: product.id, quantity: 1 }).unwrap(); } catch (error) { console.error("Failed to add to cart:", error); setLocalQuantity(0); setPendingQuantity(0); } }; 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) { setPendingQuantity(0); setLocalQuantity(0); setIsLoading(true); removeFromCart({ productId: product.id }) .unwrap() .then(() => { // Success handled by hook }) .catch(() => { setLocalQuantity(1); setPendingQuantity(1); }) .finally(() => { setIsLoading(false); }); } else { setLocalQuantity((prev) => prev - 1); setPendingQuantity((prev) => prev - 1); } }; // ✅ Debounced Cart Update 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]); if (productLoading || similarProductsLoading) return ; if (productError || similarProductsError) return ( navigate("/")}> Baş sahypa gidiň } /> ); if (!product) return Can not find product; const categoryName = product.categories?.[0]?.name || "Category"; const categoryId = product.categories?.[0]?.id; const handleCategoryClick = (categoryId) => { navigate(`/category/${categoryId}`); }; // ── Cart + favorite butonları (desktop purchase card + mobile bar'da ortak) ── const CartButtons = () => ( {showFavoriteButton && ( {localIsFavorite ? : } )} {showAddToCart && ( <> {localQuantity > 0 ? ( {localQuantity} ) : ( )} > )} ); return ( {/* Breadcrumb */} handleCategoryClick(categoryId)}> {categoryName} / {product?.name || "Product"} {/* ── 3 kolon ana section ── */} {/* KOLON 1: Resim */} {/* KOLON 2: İsim + Meta + Description */} {/* Meta tablo */} {product.name} {/* {t("product.productCode")} {product.id} {product.barcode && ( {t("product.barCode")} {product.barcode} )} */} {product.brand?.name && ( {t("order.brand")} {product.brand.name} )} {product.channel?.[0]?.name && ( {t("order.channel")} {product.channel[0].name} )} {product.properties?.length > 0 && ( product.properties.map((prop, index) => ( {prop.name} {prop.value} )) )} {/* Description card */} {product.description && ( {t("product.description")} {showReadMore && !isDescExpanded && ( setIsDescExpanded(true)} > {t("product.readMore")} )} {showReadMore && isDescExpanded && ( setIsDescExpanded(false)} > {t("product.readLess")} )} )} {/* KOLON 3: Satın alma kartı (sadece desktop/tablet) */} {/* Fiyat */} {t("product.price")}: {isPriceZero(product.price_amount) ? ( {t("cart.pendingPriceTitle")} ) : ( <> {product.price_amount} m. {product.old_price_amount && ( {product.old_price_amount} m. )} > )} {/* Butonlar */} {/* ── Mobile sticky bar ── */} {isPriceZero(product.price_amount) ? ( {t("cart.pendingPriceTitle")} ) : ( <> {product.price_amount} m. {product.old_price_amount && ( {product.old_price_amount} m. )} > )} {/* Reviews */} {/* Similar Products */} {t("product.similarProducts")} {Array.isArray(similarProducts) && similarProducts.map((product) => ( ))} {/* Stock modal */} setStockErrorModalVisible(false)} onCancel={() => setStockErrorModalVisible(false)} okText={t("common.ok")} footer={[ setStockErrorModalVisible(false)} className={styles.modalButton} > {t("common.ok")} , ]} > {t("common.not_enough_stock", { available: product.stock, requested: localQuantity + 1, })} ); }; export default ProductPage;
{t("product.description")}
{t("common.not_enough_stock", { available: product.stock, requested: localQuantity + 1, })}