added price filter, changed mobile filter ui
This commit is contained in:
@@ -13,8 +13,8 @@ import {
|
||||
useUpdateCartItemMutation,
|
||||
useCleanCartMutation,
|
||||
} from "../../app/api/cartApi";
|
||||
import { useCart } from "../../app/api/useCart";
|
||||
import { DecreaseIcon, IncreaseIcon } from "../../components/Icons";
|
||||
import { debounce } from "lodash";
|
||||
import Loader from "../../components/Loader/index";
|
||||
|
||||
const TruncatedDescription = ({ description, maxLength = 100 }) => {
|
||||
@@ -35,8 +35,8 @@ const TruncatedDescription = ({ description, maxLength = 100 }) => {
|
||||
__html: isExpanded
|
||||
? description
|
||||
: shouldTruncate
|
||||
? description.substring(0, maxLength) + "..."
|
||||
: description,
|
||||
? description.substring(0, maxLength) + "..."
|
||||
: description,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -44,25 +44,8 @@ const TruncatedDescription = ({ description, maxLength = 100 }) => {
|
||||
};
|
||||
|
||||
const CartPage = () => {
|
||||
const {
|
||||
data: response = {},
|
||||
error,
|
||||
isError,
|
||||
isLoading,
|
||||
} = useGetCartQuery(undefined, {
|
||||
refetchOnMountOrArgChange: 30, // ✅ Sadece 30 saniye sonra mount'ta refetch
|
||||
refetchOnFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
});
|
||||
const { cartData, cartItems, isLoading, isError, error } = useCart();
|
||||
|
||||
// Handle the new data structure - data is now an object grouped by store
|
||||
const cartData = isError ? {} : (response.data || {});
|
||||
|
||||
// Convert object of arrays to flat array for backward compatibility
|
||||
const cartItems = useMemo(() => {
|
||||
return Object.values(cartData).flat();
|
||||
}, [cartData]);
|
||||
|
||||
const { t, i18n } = useTranslation();
|
||||
const [checkoutStores, setCheckoutStores] = useState({});
|
||||
const [addToCart] = useAddToCartMutation();
|
||||
@@ -89,20 +72,22 @@ const CartPage = () => {
|
||||
|
||||
// Convert grouped data to stores array
|
||||
const stores = useMemo(() => {
|
||||
return Object.entries(cartData).map(([storeSlug, items]) => {
|
||||
if (!items || !items.length) return null;
|
||||
|
||||
// Get store info from first item
|
||||
const storeInfo = items[0]?.product?.channel?.[0];
|
||||
|
||||
return {
|
||||
id: storeInfo?.id || storeSlug,
|
||||
name: storeInfo?.name || storeSlug,
|
||||
slug: storeSlug,
|
||||
shipping_price: storeInfo?.shipping_price,
|
||||
items: items
|
||||
};
|
||||
}).filter(Boolean);
|
||||
return Object.entries(cartData)
|
||||
.map(([storeSlug, items]) => {
|
||||
if (!items || !items.length) return null;
|
||||
|
||||
// Get store info from first item
|
||||
const storeInfo = items[0]?.product?.channel?.[0];
|
||||
|
||||
return {
|
||||
id: storeInfo?.id || storeSlug,
|
||||
name: storeInfo?.name || storeSlug,
|
||||
slug: storeSlug,
|
||||
shipping_price: storeInfo?.shipping_price,
|
||||
items: items,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
}, [cartData]);
|
||||
|
||||
// ✅ Initialize local quantities from cart items
|
||||
@@ -121,73 +106,56 @@ const CartPage = () => {
|
||||
setPendingQuantities(newPendingQuantities);
|
||||
}, [cartItems]);
|
||||
|
||||
// ✅ Debounced update - tek bir useEffect
|
||||
// ✅ Debounced Cart Update - Her ürün için ayrı debounce
|
||||
useEffect(() => {
|
||||
const debouncedUpdates = {};
|
||||
const timers = {};
|
||||
|
||||
const updateItem = async (productId) => {
|
||||
const serverItem = cartItems.find((item) => item.product.id === productId);
|
||||
const serverQuantity = serverItem ? parseInt(serverItem.product_quantity, 10) : 0;
|
||||
Object.keys(pendingQuantities).forEach((productId) => {
|
||||
const serverItem = cartItems.find(
|
||||
(item) => String(item.product.id) === String(productId),
|
||||
);
|
||||
const serverQuantity = serverItem
|
||||
? parseInt(serverItem.product_quantity, 10)
|
||||
: 0;
|
||||
const pendingQuantity = pendingQuantities[productId];
|
||||
|
||||
// ✅ Eğer değişiklik yoksa, güncelleme yapma
|
||||
if (pendingQuantity === undefined || pendingQuantity === serverQuantity) {
|
||||
// Değişiklik yoksa veya 0 ise (Delete modalı tetikler) bir şey yapma
|
||||
if (
|
||||
pendingQuantity === undefined ||
|
||||
pendingQuantity === serverQuantity ||
|
||||
pendingQuantity <= 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoadingItems((prev) => ({ ...prev, [productId]: true }));
|
||||
|
||||
if (pendingQuantity <= 0) {
|
||||
await removeFromCart({ productId }).unwrap();
|
||||
} else {
|
||||
timers[productId] = setTimeout(async () => {
|
||||
try {
|
||||
setLoadingItems((prev) => ({ ...prev, [productId]: true }));
|
||||
await updateCartItem({
|
||||
productId,
|
||||
quantity: pendingQuantity,
|
||||
}).unwrap();
|
||||
}
|
||||
// ✅ RTK Query otomatik cache'i güncelleyecek, refetch'e gerek yok
|
||||
|
||||
} catch (error) {
|
||||
console.error("Failed to update cart:", error);
|
||||
|
||||
// ✅ Hata durumunda geri al
|
||||
const originalItem = cartItems.find(
|
||||
(item) => item.product.id === productId
|
||||
);
|
||||
if (originalItem) {
|
||||
const originalQty = parseInt(originalItem.product_quantity, 10) || 0;
|
||||
} catch (error) {
|
||||
console.error("Failed to update cart:", error);
|
||||
// Hata durumunda rollback
|
||||
setLocalQuantities((prev) => ({
|
||||
...prev,
|
||||
[productId]: originalQty,
|
||||
[productId]: serverQuantity,
|
||||
}));
|
||||
setPendingQuantities((prev) => ({
|
||||
...prev,
|
||||
[productId]: originalQty,
|
||||
[productId]: serverQuantity,
|
||||
}));
|
||||
} finally {
|
||||
setLoadingItems((prev) => ({ ...prev, [productId]: false }));
|
||||
}
|
||||
} finally {
|
||||
setLoadingItems((prev) => ({ ...prev, [productId]: false }));
|
||||
}
|
||||
};
|
||||
|
||||
// ✅ Her productId için debounced update oluştur
|
||||
Object.keys(pendingQuantities).forEach((productId) => {
|
||||
if (!debouncedUpdates[productId]) {
|
||||
debouncedUpdates[productId] = debounce(
|
||||
() => updateItem(productId),
|
||||
500 // ✅ 500ms debounce (daha stabil)
|
||||
);
|
||||
}
|
||||
debouncedUpdates[productId]();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
return () => {
|
||||
Object.values(debouncedUpdates).forEach((debouncedFn) =>
|
||||
debouncedFn.cancel()
|
||||
);
|
||||
Object.values(timers).forEach((timer) => clearTimeout(timer));
|
||||
};
|
||||
}, [pendingQuantities, cartItems, updateCartItem, removeFromCart]);
|
||||
}, [pendingQuantities, cartItems, updateCartItem]);
|
||||
|
||||
const handleQuantityIncrease = (productId) => (event) => {
|
||||
event.preventDefault();
|
||||
@@ -245,27 +213,25 @@ const CartPage = () => {
|
||||
}, 0);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const getStoreShippingPrice = (store) => {
|
||||
return store.shipping_price !== null && store.shipping_price !== undefined
|
||||
? parseFloat(store.shipping_price)
|
||||
return store.shipping_price !== null && store.shipping_price !== undefined
|
||||
? parseFloat(store.shipping_price)
|
||||
: 20;
|
||||
};
|
||||
|
||||
const handleCheckout = (storeId) => {
|
||||
setCheckoutStores(prev => ({ ...prev, [storeId]: true }));
|
||||
setCheckoutStores((prev) => ({ ...prev, [storeId]: true }));
|
||||
};
|
||||
|
||||
const handleBackToCart = (storeId) => {
|
||||
setCheckoutStores(prev => ({ ...prev, [storeId]: false }));
|
||||
setCheckoutStores((prev) => ({ ...prev, [storeId]: false }));
|
||||
};
|
||||
|
||||
const handleOrderSubmit = async (storeId, storeItems) => {
|
||||
if (checkoutStores[storeId] && checkoutRefs.current[storeId]) {
|
||||
const success = await checkoutRefs.current[storeId]();
|
||||
if (success) {
|
||||
setCheckoutStores(prev => ({ ...prev, [storeId]: false }));
|
||||
setCheckoutStores((prev) => ({ ...prev, [storeId]: false }));
|
||||
}
|
||||
} else {
|
||||
handleCheckout(storeId);
|
||||
@@ -292,7 +258,7 @@ const CartPage = () => {
|
||||
if (itemToDelete) {
|
||||
try {
|
||||
await removeFromCart({ productId: itemToDelete }).unwrap();
|
||||
|
||||
|
||||
setLocalQuantities((prev) => {
|
||||
const newState = { ...prev };
|
||||
delete newState[itemToDelete];
|
||||
@@ -318,9 +284,7 @@ const CartPage = () => {
|
||||
const handleEmptyCartConfirm = async () => {
|
||||
try {
|
||||
await cleanCart().unwrap();
|
||||
|
||||
|
||||
|
||||
|
||||
setLocalQuantities({});
|
||||
setPendingQuantities({});
|
||||
setCheckoutStores({});
|
||||
@@ -333,7 +297,7 @@ const CartPage = () => {
|
||||
const getTotalItemCount = () => {
|
||||
return cartItems.reduce(
|
||||
(sum, item) => sum + parseInt(item.product_quantity, 10),
|
||||
0
|
||||
0,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -397,14 +361,14 @@ const CartPage = () => {
|
||||
<Checkout
|
||||
cartItems={store.items}
|
||||
shippingPrice={shippingPrice}
|
||||
productIds={store.items.map(item => item.product.id)}
|
||||
productIds={store.items.map((item) => item.product.id)}
|
||||
onBackToCart={() => handleBackToCart(store.id)}
|
||||
onPlaceOrder={(placeOrderFn) => {
|
||||
checkoutRefs.current[store.id] = placeOrderFn;
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div style={{background:"white", width: "100%"}}>
|
||||
<div style={{ background: "white", width: "100%" }}>
|
||||
<div className={styles.storeHeader}>
|
||||
<h3>{store.name}</h3>
|
||||
</div>
|
||||
@@ -427,23 +391,32 @@ const CartPage = () => {
|
||||
</div>
|
||||
<div className={styles.priceQuantity}>
|
||||
<span className={styles.price}>
|
||||
{(parseFloat(item.product.price_amount) || 0).toFixed(2)} m.
|
||||
{(
|
||||
parseFloat(item.product.price_amount) || 0
|
||||
).toFixed(2)}{" "}
|
||||
m.
|
||||
</span>
|
||||
<div className={styles.quantityControls}>
|
||||
<button
|
||||
onClick={handleQuantityDecrease(item.product.id)}
|
||||
onClick={handleQuantityDecrease(
|
||||
item.product.id,
|
||||
)}
|
||||
className={styles.quantityBtn}
|
||||
disabled={loadingItems[item.product.id]}
|
||||
>
|
||||
<DecreaseIcon />
|
||||
</button>
|
||||
<span>
|
||||
{localQuantities[item.product.id] !== undefined
|
||||
{localQuantities[item.product.id] !==
|
||||
undefined
|
||||
? localQuantities[item.product.id]
|
||||
: parseInt(item.product_quantity, 10) || 0}
|
||||
: parseInt(item.product_quantity, 10) ||
|
||||
0}
|
||||
</span>
|
||||
<button
|
||||
onClick={handleQuantityIncrease(item.product.id)}
|
||||
onClick={handleQuantityIncrease(
|
||||
item.product.id,
|
||||
)}
|
||||
className={styles.quantityBtn}
|
||||
disabled={loadingItems[item.product.id]}
|
||||
>
|
||||
@@ -454,7 +427,9 @@ const CartPage = () => {
|
||||
<div className={styles.deleteBtnContainer}>
|
||||
<button
|
||||
className={styles.deleteBtn}
|
||||
onClick={() => showDeleteConfirm(item.product.id)}
|
||||
onClick={() =>
|
||||
showDeleteConfirm(item.product.id)
|
||||
}
|
||||
>
|
||||
<FaTrashAlt />
|
||||
</button>
|
||||
@@ -468,7 +443,9 @@ const CartPage = () => {
|
||||
|
||||
<div className={styles.storeSummary}>
|
||||
<div className={styles.cartContent}>
|
||||
<h3>{store.name} - {t("cart.basket")}:</h3>
|
||||
<h3>
|
||||
{store.name} - {t("cart.basket")}:
|
||||
</h3>
|
||||
<div className={styles.summaryRow}>
|
||||
<span>{t("cart.price")}:</span>
|
||||
<span>{storeTotal.toFixed(2)} m.</span>
|
||||
@@ -486,7 +463,9 @@ const CartPage = () => {
|
||||
onClick={() => handleOrderSubmit(store.id, store.items)}
|
||||
className={styles.checkoutBtn}
|
||||
>
|
||||
{checkoutStores[store.id] ? t("cart.order") : t("cart.prepareOrders")}
|
||||
{checkoutStores[store.id]
|
||||
? t("cart.order")
|
||||
: t("cart.prepareOrders")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -547,4 +526,4 @@ const CartPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default CartPage;
|
||||
export default CartPage;
|
||||
|
||||
Reference in New Issue
Block a user