diff --git a/src/app/api/cartApi.js b/src/app/api/cartApi.js index 2ddffcb..7ebb7c3 100644 --- a/src/app/api/cartApi.js +++ b/src/app/api/cartApi.js @@ -5,45 +5,23 @@ export const cartApi = baseApi.injectEndpoints({ getCart: builder.query({ query: () => `/carts`, providesTags: ["cartItems"], - pollingInterval: 5000, - refetchOnMountOrArgChange: true, + pollingInterval: 0, + refetchOnMountOrArgChange: 30, refetchOnFocus: false, - refetchOnReconnect: true, + refetchOnReconnect: false, transformResponse: (response) => { - if ( - typeof response === "string" && - (response.trim().startsWith(" response.data || null, }), - placeOrder: builder.mutation({ - query: (orderDetails) => { - orderDetails.shipping_method = 'standart'; - orderDetails.delivery_time = '13:00 - 18:00'; - orderDetails.region = 'ag'; - orderDetails.payment_type_id = 3; + placeOrder: builder.mutation({ + query: (orderDetails) => { + orderDetails.shipping_method = 'standart'; + orderDetails.delivery_time = '13:00 - 18:00'; + orderDetails.region = 'ag'; + // orderDetails.payment_type_id = 3; + + const formData = new URLSearchParams(); + + Object.keys(orderDetails).forEach(key => { + if (key === 'product_ids' && Array.isArray(orderDetails[key])) { + + orderDetails[key].forEach(id => { + formData.append('product_ids[]', id); + }); + } else { + formData.append(key, orderDetails[key]); + } + }); + return { url: "/orders", method: "POST", - body: new URLSearchParams(orderDetails), + body: formData, headers: { "Accept": "application/json", "Content-Type": "application/x-www-form-urlencoded", diff --git a/src/components/Checkout/index.jsx b/src/components/Checkout/index.jsx index 4993dfe..ed472e9 100644 --- a/src/components/Checkout/index.jsx +++ b/src/components/Checkout/index.jsx @@ -24,7 +24,7 @@ const useDeviceType = () => { return deviceType; }; -const Checkout = ({ cartItems, onBackToCart, onPlaceOrder }) => { +const Checkout = ({ cartItems, shippingPrice, productIds, onBackToCart, onPlaceOrder }) => { const { t } = useTranslation(); const [formData, setFormData] = useState({ customer_name: "", @@ -154,55 +154,57 @@ const Checkout = ({ cartItems, onBackToCart, onPlaceOrder }) => { delivery_time: defaultTimeSlot.hour, delivery_at: defaultTimeSlot.date, region: formData.region || "", - notes: formData.notes || "" + notes: formData.notes || "", + // Add shipping price and product IDs + shipping_price: shippingPrice, + product_ids: productIds // Array of product IDs [1, 3, 4, etc.] }; }; - // Make handlePlaceOrder available to the parent through a ref or expose it + // Create the place order function + const handlePlaceOrder = async () => { + const orderDetails = getOrderData(); + if (!orderDetails) return false; + + try { + const response = await placeOrder(orderDetails).unwrap(); + + console.log("Order placed successfully:", response); + window.location.href = "/orders"; + return true; + } catch (error) { + console.error("Failed to place order:", error); + + if ( + error.data && + typeof error.data === "string" && + error.data.includes("") + ) { + console.error( + "Server returned HTML instead of a proper API response" + ); + alert( + "There was a problem with the server. Please try again later or contact support." + ); + } else { + alert( + "Failed to place order. Please check your information and try again." + ); + } + return false; + } + }; + + // Expose the function to parent component via callback useEffect(() => { if (onPlaceOrder) { - onPlaceOrder.current = async () => { - const orderDetails = getOrderData(); - if (!orderDetails) return false; - - try { - const response = await placeOrder(orderDetails); - - if (response.data && !response.error) { - console.log("Order placed successfully:", response.data); - window.location.href = "/orders"; - return true; - } else { - throw new Error(response.error || "Unknown error occurred"); - } - } catch (error) { - console.error("Failed to place order:", error); - - if ( - error.data && - typeof error.data === "string" && - error.data.includes("") - ) { - console.error( - "Server returned HTML instead of a proper API response" - ); - alert( - "There was a problem with the server. Please try again later or contact support." - ); - } else { - alert( - "Failed to place order. Please check your information and try again." - ); - } - return false; - } - }; + onPlaceOrder(handlePlaceOrder); } - }, [formData, placeOrder, onPlaceOrder]); + }, [formData, shippingPrice, productIds]); return (
-

{t("cart.basket")} (2)

+

{t("cart.basket")} ({cartItems?.length || 0})

{t("checkout.paymentMethod")}:

diff --git a/src/components/Footer/FooterMobile.jsx b/src/components/Footer/FooterMobile.jsx index f32709d..ebdad34 100644 --- a/src/components/Footer/FooterMobile.jsx +++ b/src/components/Footer/FooterMobile.jsx @@ -2,31 +2,63 @@ import React from "react"; import { Home, ShoppingBag, ShoppingCart, Heart, User } from "lucide-react"; import { Link, useLocation } from "react-router-dom"; import styles from "./FooterBar.module.scss"; -import { useGetCartQuery } from "../../app/api/cartApi"; // Sepet API -import { useGetFavoritesQuery } from "../../app/api/favoritesApi"; // Favori API +import { useGetCartQuery } from "../../app/api/cartApi"; +import { useGetFavoritesQuery } from "../../app/api/favoritesApi"; import { useTranslation } from "react-i18next"; + const FooterBar = () => { const location = useLocation(); - const { t } = useTranslation(); - const { data: cartData } = useGetCartQuery(); - const { data: favoriteData } = useGetFavoritesQuery(); + const { data: cartData } = useGetCartQuery(); + const { data: favoriteData } = useGetFavoritesQuery(); - - - const cartCount = - cartData?.data?.reduce((total, item) => { - return total + (parseInt(item.product_quantity, 10) || 0); - }, 0) || 0; - + // FIX: Object içindeki tüm channel'ların item'larını birleştir + const getCartCount = () => { + if (!cartData?.data || typeof cartData.data !== 'object') { + return 0; + } + + // Object.values ile tüm channel array'lerini al + const allCartItems = Object.values(cartData.data).flat(); + + return allCartItems.reduce((total, item) => { + return total + (parseInt(item.product_quantity, 10) || 0); + }, 0); + }; + + const cartCount = getCartCount(); const favoriteCount = favoriteData?.length || 0; const navItems = [ { id: 1, icon: , label: t("navbar.home"), count: 0, path: "/" }, - { id: 2, icon: , label: t("navbar.brands"), count: 0, path: "/brands" }, - { id: 3, icon: , label: t("navbar.cart"), count: cartCount, path: "/cart" }, - { id: 4, icon: , label: t("wishtList.likedProducts"), count: favoriteCount, path: "/wishlist" }, - { id: 5, icon: , label: t("profile.profile"), count: 0, path: "/profile" }, + { + id: 2, + icon: , + label: t("navbar.brands"), + count: 0, + path: "/brands", + }, + { + id: 3, + icon: , + label: t("navbar.cart"), + count: cartCount, + path: "/cart", + }, + { + id: 4, + icon: , + label: t("wishtList.likedProducts"), + count: favoriteCount, + path: "/wishlist", + }, + { + id: 5, + icon: , + label: t("profile.profile"), + count: 0, + path: "/profile", + }, ]; return ( @@ -37,11 +69,15 @@ const FooterBar = () => {
{item.icon}
- {item.count > 0 &&
{item.count}
} + {item.count > 0 && ( +
{item.count}
+ )}
{item.label} @@ -52,4 +88,4 @@ const FooterBar = () => { ); }; -export default FooterBar; +export default FooterBar; \ No newline at end of file diff --git a/src/components/Navbar/NavbarDown.jsx b/src/components/Navbar/NavbarDown.jsx index ecb925a..d9e7be0 100644 --- a/src/components/Navbar/NavbarDown.jsx +++ b/src/components/Navbar/NavbarDown.jsx @@ -22,6 +22,7 @@ import { useGetOrdersQuery } from "../../app/api/orderApi"; import { useGetFavoritesQuery } from "../../app/api/favoritesApi"; import { useAuth } from "../../context/authContext"; import ProfileModal from "../../components/MyProfileModal/index"; + const NavbarDown = () => { const [isSearchVisible, setSearchVisible] = useState(false); const { t, i18n } = useTranslation(); @@ -34,10 +35,22 @@ const NavbarDown = () => { refetchOnMountOrArgChange: false, }); const { isAuthenticated, logout } = useAuth(); - const cartItemCount = - cartData?.data?.reduce((total, item) => { + + // FIX: Object içindeki tüm channel'ların item'larını birleştir + const getCartItemCount = () => { + if (!cartData?.data || typeof cartData.data !== 'object') { + return 0; + } + + // Object.values ile tüm channel array'lerini al ve flat ile birleştir + const allCartItems = Object.values(cartData.data).flat(); + + return allCartItems.reduce((total, item) => { return total + (parseInt(item.product_quantity, 10) || 0); - }, 0) || 0; + }, 0); + }; + + const cartItemCount = getCartItemCount(); const { data: ordersData } = useGetOrdersQuery(); const ordersItemCount = ordersData?.length || 0; @@ -45,6 +58,7 @@ const NavbarDown = () => { const { data: favoritesData } = useGetFavoritesQuery(); const favoritesItemCount = favoritesData?.length || 0; const [profileModalVisible, setProfileModalVisible] = useState(false); + const handleSearch = () => { if (searchQuery.trim()) { refetch(); @@ -60,7 +74,7 @@ const NavbarDown = () => { const toggleSearch = () => { setSearchVisible(!isSearchVisible); }; - + const changeLanguage = (langCode) => { i18n.changeLanguage(langCode); localStorage.setItem("preferredLanguage", langCode); @@ -69,11 +83,10 @@ const NavbarDown = () => { const handleLogout = async () => { await logout(); - }; - + useEffect(() => {}, [isAuthenticated]); - + const items = [ { key: "tk", @@ -115,6 +128,7 @@ const NavbarDown = () => { ), }, ]; + const profileItems = [ { key: "profile", @@ -125,15 +139,6 @@ const NavbarDown = () => {
), }, - // { - // key: "address", - // label: ( - // - // - // {t("profile.my_address")} - // - // ), - // }, { key: "logout", label: ( @@ -155,7 +160,6 @@ const NavbarDown = () => {
  • - {" "}
  • - {/*
  • - -
  • */} ) : ( <> diff --git a/src/components/ProductCard/imageCarousel/index.jsx b/src/components/ProductCard/imageCarousel/index.jsx index b1d31f3..336cf03 100644 --- a/src/components/ProductCard/imageCarousel/index.jsx +++ b/src/components/ProductCard/imageCarousel/index.jsx @@ -24,8 +24,8 @@ const ImageCarousel = ({ const currentImage = hasMultipleImages && images[currentIndex] - ? images[currentIndex].images_400x400 - : images[0]?.images_400x400 || ""; + ? images[currentIndex].images_1200x1200 + : images[0]?.images_1200x1200 || ""; // Auto-slide functionality useEffect(() => { @@ -215,7 +215,7 @@ const ImageCarousel = ({
    {altText {altText {altText { - // Create a temporary div to parse HTML const tempDiv = document.createElement("div"); tempDiv.innerHTML = htmlString; - - // Get text content without HTML tags const textContent = tempDiv.textContent || tempDiv.innerText || ""; - - // Truncate the text const truncatedText = textContent.length > maxLength ? textContent.substring(0, maxLength).trim() + "..." : textContent; - return truncatedText; }; @@ -45,51 +39,70 @@ const ProductCard = ({ onAddToCart, onToggleFavorite, isFavorite = false, - descriptionMaxLength = 85, // New prop to control description length + descriptionMaxLength = 85, }) => { const { t } = useTranslation(); const navigate = useNavigate(); const [stockErrorModalVisible, setStockErrorModalVisible] = useState(false); const [addFavorite] = useAddFavoriteMutation(); const [removeFavorite] = useRemoveFavoriteMutation(); - const { data: favoriteProducts = [], refetch } = useGetFavoritesQuery(); + const { data: favoriteProducts = [] } = useGetFavoritesQuery(); const [isLoading, setIsLoading] = useState(false); const [localIsFavorite, setLocalIsFavorite] = useState( favoriteProducts.some((fav) => fav.product?.id === product.id) ); - // Process description + const truncatedDesc = truncateDescription( product.description, descriptionMaxLength ); + // ✅ Sadece cache'den oku, yeni request gönderme const { data: cartData } = useGetCartQuery(undefined, { selectFromResult: (result) => ({ data: result.data, }), + refetchOnMountOrArgChange: false, // ✅ Mount'ta yeniden çağırma + refetchOnFocus: false, + refetchOnReconnect: false, }); const [addToCart] = useAddToCartMutation(); const [updateCartItem] = useUpdateCartItemMutation(); const [removeFromCart] = useRemoveFromCartMutation(); - const cartItem = cartData?.data?.find( - (item) => item.product?.id === product.id || item.product_id === product.id - ); - const quantity = cartItem?.quantity || cartItem?.product_quantity || 0; - const [localQuantity, setLocalQuantity] = useState(0); - const [pendingQuantity, setPendingQuantity] = useState(localQuantity); + // ✅ Cart data'yı düzgün parse et + const getCartItem = () => { + if (!cartData || typeof cartData !== "object") { + return null; + } + // Eğer data grouped object ise (store bazlı) + const allCartItems = Object.values(cartData).flat(); + + return allCartItems.find( + (item) => + item.product?.id === product.id || item.product_id === product.id + ); + }; + + const cartItem = getCartItem(); + const [localQuantity, setLocalQuantity] = useState(0); + const [pendingQuantity, setPendingQuantity] = useState(0); + + // ✅ Cart item değiştiğinde local state'i güncelle useEffect(() => { if (cartItem) { - setLocalQuantity(cartItem.quantity || cartItem.product_quantity || 0); - setPendingQuantity(cartItem.quantity || cartItem.product_quantity || 0); + const qty = cartItem.quantity || cartItem.product_quantity || 0; + setLocalQuantity(qty); + setPendingQuantity(qty); } else { setLocalQuantity(0); setPendingQuantity(0); } - }, [cartData, cartItem]); + }, [cartItem]); // ✅ Sadece cartItem değişince, cartData değil + // ✅ Favorite state'i güncelle useEffect(() => { if (Array.isArray(favoriteProducts)) { const isFav = favoriteProducts.some( @@ -102,34 +115,46 @@ const ProductCard = ({ 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 updateCart = async () => { - if (pendingQuantity !== quantity && pendingQuantity > 0) { + const currentCartQty = + cartItem?.quantity || cartItem?.product_quantity || 0; + + if (pendingQuantity !== currentCartQty && pendingQuantity > 0) { try { setIsLoading(true); await updateCartItem({ productId: product.id, quantity: pendingQuantity, }).unwrap(); + // ✅ RTK Query invalidatesTags ile otomatik güncellenecek } catch (error) { console.error("Failed to update cart item:", error); - setLocalQuantity(quantity); - setPendingQuantity(quantity); + // ✅ Hata varsa önceki değere dön + setLocalQuantity(currentCartQty); + setPendingQuantity(currentCartQty); } finally { setIsLoading(false); } @@ -138,12 +163,14 @@ const ProductCard = ({ const debouncedUpdate = debounce(updateCart, 300); - if (pendingQuantity !== quantity) { + const currentCartQty = + cartItem?.quantity || cartItem?.product_quantity || 0; + if (pendingQuantity !== currentCartQty) { debouncedUpdate(); } return () => debouncedUpdate.cancel(); - }, [pendingQuantity, quantity, product.id, updateCartItem]); + }, [pendingQuantity, cartItem, product.id, updateCartItem]); const handleQuantityIncrease = (event) => { event.preventDefault(); @@ -167,12 +194,18 @@ const ProductCard = ({ 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); }) @@ -188,24 +221,26 @@ const ProductCard = ({ 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(); - if (result === "Removed" || result?.status === "success") { - setLocalIsFavorite(false); - } + // ✅ Başarılı - RTK Query otomatik güncelleyecek } else { const result = await addFavorite(product.id).unwrap(); - if (result === "Added" || result?.status === "success") { - setLocalIsFavorite(true); - } + // ✅ Başarılı - RTK Query otomatik güncelleyecek } - await refetch(); } catch (error) { console.error("Failed to toggle favorite:", error); + // ✅ Hata varsa geri al + setLocalIsFavorite(localIsFavorite); } finally { setIsLoading(false); } @@ -234,8 +269,6 @@ const ProductCard = ({

    {name}

    - - {/* Simple truncated description */}

    {truncatedDesc}

    @@ -252,6 +285,7 @@ const ProductCard = ({ @@ -263,6 +297,7 @@ const ProductCard = ({ @@ -270,6 +305,7 @@ const ProductCard = ({ @@ -278,6 +314,7 @@ const ProductCard = ({ @@ -287,7 +324,6 @@ const ProductCard = ({
    - {/* Stock Error Modal */} { const [isExpanded, setIsExpanded] = useState(false); - // Strip HTML tags for character count const stripHtml = (html) => { const doc = new DOMParser().parseFromString(html, "text/html"); return doc.body.textContent || ""; @@ -48,14 +46,25 @@ const TruncatedDescription = ({ description, maxLength = 100 }) => { const CartPage = () => { const { data: response = {}, - refetch, error, isError, isLoading, - } = useGetCartQuery(); - const cartItems = isError ? [] : response.data || []; + } = useGetCartQuery(undefined, { + refetchOnMountOrArgChange: 30, // ✅ Sadece 30 saniye sonra mount'ta refetch + refetchOnFocus: false, + refetchOnReconnect: false, + }); + + // 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 [isCheckout, setIsCheckout] = useState(false); + const [checkoutStores, setCheckoutStores] = useState({}); const [addToCart] = useAddToCartMutation(); const [removeFromCart] = useRemoveFromCartMutation(); const [updateCartItem] = useUpdateCartItemMutation(); @@ -69,6 +78,8 @@ const CartPage = () => { const [localQuantities, setLocalQuantities] = useState({}); const [pendingQuantities, setPendingQuantities] = useState({}); const [loadingItems, setLoadingItems] = useState({}); + const checkoutRefs = useRef({}); + const modalProps = { centered: true, className: styles.cartDeleteModal, @@ -76,10 +87,25 @@ const CartPage = () => { width: 400, }; - const handleCheckout = () => setIsCheckout(true); - const handleBackToCart = () => setIsCheckout(false); - const checkoutRef = useRef({ current: null }); + // 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); + }, [cartData]); + // ✅ Initialize local quantities from cart items useEffect(() => { const newLocalQuantities = {}; const newPendingQuantities = {}; @@ -95,17 +121,17 @@ const CartPage = () => { setPendingQuantities(newPendingQuantities); }, [cartItems]); + // ✅ Debounced update - tek bir useEffect useEffect(() => { + const debouncedUpdates = {}; + const updateItem = async (productId) => { - const serverQuantity = - cartItems.find((item) => item.product.id === productId) - ?.product_quantity || 0; + const serverItem = cartItems.find((item) => item.product.id === productId); + const serverQuantity = serverItem ? parseInt(serverItem.product_quantity, 10) : 0; const pendingQuantity = pendingQuantities[productId]; - if ( - pendingQuantity === undefined || - pendingQuantity === parseInt(serverQuantity, 10) - ) { + // ✅ Eğer değişiklik yoksa, güncelleme yapma + if (pendingQuantity === undefined || pendingQuantity === serverQuantity) { return; } @@ -120,11 +146,12 @@ const CartPage = () => { quantity: pendingQuantity, }).unwrap(); } + // ✅ RTK Query otomatik cache'i güncelleyecek, refetch'e gerek yok - refetch(); } catch (error) { console.error("Failed to update cart:", error); + // ✅ Hata durumunda geri al const originalItem = cartItems.find( (item) => item.product.id === productId ); @@ -144,12 +171,12 @@ const CartPage = () => { } }; - const debouncedUpdates = {}; + // ✅ Her productId için debounced update oluştur Object.keys(pendingQuantities).forEach((productId) => { if (!debouncedUpdates[productId]) { debouncedUpdates[productId] = debounce( () => updateItem(productId), - 300 + 500 // ✅ 500ms debounce (daha stabil) ); } debouncedUpdates[productId](); @@ -160,7 +187,7 @@ const CartPage = () => { debouncedFn.cancel() ); }; - }, [pendingQuantities, cartItems, updateCartItem, removeFromCart, refetch]); + }, [pendingQuantities, cartItems, updateCartItem, removeFromCart]); const handleQuantityIncrease = (productId) => (event) => { event.preventDefault(); @@ -171,9 +198,11 @@ const CartPage = () => { const item = cartItems.find((item) => item.product.id === productId); if (!item) return; + // ✅ Stock kontrolü if (localQuantities[productId] >= item.product.stock) { return; } + const newQuantity = (localQuantities[productId] || 0) + 1; setLocalQuantities((prev) => ({ ...prev, @@ -185,18 +214,6 @@ const CartPage = () => { })); }; - const handleOrderSubmit = async () => { - if (isCheckout && checkoutRef.current) { - const success = await checkoutRef.current(); - if (success) { - refetch(); - setIsCheckout(false); - } - } else { - setIsCheckout(true); - } - }; - const handleQuantityDecrease = (productId) => (event) => { event.preventDefault(); event.stopPropagation(); @@ -205,10 +222,12 @@ const CartPage = () => { const currentQuantity = localQuantities[productId] || 0; + // ✅ 1'den azsa modal göster if (currentQuantity <= 1) { showDeleteConfirm(productId); return; } + const newQuantity = currentQuantity - 1; setLocalQuantities((prev) => ({ ...prev, @@ -220,14 +239,42 @@ const CartPage = () => { })); }; - const calculateTotal = () => { - return cartItems.reduce((sum, item) => { + const calculateStoreTotal = (storeItems) => { + return storeItems.reduce((sum, item) => { const itemPrice = parseFloat(item.product.price_amount) || 0; const itemQuantity = parseInt(item.product_quantity, 10) || 0; return sum + itemPrice * itemQuantity; }, 0); }; + + + const getStoreShippingPrice = (store) => { + return store.shipping_price !== null && store.shipping_price !== undefined + ? parseFloat(store.shipping_price) + : 20; + }; + + const handleCheckout = (storeId) => { + setCheckoutStores(prev => ({ ...prev, [storeId]: true })); + }; + + const handleBackToCart = (storeId) => { + setCheckoutStores(prev => ({ ...prev, [storeId]: false })); + }; + + const handleOrderSubmit = async (storeId, storeItems) => { + if (checkoutStores[storeId] && checkoutRefs.current[storeId]) { + const success = await checkoutRefs.current[storeId](); + if (success) { + // ✅ RTK Query otomatik güncelleyecek, refetch'e gerek yok + setCheckoutStores(prev => ({ ...prev, [storeId]: false })); + } + } else { + handleCheckout(storeId); + } + }; + useEffect(() => { const handleClickOutside = (event) => { if (expandedRef.current && !expandedRef.current.contains(event.target)) { @@ -246,8 +293,24 @@ const CartPage = () => { const handleDeleteConfirm = async () => { if (itemToDelete) { - await removeFromCart({ productId: itemToDelete }).unwrap(); - refetch(); + try { + await removeFromCart({ productId: itemToDelete }).unwrap(); + // ✅ RTK Query otomatik cache'i güncelleyecek + + // ✅ Local state'i de temizle + setLocalQuantities((prev) => { + const newState = { ...prev }; + delete newState[itemToDelete]; + return newState; + }); + setPendingQuantities((prev) => { + const newState = { ...prev }; + delete newState[itemToDelete]; + return newState; + }); + } catch (error) { + console.error("Failed to remove item:", error); + } } setDeleteModalVisible(false); setItemToDelete(null); @@ -258,17 +321,25 @@ const CartPage = () => { }; const handleEmptyCartConfirm = async () => { - await cleanCart().unwrap(); - refetch(); + try { + await cleanCart().unwrap(); + // ✅ RTK Query otomatik cache'i güncelleyecek + + // ✅ Local state'i temizle + setLocalQuantities({}); + setPendingQuantities({}); + setCheckoutStores({}); + } catch (error) { + console.error("Failed to clean cart:", error); + } setEmptyCartModalVisible(false); }; - const handleButtonClick = () => { - if (isCheckout) { - handleOrderSubmit(); - } else { - handleCheckout(); - } + const getTotalItemCount = () => { + return cartItems.reduce( + (sum, item) => sum + parseInt(item.product_quantity, 10), + 0 + ); }; return ( @@ -296,6 +367,7 @@ const CartPage = () => { >

    {t("common.Are_you_sure_you_want_to_empty_the_cart")}

    + {isLoading ? ( ) : cartItems.length === 0 ? ( @@ -303,118 +375,132 @@ const CartPage = () => { ) : (
    - {isCheckout ? ( - - ) : ( -
    -
    -

    - {t("cart.basket")} ( - {cartItems.reduce( - (sum, item) => sum + parseInt(item.product_quantity, 10), - 0 - )} - ) -

    -
    - -
    +
    +
    +

    + {t("cart.basket")} ({getTotalItemCount()}) +

    +
    +
    - {cartItems.map((item) => ( -
    -
    - {item.product.name} + + {stores.map((store) => { + const shippingPrice = getStoreShippingPrice(store); + const storeTotal = calculateStoreTotal(store.items); + const totalWithShipping = storeTotal + shippingPrice; + + return ( +
    + {checkoutStores[store.id] ? ( + item.product.id)} + onBackToCart={() => handleBackToCart(store.id)} + onPlaceOrder={(placeOrderFn) => { + checkoutRefs.current[store.id] = placeOrderFn; + }} /> -
    -
    -
    -

    {item.product.name}

    - {/* Replace the original description with the TruncatedDescription component */} - -
    -
    - - {(parseFloat(item.product.price_amount) || 0).toFixed( - 2 - )}{" "} - m. - -
    - - - {localQuantities[item.product.id] !== undefined - ? localQuantities[item.product.id] - : parseInt(item.product_quantity, 10) || 0} - - + ) : ( +
    +
    +

    {store.name}

    +
    +
    + {store.items.map((item) => ( +
    +
    + {item.product.name} +
    +
    +
    +

    {item.product.name}

    + +
    +
    + + {(parseFloat(item.product.price_amount) || 0).toFixed(2)} m. + +
    + + + {localQuantities[item.product.id] !== undefined + ? localQuantities[item.product.id] + : parseInt(item.product_quantity, 10) || 0} + + +
    +
    +
    + +
    +
    +
    + ))}
    -
    - + )} + +
    +
    +

    {store.name} - {t("cart.basket")}:

    +
    + {t("cart.price")}: + {storeTotal.toFixed(2)} m. +
    +
    + {t("cart.delivery")}: + {shippingPrice.toFixed(2)} m. +
    +
    + {t("cart.total")}: + {totalWithShipping.toFixed(2)} m. +
    +
    - ))} -
    - )} - -
    -
    -

    {t("cart.basket")}:

    -
    - {t("cart.price")}: - {calculateTotal().toFixed(2)} m. -
    -
    - {t("cart.delivery")} : - 0.00 m. -
    -
    - {t("cart.total")}: - {calculateTotal().toFixed(2)} m. -
    -
    - + ); + })}
    -
    + {/* Mobile sticky summary */} + {/*
    { >
    - {t("cart.price")}: + {t("cart.price")}: {calculateTotal().toFixed(2)} m.
    - {t("cart.delivery")}: - 0.00 m. + {t("cart.delivery")}: + + {stores.reduce((sum, store) => sum + getStoreShippingPrice(store), 0).toFixed(2)} m. +
    @@ -451,20 +539,12 @@ const CartPage = () => { {t("cart.total")}: - {calculateTotal().toFixed(2)} m. + {(calculateTotal() + stores.reduce((sum, store) => sum + getStoreShippingPrice(store), 0)).toFixed(2)} m.
    -
    - -
    -
    +
    */}
    )} @@ -472,4 +552,4 @@ const CartPage = () => { ); }; -export default CartPage; +export default CartPage; \ No newline at end of file diff --git a/src/pages/ProductDetail/index.jsx b/src/pages/ProductDetail/index.jsx index d1cfbfb..729408a 100644 --- a/src/pages/ProductDetail/index.jsx +++ b/src/pages/ProductDetail/index.jsx @@ -69,10 +69,20 @@ const ProductPage = ({ const [updateCartItem] = useUpdateCartItemMutation(); const [removeFromCart] = useRemoveFromCartMutation(); const [localQuantity, setLocalQuantity] = useState(0); - const cartItem = cartData?.data?.find( - (item) => - item.product?.id === product?.id || item.product_id === product?.id - ); + const getCartItem = () => { + if (!cartData?.data || typeof cartData.data !== "object") { + return null; + } + + const allCartItems = Object.values(cartData.data).flat(); + + return allCartItems.find( + (item) => + item.product?.id === product?.id || item.product_id === product?.id + ); + }; + + const cartItem = getCartItem(); const [pendingQuantity, setPendingQuantity] = useState(0); useEffect(() => {