fix problem update price in cart

This commit is contained in:
hamid zarghami
2025-12-25 10:46:04 +03:30
parent 91b5a61fbd
commit 0f2f0d5c29
3 changed files with 109 additions and 81 deletions
@@ -1,13 +1,14 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import * as api from "../service/CartService"; import * as api from "../service/CartService";
import type { CartResponse, CartItem } from "../types/Types"; import type { CartResponse, CartItem } from "../types/Types";
import type { FoodsResponse } from "@/app/[name]/(Main)/types/Types";
export const useBulkCart = () => { export const useBulkCart = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: api.bulkCart, mutationFn: api.bulkCart,
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["cart-items"] }); queryClient.refetchQueries({ queryKey: ["cart-items"] });
}, },
}); });
}; };
@@ -18,56 +19,74 @@ export const useIncrementCart = () => {
mutationFn: api.increment, mutationFn: api.increment,
onMutate: async (id: string | number) => { onMutate: async (id: string | number) => {
await queryClient.cancelQueries({ queryKey: ["cart-items"] }); await queryClient.cancelQueries({ queryKey: ["cart-items"] });
await queryClient.cancelQueries({ queryKey: ["menu"] });
const previousCart = queryClient.getQueryData<CartResponse>([ const previousCart = queryClient.getQueryData<CartResponse>([
"cart-items", "cart-items",
]); ]);
const foodsData = queryClient.getQueryData<FoodsResponse>(["menu"]);
if (previousCart?.data) { if (previousCart?.data && foodsData?.data) {
const existingItem = previousCart.data.items.find( const existingItem = previousCart.data.items.find(
(item) => item.foodId === String(id) (item) => item.foodId === String(id)
); );
let updatedItems: CartItem[];
let newTotalItems: number;
if (existingItem) { if (existingItem) {
// به‌روزرسانی خوش‌بینانه برای آیتم موجود updatedItems = previousCart.data.items.map((item) => {
const updatedItems = previousCart.data.items.map((item) => {
if (item.foodId === String(id)) { if (item.foodId === String(id)) {
return { return {
...item, ...item,
quantity: item.quantity + 1, quantity: item.quantity + 1,
totalPrice: item.price * (item.quantity + 1) * (1 - item.discount / 100),
}; };
} }
return item; return item;
}); });
newTotalItems = previousCart.data.totalItems + 1;
queryClient.setQueryData<CartResponse>(["cart-items"], {
...previousCart,
data: {
...previousCart.data,
items: updatedItems,
totalItems: previousCart.data.totalItems + 1,
},
});
} else { } else {
// افزودن خوش‌بینانه برای آیتم جدید const food = foodsData.data.find((f) => String(f.id) === String(id));
const newItem: CartItem = { if (food) {
foodId: String(id), const newItem: CartItem = {
foodTitle: "", // این بعداً از سرور پر می‌شود foodId: String(id),
quantity: 1, foodTitle: food.title || food.name || "",
price: 0, quantity: 1,
discount: 0, price: food.price,
totalPrice: 0, discount: food.discount || 0,
}; totalPrice: food.price * (1 - (food.discount || 0) / 100),
};
queryClient.setQueryData<CartResponse>(["cart-items"], { updatedItems = [...previousCart.data.items, newItem];
...previousCart, newTotalItems = previousCart.data.totalItems + 1;
data: { } else {
...previousCart.data, return { previousCart };
items: [...previousCart.data.items, newItem], }
totalItems: previousCart.data.totalItems + 1,
},
});
} }
// محاسبه subTotal جدید
const newSubTotal = updatedItems.reduce(
(sum, item) => sum + item.totalPrice,
0
);
// محاسبه total جدید (با حفظ deliveryFee، tax و discount)
const newTotal =
newSubTotal +
(previousCart.data.deliveryFee || 0) -
(previousCart.data.totalDiscount || 0) +
(previousCart.data.tax || 0);
queryClient.setQueryData<CartResponse>(["cart-items"], {
...previousCart,
data: {
...previousCart.data,
items: updatedItems,
totalItems: newTotalItems,
subTotal: newSubTotal,
total: newTotal,
},
});
} }
return { previousCart }; return { previousCart };
@@ -77,6 +96,9 @@ export const useIncrementCart = () => {
queryClient.setQueryData(["cart-items"], context.previousCart); queryClient.setQueryData(["cart-items"], context.previousCart);
} }
}, },
onSuccess: () => {
queryClient.refetchQueries({ queryKey: ["cart-items"] });
},
}); });
}; };
@@ -102,18 +124,34 @@ export const useDecrementCart = () => {
return { return {
...item, ...item,
quantity: newQuantity, quantity: newQuantity,
totalPrice: item.price * newQuantity * (1 - item.discount / 100),
}; };
} }
return item; return item;
}) })
.filter((item: CartItem | null): item is CartItem => item !== null); .filter((item: CartItem | null): item is CartItem => item !== null);
// محاسبه subTotal جدید
const newSubTotal = updatedItems.reduce(
(sum, item) => sum + item.totalPrice,
0
);
// محاسبه total جدید
const newTotal =
newSubTotal +
(previousCart.data.deliveryFee || 0) -
(previousCart.data.totalDiscount || 0) +
(previousCart.data.tax || 0);
queryClient.setQueryData<CartResponse>(["cart-items"], { queryClient.setQueryData<CartResponse>(["cart-items"], {
...previousCart, ...previousCart,
data: { data: {
...previousCart.data, ...previousCart.data,
items: updatedItems, items: updatedItems,
totalItems: Math.max(0, previousCart.data.totalItems - 1), totalItems: Math.max(0, previousCart.data.totalItems - 1),
subTotal: newSubTotal,
total: newTotal,
}, },
}); });
} }
@@ -125,6 +163,9 @@ export const useDecrementCart = () => {
queryClient.setQueryData(["cart-items"], context.previousCart); queryClient.setQueryData(["cart-items"], context.previousCart);
} }
}, },
onSuccess: () => {
queryClient.refetchQueries({ queryKey: ["cart-items"] });
},
}); });
}; };
@@ -132,32 +173,8 @@ export const useClearCart = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: api.clear, mutationFn: api.clear,
onMutate: async () => { onSuccess: () => {
await queryClient.cancelQueries({ queryKey: ["cart-items"] }); queryClient.refetchQueries({ queryKey: ["cart-items"] });
const previousCart = queryClient.getQueryData<CartResponse>([
"cart-items",
]);
if (previousCart?.data) {
queryClient.setQueryData<CartResponse>(["cart-items"], {
...previousCart,
data: {
...previousCart.data,
items: [],
totalItems: 0,
subTotal: 0,
total: 0,
},
});
}
return { previousCart };
},
onError: (_err, _variables, context) => {
if (context?.previousCart) {
queryClient.setQueryData(["cart-items"], context.previousCart);
}
}, },
}); });
}; };
@@ -29,28 +29,28 @@ const getDeliveryMethodTitle = (method: string) => {
} }
}; };
const getStatusPercentage = (status: string) => { // const getStatusPercentage = (status: string) => {
switch (status) { // switch (status) {
case 'new': // case 'new':
case 'pendingPayment': // case 'pendingPayment':
return 0; // return 0;
case 'paid': // case 'paid':
case 'confirmed': // case 'confirmed':
return 20; // return 20;
case 'preparing': // case 'preparing':
return 40; // return 40;
case 'ready': // case 'ready':
return 60; // return 60;
case 'shipped': // case 'shipped':
case 'delivering': // case 'delivering':
return 80; // return 80;
case 'completed': // case 'completed':
case 'delivered': // case 'delivered':
return 100; // return 100;
default: // default:
return 0; // return 0;
} // }
}; // };
const getStatusText = (status: string): string => { const getStatusText = (status: string): string => {
const statusKey = status as keyof typeof ordersTranslations.status; const statusKey = status as keyof typeof ordersTranslations.status;
@@ -1,8 +1,9 @@
'use client'; 'use client';
import { useSearchParams } from 'next/navigation'; import { useSearchParams, useRouter } from 'next/navigation';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { ArrowLeft } from 'iconsax-react';
import useToggle from '@/hooks/helpers/useToggle'; import useToggle from '@/hooks/helpers/useToggle';
import { useAuthStore } from '@/zustand/authStore'; import { useAuthStore } from '@/zustand/authStore';
import { MarkerData, NominatimReverseGeocodingResponse } from '../types/Types'; import { MarkerData, NominatimReverseGeocodingResponse } from '../types/Types';
@@ -20,6 +21,7 @@ const CustomMap = dynamic(
function OrderTrackingPage() { function OrderTrackingPage() {
const router = useRouter();
const params = useSearchParams(); const params = useSearchParams();
const id = params.get('id'); const id = params.get('id');
const editMode = !!id; const editMode = !!id;
@@ -186,8 +188,17 @@ function OrderTrackingPage() {
/> />
</div> </div>
<div className="absolute top-4 right-4 z-1000">
<button
onClick={() => router.back()}
className="p-2 rounded-full bg-container/90 backdrop-blur-sm shadow-sm"
>
<ArrowLeft size={24} className="stroke-foreground" />
</button>
</div>
{hasServiceArea && ( {hasServiceArea && (
<div className="absolute top-4 left-4 right-4 z-1000 max-w-sm"> <div className="absolute top-4 left-4 z-1000 max-w-sm">
<div className="bg-blue-50/90 border border-blue-200 rounded-lg p-3 shadow-sm"> <div className="bg-blue-50/90 border border-blue-200 rounded-lg p-3 shadow-sm">
<p className="text-xs text-blue-800"> <p className="text-xs text-blue-800">
محدوده سرویسدهی روی نقشه با رنگ آبی مشخص شده است محدوده سرویسدهی روی نقشه با رنگ آبی مشخص شده است