add cart API's (add , delete , get)
This commit is contained in:
@@ -11,6 +11,7 @@ import { registerUser } from '@/public/src/services/auth/api';
|
||||
import { loginUser } from '@/public/src/services/auth/api';
|
||||
import { logoutUser } from '@/public/src/services/auth/api';
|
||||
import { useCategories } from './context/categoryprovider';
|
||||
import { getCartApi } from '@/public/src/services/cart/api';
|
||||
|
||||
const topBarLinks = [
|
||||
{ label: "بخش صنعتی", href: "/" },
|
||||
@@ -129,7 +130,48 @@ export function Header() {
|
||||
const [showLoginPassword, setShowLoginPassword] = useState(false);
|
||||
const [loginMobile, setLoginMobile] = useState("");
|
||||
const [showRegisterSuccessDialog, setShowRegisterSuccessDialog] = useState(false);
|
||||
const [isOptimistic, setIsOptimistic] = useState(false);
|
||||
// ۱. افکت اول: وقتی کانتکست لوکال (cart) با کلیک کاربر آپدیت میشود،
|
||||
// منو را به حالت Optimistic میبریم تا تغییرات را درجا نشان دهد.
|
||||
useEffect(() => {
|
||||
setIsOptimistic(true);
|
||||
// بعد از ۱.۵ ثانیه (زمانی که قاعدتاً API سرور کارش تمام شده) به حالت عادی برمیگردد
|
||||
const timer = setTimeout(() => setIsOptimistic(false), 1500);
|
||||
return () => clearTimeout(timer);
|
||||
}, [cart]);
|
||||
|
||||
// ۲. افکت دوم: دریافت اطلاعات سرور در پسزمینه بدون رفرش صفحه
|
||||
useEffect(() => {
|
||||
const fetchServerData = async () => {
|
||||
const token = localStorage.getItem('accessToken');
|
||||
if (token) {
|
||||
setIsLoggedIn(true);
|
||||
try {
|
||||
const data = await getCartApi();
|
||||
if (data) {
|
||||
setServerCartItems(data.items || []);
|
||||
setServerSummary(data.summary || null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("خطا در همگامسازی سبد خرید سرور:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// گوش دادن به سیگنالی که از ProductCard ارسال میشود
|
||||
window.addEventListener('cartUpdated', fetchServerData);
|
||||
|
||||
// فراخوانی در لود اولیه
|
||||
fetchServerData();
|
||||
|
||||
return () => window.removeEventListener('cartUpdated', fetchServerData);
|
||||
}, []);
|
||||
|
||||
// محاسبه قیمت کل لوکال (برای زمانی که کاربر لاگین نیست یا در حالت Optimistic هستیم)
|
||||
const localTotalPrice = cart.reduce((total, item) => {
|
||||
const price = item.price ? Number(item.price.toString().replace(/,/g, '')) : 0;
|
||||
return total + (price * item.quantity);
|
||||
}, 0);
|
||||
|
||||
|
||||
const [loginForm, setLoginForm] = useState({
|
||||
@@ -212,9 +254,9 @@ export function Header() {
|
||||
localStorage.setItem("username", username);
|
||||
localStorage.setItem("fullName", displayName);
|
||||
localStorage.setItem("role", role.toLowerCase());
|
||||
console.log(res);
|
||||
console.log(res);
|
||||
|
||||
|
||||
|
||||
setUser({ username, displayName });
|
||||
setIsOpen(false);
|
||||
setShowRegisterSuccessDialog(true);
|
||||
@@ -267,7 +309,7 @@ export function Header() {
|
||||
localStorage.setItem("refreshToken", res.data.refreshToken);
|
||||
localStorage.setItem("username", username);
|
||||
localStorage.setItem("fullName", displayName);
|
||||
console.log(res);
|
||||
console.log(res);
|
||||
setUser({ username, displayName });
|
||||
setIsOpen(false);
|
||||
router.push("/dashboard?success=login");
|
||||
@@ -373,6 +415,133 @@ export function Header() {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||
const [serverCartItems, setServerCartItems] = useState<any[]>([]);
|
||||
const [serverSummary, setServerSummary] = useState<any>(null);
|
||||
|
||||
// // درون کامپوننت هدر شما
|
||||
// useEffect(() => {
|
||||
// const fetchServerData = async () => {
|
||||
// const token = localStorage.getItem('accessToken');
|
||||
// if (token) {
|
||||
// setIsLoggedIn(true);
|
||||
// try {
|
||||
// const data = await getCartApi();
|
||||
// if (data) {
|
||||
// setServerCartItems(data.items || []);
|
||||
// setServerSummary(data.summary || null);
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error("خطا در همگامسازی سبد خرید سرور:", error);
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
// // --- این تابع جدید را اضافه کنید ---
|
||||
// const handleCartClear = () => {
|
||||
// setServerCartItems([]); // درجا لیست منو را خالی میکند
|
||||
// setServerSummary(null);
|
||||
// };
|
||||
|
||||
// // گوش دادن به رویدادها
|
||||
// window.addEventListener('cartUpdated', fetchServerData);
|
||||
// window.addEventListener('cartCleared', handleCartClear); // لیسنر جدید
|
||||
|
||||
// fetchServerData();
|
||||
|
||||
// return () => {
|
||||
// window.removeEventListener('cartUpdated', fetchServerData);
|
||||
// window.removeEventListener('cartCleared', handleCartClear); // کلینآپ لیسنر جدید
|
||||
// };
|
||||
// }, []);
|
||||
|
||||
|
||||
|
||||
|
||||
// دریافت اطلاعات سبد خرید از سرور
|
||||
useEffect(() => {
|
||||
const fetchServerCart = async () => {
|
||||
const token = localStorage.getItem('accessToken');
|
||||
if (token) {
|
||||
setIsLoggedIn(true);
|
||||
try {
|
||||
const data = await getCartApi();
|
||||
if (data) {
|
||||
setServerCartItems(data.items || []);
|
||||
setServerSummary(data.summary || null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("خطا در دریافت سبد خرید منو:", error);
|
||||
}
|
||||
} else {
|
||||
setIsLoggedIn(false);
|
||||
}
|
||||
};
|
||||
// --- این تابع جدید را اضافه کنید ---
|
||||
const handleCartClear = () => {
|
||||
setServerCartItems([]); // درجا لیست منو را خالی میکند
|
||||
setServerSummary(null);
|
||||
};
|
||||
// گوش دادن به رویدادها
|
||||
window.addEventListener('cartUpdated', fetchServerCart);
|
||||
window.addEventListener('cartCleared', handleCartClear); // لیسنر جدید
|
||||
|
||||
fetchServerCart();
|
||||
return () => {
|
||||
window.removeEventListener('cartUpdated', fetchServerCart);
|
||||
window.removeEventListener('cartCleared', handleCartClear); // کلینآپ لیسنر جدید
|
||||
};
|
||||
// در صورت نیاز به آپدیت شدن دراپداون با هر تغییر، میتوانید این تابع را به یک Event یا Context متصل کنید
|
||||
}, []);
|
||||
|
||||
// --- متغیرهای هوشمند برای جایگزینی در UI ---
|
||||
// const displayCart = isLoggedIn
|
||||
// ? serverCartItems.map(item => ({
|
||||
// id: item.product?.id || item.productId,
|
||||
// title: item.product?.title || "بدون نام",
|
||||
// brand: item.product?.brand || "متفرقه",
|
||||
// price: item.unitPrice || item.product?.price || 0,
|
||||
// quantity: item.quantity || 1,
|
||||
// image: item.product?.mainImageUrl || item.product?.image || "/placeholder.png"
|
||||
// }))
|
||||
// : cart; // cart از useCart() میآید
|
||||
|
||||
// const displayTotalQuantity = isLoggedIn && serverSummary
|
||||
// ? serverSummary.totalQuantity || serverSummary.itemsCount || 0
|
||||
// : cart.reduce((total, item) => total + item.quantity, 0);
|
||||
|
||||
// const displayTotalPrice = isLoggedIn && serverSummary
|
||||
// ? serverSummary.totalPrice || serverSummary.total || 0
|
||||
// : totalPrice;
|
||||
|
||||
|
||||
|
||||
|
||||
// --- متغیرهای هوشمند اصلاح شده ---
|
||||
// اگر لاگین باشیم و در لحظهی کلیک (Optimistic) نباشیم، دیتای سرور را نشان میدهد
|
||||
// در غیر این صورت (برای نمایش آنی) دیتای لوکال را نشان میدهد.
|
||||
const displayCart = (isLoggedIn && !isOptimistic)
|
||||
? serverCartItems.map(item => ({
|
||||
id: item.product?.id || item.productId,
|
||||
title: item.product?.title || "بدون نام",
|
||||
brand: item.product?.brand || "متفرقه",
|
||||
price: item.unitPrice || item.product?.price || 0,
|
||||
quantity: item.quantity || 1,
|
||||
image: item.product?.mainImageUrl || item.product?.image || "/placeholder.png"
|
||||
}))
|
||||
: cart;
|
||||
|
||||
const displayTotalQuantity = (isLoggedIn && !isOptimistic) && serverSummary
|
||||
? serverSummary.totalQuantity || serverSummary.itemsCount || 0
|
||||
: cart.reduce((total, item) => total + item.quantity, 0);
|
||||
|
||||
const displayTotalPrice = (isLoggedIn && !isOptimistic) && serverSummary
|
||||
? serverSummary.totalPrice || serverSummary.total || 0
|
||||
: localTotalPrice;
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<header>
|
||||
<div>
|
||||
@@ -573,9 +742,9 @@ export function Header() {
|
||||
|
||||
<Link href="/cart" className="flex items-center justify-center p-2.5 bg-white border border-gray-300/60 rounded-xl text-gray-700 hover:bg-gray-50 transition relative z-10">
|
||||
<ShoppingCart className="w-4 h-4 text-gray-500" strokeWidth={1.8} />
|
||||
{cart.length > 0 && (
|
||||
{displayCart.length > 0 && (
|
||||
<span className="absolute -top-1.5 -right-1.5 bg-[#f92a35] text-white text-[10px] font-bold w-4 h-4 flex items-center justify-center rounded-full shadow-sm">
|
||||
{cart.reduce((total, item) => total + item.quantity, 0)}
|
||||
{displayTotalQuantity}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
@@ -583,7 +752,7 @@ export function Header() {
|
||||
<div className="absolute top-full left-0 w-full h-3 bg-transparent hidden group-hover:block"></div>
|
||||
|
||||
<div className="absolute top-[calc(100%+12px)] left-0 w-80 bg-white border border-gray-200 rounded-2xl shadow-xl hidden group-hover:flex flex-col overflow-hidden z-50">
|
||||
{cart.length === 0 ? (
|
||||
{displayCart.length === 0 ? (
|
||||
<div className="p-6 text-center text-sm text-gray-500 flex flex-col items-center gap-2">
|
||||
<ShoppingCart className="w-8 h-8 text-gray-200" />
|
||||
<span>سبد خرید شما خالی است</span>
|
||||
@@ -591,11 +760,11 @@ export function Header() {
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-between items-center p-4 border-b border-gray-100 bg-gray-50/50">
|
||||
<span className="text-xs font-semibold text-gray-500">{cart.length} کالا</span>
|
||||
<span className="text-xs font-semibold text-gray-500">{displayCart.length} کالا</span>
|
||||
<Link href="/cart" className="text-xs text-blue-500 hover:text-blue-700 font-medium transition">مشاهده سبد خرید</Link>
|
||||
</div>
|
||||
<div className="max-h-64 overflow-y-auto p-2 flex flex-col gap-1 custom-scrollbar">
|
||||
{cart.slice(0, 3).map((item) => {
|
||||
{displayCart.slice(0, 3).map((item) => {
|
||||
const itemTotal = item.price ? (Number(item.price.toString().replace(/,/g, '')) * item.quantity).toLocaleString('fa-IR') : null;
|
||||
return (
|
||||
<div key={item.id} className="flex items-center gap-3 p-2 hover:bg-gray-50 rounded-xl transition group/item relative">
|
||||
@@ -619,12 +788,12 @@ export function Header() {
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{cart.length > 3 && (<p className="text-center text-[10px] text-gray-400 py-2 border-t border-gray-50 mt-1"> و {cart.length - 3} کالای دیگر...</p>)}
|
||||
{displayCart.length > 3 && (<p className="text-center text-[10px] text-gray-400 py-2 border-t border-gray-50 mt-1"> و {displayCart.length - 3} کالای دیگر...</p>)}
|
||||
</div>
|
||||
<div className="p-4 bg-white border-t border-gray-100">
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<span className="text-xs text-gray-500">مبلغ قابل پرداخت:</span>
|
||||
<span className="text-sm font-bold text-gray-800">{totalPrice > 0 ? `${totalPrice.toLocaleString('fa-IR')} تومان` : 'استعلام'}</span>
|
||||
<span className="text-sm font-bold text-gray-800">{displayTotalPrice > 0 ? `${displayTotalPrice.toLocaleString('fa-IR')} تومان` : 'استعلام'}</span>
|
||||
</div>
|
||||
<Link href="/cart" className="flex items-center justify-center w-full py-2 bg-[#ffb900] hover:bg-[#e6a600] text-black text-xs font-semibold rounded-xl transition-colors">ثبت سفارش</Link>
|
||||
</div>
|
||||
@@ -633,6 +802,7 @@ export function Header() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{!user ? (
|
||||
<button
|
||||
onClick={() => setIsOpen(true)}
|
||||
|
||||
Reference in New Issue
Block a user