From cc8945596760181f24c5ead138e3424f6e0c57e1 Mon Sep 17 00:00:00 2001 From: Jelaletdin12 Date: Sun, 26 Apr 2026 22:07:09 +0500 Subject: [PATCH] added baha tassyklamak --- src/components/Checkout/index.jsx | 200 +++++-------- src/components/ProductCard/index.jsx | 177 +++++------ src/pages/Cart/CartPage.module.scss | 6 +- src/pages/Cart/index.jsx | 282 +++++++----------- src/pages/OrderDetail/OrderDetail.module.scss | 113 ++++++- src/pages/OrderDetail/index.jsx | 249 +++++++++++----- src/pages/Orders/Orders.module.scss | 107 ++++++- src/pages/Orders/index.jsx | 205 +++++++++---- 8 files changed, 792 insertions(+), 547 deletions(-) diff --git a/src/components/Checkout/index.jsx b/src/components/Checkout/index.jsx index 23e2312..bff0206 100644 --- a/src/components/Checkout/index.jsx +++ b/src/components/Checkout/index.jsx @@ -1,6 +1,5 @@ import React, { useState, useEffect } from "react"; import styles from "./Checkout.module.scss"; -import { X } from "lucide-react"; import { useTranslation } from "react-i18next"; import { usePlaceOrderMutation, @@ -9,202 +8,145 @@ import { } from "../../app/api/orderApi"; import { useGetLocationsQuery } from "../../app/api/locationApi"; +const isPriceZero = (price) => !price || parseFloat(price) === 0; + const useDeviceType = () => { const [deviceType, setDeviceType] = useState("desktop"); - useEffect(() => { - const userAgent = navigator.userAgent; - if (/Mobi|Android/i.test(userAgent)) { - setDeviceType("mobile"); - } else { - setDeviceType("desktop"); - } + setDeviceType( + /Mobi|Android/i.test(navigator.userAgent) ? "mobile" : "desktop", + ); }, []); - return deviceType; }; -const Checkout = ({ cartItems, shippingPrice, productIds, onBackToCart, onPlaceOrder }) => { +const Checkout = ({ + cartItems, + shippingPrice, + productIds, + onBackToCart, + onPlaceOrder, +}) => { const { t } = useTranslation(); const [formData, setFormData] = useState({ customer_name: "", - customer_phone: "", + customer_phone: "+993 ", customer_address: "", - deliveryAddress: "null", payment_type_id: "", notes: "", region: "", }); const [selectedAddress, setSelectedAddress] = useState(null); - const [placeOrder, { isLoading: isPlacingOrder }] = usePlaceOrderMutation(); - const { data: orderTimes = {} } = useGetOrderTimesQuery(); + const [placeOrder] = usePlaceOrderMutation(); const { data: orderPayments = [] } = useGetOrderPaymentsQuery(); const { data: locationsData } = useGetLocationsQuery(); const deviceType = useDeviceType(); + // Sepetteki tüm ürünlerin fiyatı 0 mı? + const allItemsZeroPrice = cartItems?.every((item) => + isPriceZero(item.product?.price_amount), + ); + const handleInputChange = (e) => { const { name, value } = e.target; - + if (name === "customer_phone") { - // Always keep the +993 prefix const prefix = "+993 "; - - // If user is trying to delete the prefix, prevent it - if (value.length < prefix.length) { - return; // Don't update state, keep the current value - } - - // Extract only the digits after the prefix - const inputWithoutPrefix = value.substring(prefix.length).replace(/\D/g, ""); - - // Limit to 8 digits max (Turkmenistan mobile number format) - const limitedDigits = inputWithoutPrefix.substring(0, 8); - - // Format with space after first 2 digits - let formattedPhone = prefix; - if (limitedDigits.length > 0) { - formattedPhone += limitedDigits.substring(0, 2); - - if (limitedDigits.length > 2) { - formattedPhone += " " + limitedDigits.substring(2); - } - } - - setFormData((prev) => ({ - ...prev, - [name]: formattedPhone, - })); + if (value.length < prefix.length) return; + + const digits = value + .substring(prefix.length) + .replace(/\D/g, "") + .substring(0, 8); + let formatted = prefix + digits.substring(0, 2); + if (digits.length > 2) formatted += " " + digits.substring(2); + + setFormData((prev) => ({ ...prev, [name]: formatted })); } else { - setFormData((prev) => ({ - ...prev, - [name]: value, - })); + setFormData((prev) => ({ ...prev, [name]: value })); } }; const handleAddressSelect = (value) => { setSelectedAddress(value); - const selectedLocation = locationsData?.data?.find( - (location) => location.name === value - ); - + const selectedLocation = locationsData?.data?.find((l) => l.name === value); setFormData((prev) => ({ ...prev, address: value, - region: selectedLocation ? selectedLocation.region : "", + region: selectedLocation?.region || "", })); }; - // Initialize phone with prefix - useEffect(() => { - setFormData(prev => ({ - ...prev, - customer_phone: "+993 " - })); - }, []); - - const formatPhoneNumber = (phoneNumber) => { - // Remove the +993 prefix and any spaces - return phoneNumber.replace(/^\+993\s*/, "").replace(/\s+/g, ""); - }; - const handleClearAddress = () => { setSelectedAddress(null); - setFormData((prev) => ({ - ...prev, - address: "", - })); + setFormData((prev) => ({ ...prev, address: "" })); }; - const handleFocus = (event) => { - event.target.scrollIntoView({ - behavior: "smooth", - block: "center", - }); - }; + const handleFocus = (e) => + e.target.scrollIntoView({ behavior: "smooth", block: "center" }); + + const formatPhoneNumber = (phone) => + phone.replace(/^\+993\s*/, "").replace(/\s+/g, ""); const getOrderData = () => { - // Validation checks if ( !formData.customer_name || !formData.customer_phone || !formData.customer_address || !formData.payment_type_id ) { - console.error("Missing required fields"); alert("Please fill in all required fields"); return null; } - // Set default values for delivery - const currentDate = new Date().toISOString().split('T')[0]; - const defaultTimeSlot = { - date: currentDate, - hour: "12:00-14:00" // Default time slot - }; + const currentDate = new Date().toISOString().split("T")[0]; - // Prepare data in the format expected by the API return { customer_name: formData.customer_name, customer_phone: formatPhoneNumber(formData.customer_phone), customer_address: formData.customer_address, - shipping_method: "standard", // Default to standard shipping + shipping_method: "standard", payment_type_id: formData.payment_type_id, - delivery_time: defaultTimeSlot.hour, - delivery_at: defaultTimeSlot.date, + delivery_time: "12:00-14:00", + delivery_at: currentDate, region: formData.region || "", notes: formData.notes || "", - // Add shipping price and product IDs shipping_price: shippingPrice, - product_ids: productIds // Array of product IDs [1, 3, 4, etc.] + product_ids: productIds, }; }; - // 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); + await placeOrder(orderDetails).unwrap(); window.location.href = "/orders"; return true; } catch (error) { - console.error("Failed to place order:", error); - - if ( + const isHtmlResponse = 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." - ); - } + error.data.includes(""); + + alert( + isHtmlResponse + ? "There was a problem with the server. Please try again later." + : "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(handlePlaceOrder); - } + if (onPlaceOrder) onPlaceOrder(handlePlaceOrder); }, [formData, shippingPrice, productIds]); return (
-

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

+ {/*

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

*/}

{t("checkout.paymentMethod")}:

@@ -221,15 +163,15 @@ const Checkout = ({ cartItems, shippingPrice, productIds, onBackToCart, onPlaceO + />
{ + onClick={() => setFormData((prev) => ({ ...prev, payment_type_id: String(payment.id), - })); - }} + })) + } > {payment.name} @@ -256,7 +198,6 @@ const Checkout = ({ cartItems, shippingPrice, productIds, onBackToCart, onPlaceO onFocus={handleFocus} />
-
-
@@ -283,7 +223,6 @@ const Checkout = ({ cartItems, shippingPrice, productIds, onBackToCart, onPlaceO onFocus={handleFocus} />
-
  • {t( - "checkout.Delivery_is_carried_out_in_the_cities_of_Ashgabat_Buzmein_and_Anau" - )} -
  • - {/*
  • - {t( - "checkout.The_minimum_order_amount_must_be_at_least_50_manat_for_orders_over_150_manat_delivery_is_free" - )} -
  • */} -
  • - {t( - "checkout.After_you_place_an_order_on_the_website_the_operator_will_call_you_to_confirm_the_order_for_regular_customers_confirmation_is_carried_out_automatically_at_their_request" + "checkout.Delivery_is_carried_out_in_the_cities_of_Ashgabat_Buzmein_and_Anau", )}
  • {t( - "checkout.Payment_is_made_after_you_check_and_accept_the_order_The_amount_of_your_payment_is_indicated_on_the_delivery_persons_payment_document_Payment_is_made_in_cash_and_by_card_in_national_currency_Accepted_and_paid_goods_are_not_subject_to_return" + "checkout.After_you_place_an_order_on_the_website_the_operator_will_call_you_to_confirm_the_order_for_regular_customers_confirmation_is_carried_out_automatically_at_their_request", + )} +
  • +
  • + {t( + "checkout.Payment_is_made_after_you_check_and_accept_the_order_The_amount_of_your_payment_is_indicated_on_the_delivery_persons_payment_document_Payment_is_made_in_cash_and_by_card_in_national_currency_Accepted_and_paid_goods_are_not_subject_to_return", )}
  • @@ -326,4 +260,4 @@ const Checkout = ({ cartItems, shippingPrice, productIds, onBackToCart, onPlaceO ); }; -export default Checkout; \ No newline at end of file +export default Checkout; diff --git a/src/components/ProductCard/index.jsx b/src/components/ProductCard/index.jsx index c9f9cf1..5c81269 100644 --- a/src/components/ProductCard/index.jsx +++ b/src/components/ProductCard/index.jsx @@ -3,36 +3,32 @@ 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, + useGetFavoritesQuery, } 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"; +import { useCart } from "../../app/api/useCart"; -// 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; + return textContent.length > maxLength + ? textContent.substring(0, maxLength).trim() + "..." + : textContent; }; -import { useCart } from "../../app/api/useCart"; +const isPriceZero = (price) => !price || parseFloat(price) === 0; const ProductCard = ({ product, @@ -45,22 +41,20 @@ const ProductCard = ({ }) => { const { t } = useTranslation(); const navigate = useNavigate(); + const [stockErrorModalVisible, setStockErrorModalVisible] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [isHovered, setIsHovered] = 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, + favoriteProducts.some((fav) => fav.product?.id === product.id) ); const { getCartItem } = useCart(); - const [addToCart] = useAddToCartMutation(); const [updateCartItem] = useUpdateCartItemMutation(); const [removeFromCart] = useRemoveFromCartMutation(); @@ -69,26 +63,54 @@ const ProductCard = ({ const [localQuantity, setLocalQuantity] = useState(0); const [pendingQuantity, setPendingQuantity] = useState(0); - // ✅ Cart item değiştiğinde local state'i güncelle + const { name, price_amount, old_price_amount, media = [], reviews } = product; + + const truncatedDesc = truncateDescription(product.description, descriptionMaxLength); + + const calculatedDiscount = + !product.discount && + old_price_amount && + price_amount && + old_price_amount > price_amount + ? Math.round(((old_price_amount - price_amount) / old_price_amount) * 100) + : null; + useEffect(() => { - const qty = parseInt( - cartItem?.quantity || cartItem?.product_quantity || 0, - 10, - ); + 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( + favoriteProducts.some((fav) => fav.product?.id === product.id) ); - setLocalIsFavorite(isFav); } }, [favoriteProducts, product.id]); + 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 { + setLocalQuantity(serverQty); + setPendingQuantity(serverQty); + } finally { + setIsLoading(false); + } + }, 500); + + return () => clearTimeout(handler); + }, [pendingQuantity, cartItem, product.id, updateCartItem]); + + const handleCardClick = () => navigate(`/product/${product.id}`); + const handleAddToCart = async (event) => { event.preventDefault(); event.stopPropagation(); @@ -98,51 +120,17 @@ const ProductCard = ({ 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 + } catch { 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(); @@ -165,24 +153,17 @@ 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); }) - .finally(() => { - setIsLoading(false); - }); + .finally(() => setIsLoading(false)); } else { setLocalQuantity((prev) => prev - 1); setPendingQuantity((prev) => prev - 1); @@ -196,45 +177,25 @@ const ProductCard = ({ if (isLoading) return; setIsLoading(true); - - // ✅ Optimistic update - setLocalIsFavorite(!localIsFavorite); + setLocalIsFavorite((prev) => !prev); try { if (localIsFavorite) { - const result = await removeFavorite(product.id).unwrap(); - // ✅ Başarılı - RTK Query otomatik güncelleyecek + await removeFavorite(product.id).unwrap(); } else { - const result = await addFavorite(product.id).unwrap(); - // ✅ Başarılı - RTK Query otomatik güncelleyecek + await addFavorite(product.id).unwrap(); } - } catch (error) { - console.error("Failed to toggle favorite:", error); - // ✅ Hata varsa geri al - setLocalIsFavorite(localIsFavorite); + } catch { + setLocalIsFavorite((prev) => !prev); // revert } 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)} @@ -242,7 +203,7 @@ const ProductCard = ({
    {(product.discount || calculatedDiscount) && ( - -{product.discount ? product.discount : calculatedDiscount}% + -{product.discount ?? calculatedDiscount}% )} {product.stock === 0 && ( @@ -250,22 +211,29 @@ const ProductCard = ({ {t("common.out_of_stock")} )} - - +
    +

    {name}

    {truncatedDesc}

    - {price_amount} m. - {old_price_amount && ( - {old_price_amount} m. + {isPriceZero(price_amount) ? ( + Bahasyny anyklamaly + ) : ( + <> + {price_amount} m. + {old_price_amount && ( + {old_price_amount} m. + )} + )}
    +
    {showFavoriteButton && ( )} + {showAddToCart && ( <> {localQuantity > 0 ? ( @@ -337,4 +306,4 @@ const ProductCard = ({ ); }; -export default ProductCard; +export default ProductCard; \ No newline at end of file diff --git a/src/pages/Cart/CartPage.module.scss b/src/pages/Cart/CartPage.module.scss index 718e103..3b9d384 100644 --- a/src/pages/Cart/CartPage.module.scss +++ b/src/pages/Cart/CartPage.module.scss @@ -16,10 +16,9 @@ .cartHeader { display: flex; align-items: center; - margin-bottom: 12px; justify-content: space-between; background-color: #f3f4f6; - padding-bottom: 15px; + padding-top: 10px; h2 { font-size: 24px; font-weight: 700; @@ -27,6 +26,7 @@ @media screen and (max-width: 768px) { font-size: 16px; font-weight: 500; + } } } @@ -226,7 +226,7 @@ @media screen and (max-width: 1023px) { width: 100%; position: static; - margin-top: 16px; + margin-bottom: 16px; } h3 { diff --git a/src/pages/Cart/index.jsx b/src/pages/Cart/index.jsx index b2dfac7..1778aeb 100644 --- a/src/pages/Cart/index.jsx +++ b/src/pages/Cart/index.jsx @@ -2,13 +2,10 @@ 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 { ChevronDown, ChevronUp } from "lucide-react"; import { Modal } from "antd"; import { useTranslation } from "react-i18next"; import EmptyCartState from "./emptyCart"; import { - useGetCartQuery, - useAddToCartMutation, useRemoveFromCartMutation, useUpdateCartItemMutation, useCleanCartMutation, @@ -17,9 +14,9 @@ import { useCart } from "../../app/api/useCart"; import { DecreaseIcon, IncreaseIcon } from "../../components/Icons"; import Loader from "../../components/Loader/index"; -const TruncatedDescription = ({ description, maxLength = 100 }) => { - const [isExpanded, setIsExpanded] = useState(false); +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 || ""; @@ -32,11 +29,9 @@ const TruncatedDescription = ({ description, maxLength = 100 }) => {
    @@ -44,20 +39,16 @@ const TruncatedDescription = ({ description, maxLength = 100 }) => { }; const CartPage = () => { - const { cartData, cartItems, isLoading, isError, error } = useCart(); + const { cartData, cartItems, isLoading } = useCart(); + const { t } = useTranslation(); - const { t, i18n } = useTranslation(); const [checkoutStores, setCheckoutStores] = useState({}); - const [addToCart] = useAddToCartMutation(); const [removeFromCart] = useRemoveFromCartMutation(); const [updateCartItem] = useUpdateCartItemMutation(); const [cleanCart] = useCleanCartMutation(); - const [isExpanded, setIsExpanded] = useState(false); - const expandedRef = useRef(null); 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({}); @@ -70,43 +61,35 @@ const CartPage = () => { width: 400, }; - // 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 + 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: items, + items, }; }) .filter(Boolean); }, [cartData]); - // ✅ Initialize local quantities from cart items useEffect(() => { - const newLocalQuantities = {}; - const newPendingQuantities = {}; - + const newLocal = {}; + const newPending = {}; cartItems.forEach((item) => { - const productId = item.product.id; - const quantity = parseInt(item.product_quantity, 10) || 0; - newLocalQuantities[productId] = quantity; - newPendingQuantities[productId] = quantity; + const id = item.product.id; + const qty = parseInt(item.product_quantity, 10) || 0; + newLocal[id] = qty; + newPending[id] = qty; }); - - setLocalQuantities(newLocalQuantities); - setPendingQuantities(newPendingQuantities); + setLocalQuantities(newLocal); + setPendingQuantities(newPending); }, [cartItems]); - // ✅ Debounced Cart Update - Her ürün için ayrı debounce useEffect(() => { const timers = {}; @@ -114,141 +97,94 @@ const CartPage = () => { const serverItem = cartItems.find( (item) => String(item.product.id) === String(productId), ); - const serverQuantity = serverItem + const serverQty = serverItem ? parseInt(serverItem.product_quantity, 10) : 0; - const pendingQuantity = pendingQuantities[productId]; + const pendingQty = pendingQuantities[productId]; - // Değişiklik yoksa veya 0 ise (Delete modalı tetikler) bir şey yapma if ( - pendingQuantity === undefined || - pendingQuantity === serverQuantity || - pendingQuantity <= 0 - ) { + pendingQty === undefined || + pendingQty === serverQty || + pendingQty <= 0 + ) return; - } timers[productId] = setTimeout(async () => { try { setLoadingItems((prev) => ({ ...prev, [productId]: true })); - await updateCartItem({ - productId, - quantity: pendingQuantity, - }).unwrap(); - } catch (error) { - console.error("Failed to update cart:", error); - // Hata durumunda rollback - setLocalQuantities((prev) => ({ - ...prev, - [productId]: serverQuantity, - })); - setPendingQuantities((prev) => ({ - ...prev, - [productId]: serverQuantity, - })); + 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((timer) => clearTimeout(timer)); - }; + 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((item) => item.product.id === productId); - if (!item) return; + const item = cartItems.find((i) => i.product.id === productId); + if (!item || localQuantities[productId] >= item.product.stock) return; - if (localQuantities[productId] >= item.product.stock) { - return; - } - - const newQuantity = (localQuantities[productId] || 0) + 1; - setLocalQuantities((prev) => ({ - ...prev, - [productId]: newQuantity, - })); - setPendingQuantities((prev) => ({ - ...prev, - [productId]: newQuantity, - })); + 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 currentQuantity = localQuantities[productId] || 0; - - if (currentQuantity <= 1) { + const currentQty = localQuantities[productId] || 0; + if (currentQty <= 1) { showDeleteConfirm(productId); return; } - const newQuantity = currentQuantity - 1; - setLocalQuantities((prev) => ({ - ...prev, - [productId]: newQuantity, - })); - setPendingQuantities((prev) => ({ - ...prev, - [productId]: newQuantity, - })); + const newQty = currentQty - 1; + setLocalQuantities((prev) => ({ ...prev, [productId]: newQty })); + setPendingQuantities((prev) => ({ ...prev, [productId]: newQty })); }; - 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; + 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 getStoreShippingPrice = (store) => { - return store.shipping_price !== null && store.shipping_price !== undefined - ? parseFloat(store.shipping_price) - : 20; - }; - - const handleCheckout = (storeId) => { + const handleCheckout = (storeId) => setCheckoutStores((prev) => ({ ...prev, [storeId]: true })); - }; - const handleBackToCart = (storeId) => { + const handleBackToCart = (storeId) => setCheckoutStores((prev) => ({ ...prev, [storeId]: false })); - }; - const handleOrderSubmit = async (storeId, storeItems) => { + const handleOrderSubmit = async (storeId) => { if (checkoutStores[storeId] && checkoutRefs.current[storeId]) { const success = await checkoutRefs.current[storeId](); - if (success) { - setCheckoutStores((prev) => ({ ...prev, [storeId]: false })); - } + if (success) setCheckoutStores((prev) => ({ ...prev, [storeId]: false })); } else { handleCheckout(storeId); } }; - useEffect(() => { - const handleClickOutside = (event) => { - if (expandedRef.current && !expandedRef.current.contains(event.target)) { - setIsExpanded(false); - } - }; - - document.addEventListener("mousedown", handleClickOutside); - return () => document.removeEventListener("mousedown", handleClickOutside); - }, []); - const showDeleteConfirm = (productId) => { setItemToDelete(productId); setDeleteModalVisible(true); @@ -258,48 +194,41 @@ const CartPage = () => { if (itemToDelete) { try { await removeFromCart({ productId: itemToDelete }).unwrap(); - setLocalQuantities((prev) => { - const newState = { ...prev }; - delete newState[itemToDelete]; - return newState; + const s = { ...prev }; + delete s[itemToDelete]; + return s; }); setPendingQuantities((prev) => { - const newState = { ...prev }; - delete newState[itemToDelete]; - return newState; + const s = { ...prev }; + delete s[itemToDelete]; + return s; }); - } catch (error) { - console.error("Failed to remove item:", error); + } catch (e) { + console.error("Failed to remove item:", e); } } setDeleteModalVisible(false); setItemToDelete(null); }; - const showEmptyCartConfirm = () => { - setEmptyCartModalVisible(true); - }; - const handleEmptyCartConfirm = async () => { try { await cleanCart().unwrap(); - setLocalQuantities({}); setPendingQuantities({}); setCheckoutStores({}); - } catch (error) { - console.error("Failed to clean cart:", error); + } catch (e) { + console.error("Failed to clean cart:", e); } setEmptyCartModalVisible(false); }; - const getTotalItemCount = () => { - return cartItems.reduce( + const getTotalItemCount = () => + cartItems.reduce( (sum, item) => sum + parseInt(item.product_quantity, 10), 0, ); - }; return (
    @@ -339,21 +268,20 @@ const CartPage = () => {

    {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 (
    @@ -363,8 +291,8 @@ const CartPage = () => { shippingPrice={shippingPrice} productIds={store.items.map((item) => item.product.id)} onBackToCart={() => handleBackToCart(store.id)} - onPlaceOrder={(placeOrderFn) => { - checkoutRefs.current[store.id] = placeOrderFn; + onPlaceOrder={(fn) => { + checkoutRefs.current[store.id] = fn; }} /> ) : ( @@ -391,10 +319,9 @@ const CartPage = () => {
    - {( - parseFloat(item.product.price_amount) || 0 - ).toFixed(2)}{" "} - m. + {isPriceZero(item.product.price_amount) + ? "Bahasyny anyklamaly" + : `${parseFloat(item.product.price_amount).toFixed(2)} m.`}
    )} + {/* ✅ Store Summary - fiyatsız ürün varsa "Baha anyklamak" */}

    {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. -
    + {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 */} {/*
    diff --git a/src/pages/OrderDetail/OrderDetail.module.scss b/src/pages/OrderDetail/OrderDetail.module.scss index 535428d..19c1186 100644 --- a/src/pages/OrderDetail/OrderDetail.module.scss +++ b/src/pages/OrderDetail/OrderDetail.module.scss @@ -127,6 +127,7 @@ border-radius: 4px; margin-bottom: 20px; display: flex; + @media screen and (max-width: 640px) { flex-direction: column; } @@ -140,6 +141,7 @@ .row { display: flex; margin-bottom: 10px; + gap: 12px; @media screen and (max-width: 640px) { justify-content: space-between; } @@ -157,10 +159,10 @@ &:first-child { font-weight: 600; color: #000; - width: 25%; - @media screen and (max-width: 640px) { - width: 50%; - } + // width: 25%; + // @media screen and (max-width: 640px) { + // width: 50%; + // } } &:last-child { @@ -295,3 +297,106 @@ } } } + +.pendingPriceBadgeWrapper { + position: relative; + display: inline-flex; + align-items: center; +} + +.pendingPriceBadge { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + border-radius: 50%; + background: #faeeda; + border: 0.5px solid #ef9f27; + color: #854f0b; + font-size: 12px; + font-weight: 500; + cursor: pointer; + user-select: none; +} + +.pendingPriceTooltip { + position: absolute; + bottom: calc(100% + 6px); + left: 50%; + transform: translateX(-50%); + background: var(--color-background-primary, #ffffff); + border: 0.5px solid var(--color-border-secondary, #e2e2e2); + border-radius: var(--border-radius-md, 6px); + padding: 8px 12px; + width: 220px; + font-size: 13px; + color: var(--color-text-primary, #333333); + line-height: 1.5; + z-index: 100; + white-space: normal; + pointer-events: none; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + + @media (max-width: 767px) { + display: none; + } + + strong { + display: block; + margin-bottom: 4px; + color: var(--color-text-primary, #000000); + } +} + +:global { + .pending-price-modal { + .ant-modal-content { + border-radius: 12px; + padding: 24px; + @media (max-width: 767px) { + padding: 20px; + } + } + + .ant-modal-header { + margin-bottom: 12px; + .ant-modal-title { + font-size: 18px; + font-weight: 600; + color: #333; + @media (max-width: 767px) { + font-size: 16px; + } + } + } + + .ant-modal-body { + p { + font-size: 14px; + line-height: 1.6; + color: #555; + margin: 0; + @media (max-width: 767px) { + font-size: 13px; + } + } + } + + .ant-modal-footer { + margin-top: 20px; + .ant-btn-primary { + background-color: #888888; + border-color: #888888; + border-radius: 6px; + height: 36px; + padding: 0 20px; + font-weight: 500; + &:hover { + background-color: #666666; + border-color: #666666; + } + } + } + } +} diff --git a/src/pages/OrderDetail/index.jsx b/src/pages/OrderDetail/index.jsx index 252f4d8..70d1ad8 100644 --- a/src/pages/OrderDetail/index.jsx +++ b/src/pages/OrderDetail/index.jsx @@ -1,63 +1,128 @@ -import { useParams } from "react-router-dom"; +import { useParams, useNavigate } from "react-router-dom"; +import { useState } from "react"; import styles from "./OrderDetail.module.scss"; -import { Ban, CircleCheck, X } from "lucide-react"; import { useTranslation } from "react-i18next"; -import { useGetOrderByIdQuery } from "../../app/api/orderApi"; // Update with your correct path -import track from "../../assets/track.jpg"; // Keep for delivery service icon +import { useGetOrderByIdQuery } from "../../app/api/orderApi"; +import track from "../../assets/track.jpg"; import Loader from "../../components/Loader/index"; -import { Result, Button } from "antd"; -import { useNavigate } from "react-router-dom"; +import { Result, Button, Modal } from "antd"; + +const isPriceZero = (price) => !price || parseFloat(price) === 0; + +const PendingPriceModal = ({ open, onClose, t }) => ( + +

    + Bu sargytdaky bir ýa-da birnäçe harydyň bahasy entek kesgitlenmedik. + Operatorymyz siziň bilen habarlaşyp, goşmaça maglumat berer. +

    +
    +); + +const PendingPriceBadge = ({ t }) => { + const [tooltipVisible, setTooltipVisible] = useState(false); + const [modalVisible, setModalVisible] = useState(false); + const [isMobile] = useState(() => /Mobi|Android/i.test(navigator.userAgent)); + + const handleClick = (e) => { + e.preventDefault(); + e.stopPropagation(); + if (isMobile) setModalVisible(true); + }; + + const stopPropagation = (e) => { + e.stopPropagation(); + }; + + return ( + + !isMobile && setTooltipVisible(true)} + onMouseLeave={() => setTooltipVisible(false)} + onClick={handleClick} + onTouchEnd={(e) => { + e.stopPropagation(); + }} + > + ! + + {tooltipVisible && ( + + Bahasyny anyklamaly + Bu sargytdaky harydyň bahasy kesgitlenmedik. Operator size jaň edip + goşmaça maglumat berer. + + )} + + + setModalVisible(false)} + t={t} + /> + + ); +}; + const OrderDetail = () => { const { t } = useTranslation(); - const { id } = useParams(); // Get the order ID from URL params - const { data: orderData, isLoading, error } = useGetOrderByIdQuery(id); + const { id } = useParams(); const navigate = useNavigate(); - // Format date function + const { data: orderData, isLoading, error } = useGetOrderByIdQuery(id); + const formatDate = (dateString) => { try { - const date = new Date(dateString); - return date.toLocaleString("tk-TM", { + return new Date(dateString).toLocaleString("tk-TM", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", }); - } catch (e) { + } catch { return dateString; } }; - // Format delivery time for display const formatDeliveryTime = (time, date) => { try { - const deliveryDate = new Date(date); - const formattedDate = deliveryDate.toLocaleDateString("tk-TM", { + const formatted = new Date(date).toLocaleDateString("tk-TM", { day: "2-digit", month: "2-digit", year: "numeric", }); - return `${time} (${formattedDate})`; - } catch (e) { - return `${time}`; + return `${time} (${formatted})`; + } catch { + return time; } }; - // Calculate total order amount const calculateTotal = (orderItems) => { - if (!orderItems || !orderItems.length) return 0; + if (!orderItems?.length) return null; + const hasZero = orderItems.some((item) => + isPriceZero(item.unit_price_amount), + ); + if (hasZero) return null; return orderItems .reduce( (sum, item) => sum + parseFloat(item.unit_price_amount) * item.quantity, - 0 + 0, ) .toFixed(2); }; - // Handle loading state if (isLoading) return ; - // Handle error state if (error) return ( { /> ); - // Handle case where order data is not available if (!orderData) return
    Order not found
    ; - // Calculate total const totalAmount = calculateTotal(orderData.orderItems); return ( @@ -84,39 +147,10 @@ const OrderDetail = () => {

    {t("order.orderNumber")}: {orderData.id}

    -
    - {/* */} - {/* */} -
    +
    -
    - {/* Order Status */} - {/*
    -

    - - - {" "} - {t("order.Your_order_has_been_accepted")} -

    - - - -
    */} - {/* Order Details */} +
    @@ -132,7 +166,7 @@ const OrderDetail = () => { {formatDeliveryTime( orderData.delivery_time, - orderData.delivery_at + orderData.delivery_at, )}
    @@ -144,11 +178,26 @@ const OrderDetail = () => {
    {t("order.sum")}: - {totalAmount} m. + + {totalAmount === null ? ( + + + + ) : ( + `${totalAmount} m.` + )} +
    + {/* Desktop table */}
    @@ -165,9 +214,12 @@ const OrderDetail = () => { {orderData.orderItems.map((item, index) => { const product = item.product; - const itemTotal = ( - parseFloat(item.unit_price_amount) * item.quantity - ).toFixed(2); + const zeroPriceItem = isPriceZero(item.unit_price_amount); + const itemTotal = zeroPriceItem + ? null + : ( + parseFloat(item.unit_price_amount) * item.quantity + ).toFixed(2); return ( @@ -181,27 +233,50 @@ const OrderDetail = () => { - + - + ); })} - {/* Add delivery service row if shipping method exists */} + {orderData.shipping_method && ( - {" "} - {/* You may need to get actual delivery cost from API */} + @@ -210,13 +285,15 @@ const OrderDetail = () => {
    {product.name} {product.brand || "-"} {product.id || "-"}{item.unit_price_amount} m. + {zeroPriceItem ? ( + + + + ) : ( + `${item.unit_price_amount} m.` + )} + {item.quantity}{itemTotal} m. + {itemTotal === null ? ( + + + + ) : ( + `${itemTotal} m.` + )} +
    - Delivery Service + Delivery Eltip bermek hyzmaty Beýleki DELIVERY10.00 m.10.00 m. 1 10.00 m.
    - {/* Mobile View */} + + {/* Mobile cards */}
    {orderData.orderItems.map((item, index) => { const product = item.product; - const itemTotal = ( - parseFloat(item.unit_price_amount) * item.quantity - ).toFixed(2); + const zeroPriceItem = isPriceZero(item.unit_price_amount); + const itemTotal = zeroPriceItem + ? null + : (parseFloat(item.unit_price_amount) * item.quantity).toFixed(2); return (
    @@ -233,18 +310,30 @@ const OrderDetail = () => { {t("order.quantity")}: {item.quantity} - {item.unit_price_amount} m. + {zeroPriceItem ? ( + + + + ) : ( + `${item.unit_price_amount} m.` + )}
    ); })} - {/* Add delivery service card if shipping method exists */} - {orderData.shipping_method && ( + + {/* {orderData.shipping_method && (
    - Delivery Service + Delivery

    Beýleki

    @@ -257,7 +346,7 @@ const OrderDetail = () => {
    - )} + )} */}
    ); diff --git a/src/pages/Orders/Orders.module.scss b/src/pages/Orders/Orders.module.scss index eb17a11..ec399d2 100644 --- a/src/pages/Orders/Orders.module.scss +++ b/src/pages/Orders/Orders.module.scss @@ -1,10 +1,10 @@ // SargytlarymComponent.module.scss .container { - padding: 15px 24px 0 24px; + padding: 15px 24px 24px 24px; max-width: 1366px; margin: 0 auto; box-sizing: border-box; - a{ + a { text-decoration: none; } @media (max-width: 767px) { @@ -121,3 +121,106 @@ font-weight: 700; color: #888888; } + +.pendingPriceBadgeWrapper { + position: relative; + display: inline-flex; + align-items: center; +} + +.pendingPriceBadge { + display: inline-flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + border-radius: 50%; + background: #faeeda; + border: 0.5px solid #ef9f27; + color: #854f0b; + font-size: 12px; + font-weight: 500; + cursor: pointer; + user-select: none; +} + +.pendingPriceTooltip { + position: absolute; + bottom: calc(100% + 6px); + left: 50%; + transform: translateX(-50%); + background: var(--color-background-primary, #ffffff); + border: 0.5px solid var(--color-border-secondary, #e2e2e2); + border-radius: var(--border-radius-md, 6px); + padding: 8px 12px; + width: 220px; + font-size: 13px; + color: var(--color-text-primary, #333333); + line-height: 1.5; + z-index: 100; + white-space: normal; + pointer-events: none; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + + @media (max-width: 767px) { + display: none; + } + + strong { + display: block; + margin-bottom: 4px; + color: var(--color-text-primary, #000000); + } +} + +:global { + .pending-price-modal { + .ant-modal-content { + border-radius: 12px; + padding: 24px; + @media (max-width: 767px) { + padding: 20px; + } + } + + .ant-modal-header { + margin-bottom: 12px; + .ant-modal-title { + font-size: 18px; + font-weight: 600; + color: #333; + @media (max-width: 767px) { + font-size: 16px; + } + } + } + + .ant-modal-body { + p { + font-size: 14px; + line-height: 1.6; + color: #555; + margin: 0; + @media (max-width: 767px) { + font-size: 13px; + } + } + } + + .ant-modal-footer { + margin-top: 20px; + .ant-btn-primary { + background-color: #888888; + border-color: #888888; + border-radius: 6px; + height: 36px; + padding: 0 20px; + font-weight: 500; + &:hover { + background-color: #666666; + border-color: #666666; + } + } + } + } +} diff --git a/src/pages/Orders/index.jsx b/src/pages/Orders/index.jsx index 8c9c8b8..4fa697e 100644 --- a/src/pages/Orders/index.jsx +++ b/src/pages/Orders/index.jsx @@ -1,36 +1,104 @@ -// Orders.jsx -import React from "react"; +import React, { useState } from "react"; import styles from "./Orders.module.scss"; import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { useGetOrdersQuery } from "../../app/api/orderApi"; // Update with your correct path -import EmptyOrderState from "./emptyOrder"; // Import the EmptyOrderState component +import { useGetOrdersQuery } from "../../app/api/orderApi"; +import EmptyOrderState from "./emptyOrder"; import Loader from "../../components/Loader/index"; -import { Result, Button } from "antd"; +import { Result, Button, Modal } from "antd"; import { useNavigate } from "react-router-dom"; + +const isPriceZero = (price) => !price || parseFloat(price) === 0; + +const orderHasZeroPrice = (orderItems) => + orderItems?.some((item) => isPriceZero(item.unit_price_amount)); + +const PendingPriceModal = ({ open, onClose, t }) => ( + +

    + Bu sargytdaky bir ýa-da birnäçe harydyň bahasy entek kesgitlenmedik. + Operatorymyz siziň bilen habarlaşyp, goşmaça maglumat berer. +

    +
    +); + +const PendingPriceBadge = ({ t }) => { + const [tooltipVisible, setTooltipVisible] = useState(false); + const [modalVisible, setModalVisible] = useState(false); + const [isMobile] = useState(() => /Mobi|Android/i.test(navigator.userAgent)); + + const handleClick = (e) => { + e.preventDefault(); + e.stopPropagation(); + if (isMobile) setModalVisible(true); + }; + + const stopPropagation = (e) => { + e.stopPropagation(); + }; + + return ( + + !isMobile && setTooltipVisible(true)} + onMouseLeave={() => setTooltipVisible(false)} + onClick={handleClick} + onTouchEnd={(e) => { + e.stopPropagation(); + }} + > + ! + + {tooltipVisible && ( + + Bahasyny anyklamaly + Bu sargytdaky harydyň bahasy kesgitlenmedik. Operator size jaň edip + goşmaça maglumat berer. + + )} + + + setModalVisible(false)} + t={t} + /> + + ); +}; + const Orders = () => { const { t } = useTranslation(); const { data: orders, isLoading, error } = useGetOrdersQuery(); const navigate = useNavigate(); - // Function to format date - implement this or use a library like date-fns + const formatOrderDate = (dateString) => { try { - const date = new Date(dateString); - return date.toLocaleString("tk-TM", { + return new Date(dateString).toLocaleString("tk-TM", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", }); - } catch (e) { + } catch { return dateString; } }; if (isLoading) return ; - // Handle error state if (error) return ( { /> ); - // Handle empty orders - render EmptyOrderState component - if (!orders || orders.length === 0) { - return ; - } + if (!orders || orders.length === 0) return ; return (

    Sargytlarym

    - {/* Desktop table view */} + {/* Desktop table */}
    @@ -69,11 +134,11 @@ const Orders = () => { {orders.map((order) => { - // Calculate total order amount + const hasZeroPrice = orderHasZeroPrice(order.orderItems); const totalAmount = order.orderItems.reduce( (sum, item) => sum + parseFloat(item.unit_price_amount) * item.quantity, - 0 + 0, ); return ( @@ -81,7 +146,19 @@ const Orders = () => { @@ -99,50 +176,72 @@ const Orders = () => {
    {order.id} {formatOrderDate(order.delivery_at)} - {totalAmount.toFixed(2)} m. + {hasZeroPrice ? ( + + Bahasyny anyklamaly + + ) : ( + `${totalAmount.toFixed(2)} m.` + )} {order.payment_type} {order.status}
    - {/* Mobile card view */} + {/* Mobile cards */}
    {orders.map((order) => { + const hasZeroPrice = orderHasZeroPrice(order.orderItems); const totalAmount = order.orderItems.reduce( (sum, item) => sum + parseFloat(item.unit_price_amount) * item.quantity, - 0 + 0, ); return ( - -
    -
    - - {t("order.orderNumber")}: - - {order.id} -
    -
    - {t("order.orderDate")}: - - {formatOrderDate(order.delivery_at)} - -
    -
    - {t("order.sum")}: - - {totalAmount.toFixed(2)} m. - -
    -
    - - {t("checkout.paymentMethod")}: - - {order.payment_type} -
    -
    - - {t("order.orderStatus")}: - - {order.status} -
    +
    { + // Modal veya badge içerisine tıklandığında yönlendirmeyi engelle + if ( + e.target.closest(`.${styles.pendingPriceBadgeWrapper}`) || + e.target.closest(".ant-modal-root") || + e.target.closest(".ant-modal-wrap") + ) { + return; + } + navigate(`/orderdetail/${order.id}`); + }} + style={{ cursor: "pointer" }} + > +
    + {t("order.orderNumber")}: + {order.id}
    - +
    + {t("order.orderDate")}: + + {formatOrderDate(order.delivery_at)} + +
    +
    + {t("order.sum")}: + + {hasZeroPrice ? ( + + Bahasyny anyklamaly + + ) : ( + `${totalAmount.toFixed(2)} m.` + )} + +
    +
    + + {t("checkout.paymentMethod")}: + + {order.payment_type} +
    +
    + {t("order.orderStatus")}: + {order.status} +
    +
    ); })}