import React, { useState, useRef, useEffect, useMemo } from "react";
import styles from "./CartPage.module.scss";
import { FaTrashAlt } from "react-icons/fa";
import Checkout from "../../components/Checkout";
import { Modal } from "antd";
import { useTranslation } from "react-i18next";
import EmptyCartState from "./emptyCart";
import {
useRemoveFromCartMutation,
useUpdateCartItemMutation,
useCleanCartMutation,
} from "../../app/api/cartApi";
import { useCart } from "../../app/api/useCart";
import { DecreaseIcon, IncreaseIcon } from "../../components/Icons";
import Loader from "../../components/Loader/index";
const isPriceZero = (price) => !price || parseFloat(price) === 0;
const TruncatedDescription = ({ description, maxLength = 100 }) => {
const stripHtml = (html) => {
const doc = new DOMParser().parseFromString(html, "text/html");
return doc.body.textContent || "";
};
const plainText = stripHtml(description);
const shouldTruncate = plainText.length > maxLength;
return (
);
};
const CartPage = () => {
const { cartData, cartItems, isLoading } = useCart();
const { t } = useTranslation();
const [checkoutStores, setCheckoutStores] = useState({});
const [removeFromCart] = useRemoveFromCartMutation();
const [updateCartItem] = useUpdateCartItemMutation();
const [cleanCart] = useCleanCartMutation();
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
const [emptyCartModalVisible, setEmptyCartModalVisible] = useState(false);
const [itemToDelete, setItemToDelete] = useState(null);
const [localQuantities, setLocalQuantities] = useState({});
const [pendingQuantities, setPendingQuantities] = useState({});
const [loadingItems, setLoadingItems] = useState({});
const checkoutRefs = useRef({});
const modalProps = {
centered: true,
className: styles.cartDeleteModal,
maskClosable: false,
width: 400,
};
const stores = useMemo(() => {
return Object.entries(cartData)
.map(([storeSlug, items]) => {
if (!items?.length) return null;
const storeInfo = items[0]?.product?.channel?.[0];
return {
id: storeInfo?.id || storeSlug,
name: storeInfo?.name || storeSlug,
slug: storeSlug,
shipping_price: storeInfo?.shipping_price,
items,
};
})
.filter(Boolean);
}, [cartData]);
useEffect(() => {
const newLocal = {};
const newPending = {};
cartItems.forEach((item) => {
const id = item.product.id;
const qty = parseInt(item.product_quantity, 10) || 0;
newLocal[id] = qty;
newPending[id] = qty;
});
setLocalQuantities(newLocal);
setPendingQuantities(newPending);
}, [cartItems]);
useEffect(() => {
const timers = {};
Object.keys(pendingQuantities).forEach((productId) => {
const serverItem = cartItems.find(
(item) => String(item.product.id) === String(productId),
);
const serverQty = serverItem
? parseInt(serverItem.product_quantity, 10)
: 0;
const pendingQty = pendingQuantities[productId];
if (
pendingQty === undefined ||
pendingQty === serverQty ||
pendingQty <= 0
)
return;
timers[productId] = setTimeout(async () => {
try {
setLoadingItems((prev) => ({ ...prev, [productId]: true }));
await updateCartItem({ productId, quantity: pendingQty }).unwrap();
} catch {
setLocalQuantities((prev) => ({ ...prev, [productId]: serverQty }));
setPendingQuantities((prev) => ({ ...prev, [productId]: serverQty }));
} finally {
setLoadingItems((prev) => ({ ...prev, [productId]: false }));
}
}, 500);
});
return () => Object.values(timers).forEach(clearTimeout);
}, [pendingQuantities, cartItems, updateCartItem]);
const handleQuantityIncrease = (productId) => (event) => {
event.preventDefault();
event.stopPropagation();
if (loadingItems[productId]) return;
const item = cartItems.find((i) => i.product.id === productId);
if (!item || localQuantities[productId] >= item.product.stock) return;
const newQty = (localQuantities[productId] || 0) + 1;
setLocalQuantities((prev) => ({ ...prev, [productId]: newQty }));
setPendingQuantities((prev) => ({ ...prev, [productId]: newQty }));
};
const handleQuantityDecrease = (productId) => (event) => {
event.preventDefault();
event.stopPropagation();
if (loadingItems[productId]) return;
const currentQty = localQuantities[productId] || 0;
if (currentQty <= 1) {
showDeleteConfirm(productId);
return;
}
const newQty = currentQty - 1;
setLocalQuantities((prev) => ({ ...prev, [productId]: newQty }));
setPendingQuantities((prev) => ({ ...prev, [productId]: newQty }));
};
const getStoreShippingPrice = (store) =>
store.shipping_price != null ? parseFloat(store.shipping_price) : 20;
// Store içinde fiyatsız ürün var mı?
const storeHasZeroPriceItem = (storeItems) =>
storeItems.some((item) => isPriceZero(item.product.price_amount));
const calculateStoreTotal = (storeItems) =>
storeItems.reduce((sum, item) => {
return (
sum +
(parseFloat(item.product.price_amount) || 0) *
(parseInt(item.product_quantity, 10) || 0)
);
}, 0);
const handleCheckout = (storeId) =>
setCheckoutStores((prev) => ({ ...prev, [storeId]: true }));
const handleBackToCart = (storeId) =>
setCheckoutStores((prev) => ({ ...prev, [storeId]: false }));
const handleOrderSubmit = async (storeId) => {
if (checkoutStores[storeId] && checkoutRefs.current[storeId]) {
const success = await checkoutRefs.current[storeId]();
if (success) setCheckoutStores((prev) => ({ ...prev, [storeId]: false }));
} else {
handleCheckout(storeId);
}
};
const showDeleteConfirm = (productId) => {
setItemToDelete(productId);
setDeleteModalVisible(true);
};
const handleDeleteConfirm = async () => {
if (itemToDelete) {
try {
await removeFromCart({ productId: itemToDelete }).unwrap();
setLocalQuantities((prev) => {
const s = { ...prev };
delete s[itemToDelete];
return s;
});
setPendingQuantities((prev) => {
const s = { ...prev };
delete s[itemToDelete];
return s;
});
} catch (e) {
console.error("Failed to remove item:", e);
}
}
setDeleteModalVisible(false);
setItemToDelete(null);
};
const handleEmptyCartConfirm = async () => {
try {
await cleanCart().unwrap();
setLocalQuantities({});
setPendingQuantities({});
setCheckoutStores({});
} catch (e) {
console.error("Failed to clean cart:", e);
}
setEmptyCartModalVisible(false);
};
const getTotalItemCount = () =>
cartItems.reduce(
(sum, item) => sum + parseInt(item.product_quantity, 10),
0,
);
return (
setDeleteModalVisible(false)}
okText={t("common.yes")}
cancelText={t("common.no")}
>
{t("common.Do_you_really_want_to_remove_the_item_from_the_cart")}
setEmptyCartModalVisible(false)}
okText={t("common.yes")}
cancelText={t("common.no")}
>
{t("common.Are_you_sure_you_want_to_empty_the_cart")}
{isLoading ? (
) : cartItems.length === 0 ? (
) : (
{t("cart.basket")} ({getTotalItemCount()})
{stores.map((store) => {
const shippingPrice = getStoreShippingPrice(store);
const storeTotal = calculateStoreTotal(store.items);
const totalWithShipping = storeTotal + shippingPrice;
const hasZeroPrice = storeHasZeroPriceItem(store.items);
return (
{checkoutStores[store.id] ? (
item.product.id)}
onBackToCart={() => handleBackToCart(store.id)}
onPlaceOrder={(fn) => {
checkoutRefs.current[store.id] = fn;
}}
/>
) : (
{store.name}
{store.items.map((item) => (
{item.product.name}
{isPriceZero(item.product.price_amount)
? "Bahasyny anyklamaly"
: `${parseFloat(item.product.price_amount).toFixed(2)} m.`}
{localQuantities[item.product.id] !==
undefined
? localQuantities[item.product.id]
: parseInt(item.product_quantity, 10) ||
0}
))}
)}
{/* ✅ Store Summary - fiyatsız ürün varsa "Baha anyklamak" */}
{store.name} - {t("cart.basket")}:
{hasZeroPrice ? (
<>
{t("cart.price")}:
Bahasyny anyklamaly
{t("cart.delivery")}:
Bahasyny anyklamaly
{t("cart.total")}:
Bahasyny anyklamaly
>
) : (
<>
{t("cart.price")}:
{storeTotal.toFixed(2)} m.
{t("cart.delivery")}:
{shippingPrice.toFixed(2)} m.
{t("cart.total")}:
{totalWithShipping.toFixed(2)} m.
>
)}
);
})}
{/* Mobile sticky summary */}
{/*
{t("cart.price")}:
{calculateTotal().toFixed(2)} m.
{t("cart.delivery")}:
{stores.reduce((sum, store) => sum + getStoreShippingPrice(store), 0).toFixed(2)} m.
{
setIsExpanded(!isExpanded);
e.target.style.outline = "none";
}}
>
{isExpanded ? (
) : (
)}
{t("cart.total")}:
{(calculateTotal() + stores.reduce((sum, store) => sum + getStoreShippingPrice(store), 0)).toFixed(2)} m.
*/}
)}
);
};
export default CartPage;