688 lines
45 KiB
TypeScript
688 lines
45 KiB
TypeScript
'use client';
|
||
|
||
import { useCart } from "@/components/context/cartcontext";
|
||
import Link from "next/link";
|
||
import { useState, useEffect } from "react";
|
||
import {
|
||
ShoppingBag,
|
||
ChevronLeft,
|
||
ChevronRight,
|
||
Truck,
|
||
CreditCard,
|
||
MapPin,
|
||
User,
|
||
Lock
|
||
} from "lucide-react";
|
||
import PaymentMethodsSection from "@/components/PaymentMethods";
|
||
import { getCartApi } from "@/public/src/services/cart/api";
|
||
import { fetchUserAddresses, type Address, addAddressApi, NewAddressData, updateAddressApi, deleteAddressApi } from "@/public/src/services/address/api";
|
||
|
||
|
||
|
||
export default function CheckoutPage() {
|
||
const { cart } = useCart();
|
||
const [shippingMethod, setShippingMethod] = useState('post');
|
||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [serverSummary, setServerSummary] = useState<any>(null);
|
||
const [addresses, setAddresses] = useState<Address[]>([]);
|
||
const [selectedAddressId, setSelectedAddressId] = useState<string | null>(null);
|
||
const [showNewAddressForm, setShowNewAddressForm] = useState(false);
|
||
const [isAddressLoading, setIsAddressLoading] = useState(true);
|
||
const [newAddress, setNewAddress] = useState<NewAddressData>({
|
||
title: 'خانه',
|
||
recipientName: '',
|
||
phone: '',
|
||
province: '',
|
||
city: '',
|
||
postalCode: '',
|
||
addressLine: '',
|
||
plaque: '',
|
||
unit: '',
|
||
isDefault: false
|
||
});
|
||
|
||
|
||
useEffect(() => {
|
||
const initializeCheckout = async () => {
|
||
// توجه: در صفحه Cart از refreshToken استفاده کرده بودید، اگر اینجا accessToken است دقت کنید که یکسان باشند
|
||
const token = localStorage.getItem('accessToken') || localStorage.getItem('refreshToken');
|
||
|
||
if (token) {
|
||
setIsAuthenticated(true);
|
||
// ۳. دریافت اطلاعات سبد خرید از سرور
|
||
try {
|
||
const data = await getCartApi();
|
||
if (data && data.summary) {
|
||
setServerSummary(data.summary);
|
||
}
|
||
} catch (error) {
|
||
console.error("خطا در دریافت اطلاعات سبد خرید:", error);
|
||
}
|
||
}
|
||
setIsLoading(false);
|
||
};
|
||
|
||
initializeCheckout();
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
const initializeCheckout = async () => {
|
||
const token = localStorage.getItem('accessToken') || localStorage.getItem('refreshToken');
|
||
|
||
if (token) {
|
||
setIsAuthenticated(true);
|
||
try {
|
||
// دریافت همزمان اطلاعات سبد و آدرسها
|
||
const [cartData, addressData] = await Promise.all([
|
||
getCartApi(),
|
||
fetchUserAddresses()
|
||
]);
|
||
|
||
if (cartData && cartData.summary) {
|
||
setServerSummary(cartData.summary);
|
||
}
|
||
|
||
if (addressData && addressData.length > 0) {
|
||
setAddresses(addressData);
|
||
// انتخاب آدرس پیشفرض یا اولین آدرس به عنوان انتخابشده
|
||
const defaultAddress = addressData.find(addr => addr.isDefault) || addressData[0];
|
||
setSelectedAddressId(defaultAddress.id);
|
||
} else {
|
||
// اگر آدرسی وجود نداشت، فرم افزودن آدرس را نمایش بده
|
||
setShowNewAddressForm(true);
|
||
}
|
||
} catch (error) {
|
||
console.error("خطا در دریافت اطلاعات صفحه پرداخت:", error);
|
||
} finally {
|
||
setIsAddressLoading(false);
|
||
}
|
||
} else {
|
||
setIsAuthenticated(false);
|
||
setIsAddressLoading(false);
|
||
}
|
||
setIsLoading(false);
|
||
};
|
||
|
||
initializeCheckout();
|
||
}, []);
|
||
|
||
|
||
const handleAddressInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
||
const { name, value } = e.target;
|
||
setNewAddress(prev => ({ ...prev, [name]: value }));
|
||
};
|
||
|
||
const handleAddNewAddress = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
// اینجا میتوانید ولیدیشنهای لازم را اضافه کنید
|
||
try {
|
||
const addedAddress = await addAddressApi(newAddress);
|
||
// آدرس جدید را به لیست اضافه کن و آن را به عنوان انتخاب شده قرار بده
|
||
setAddresses(prev => [...prev, addedAddress]);
|
||
setSelectedAddressId(addedAddress.id);
|
||
setShowNewAddressForm(false); // فرم را مخفی کن
|
||
// فرم را ریست کن
|
||
setNewAddress({
|
||
title: 'خانه', recipientName: '', phone: '', province: '', city: '',
|
||
postalCode: '', addressLine: '', plaque: '', unit: '', isDefault: false
|
||
});
|
||
} catch (error) {
|
||
console.error("خطا در ذخیره آدرس:", error);
|
||
// اینجا میتوانید به کاربر خطا را نمایش دهید
|
||
}
|
||
};
|
||
|
||
const [editingAddressId, setEditingAddressId] = useState(null);
|
||
|
||
// 2. تابع کلیک روی دکمه ویرایش
|
||
const handleEditClick = (address: any, e: any) => {
|
||
e.preventDefault();
|
||
e.stopPropagation(); // برای جلوگیری از انتخاب شدن radio button هنگام کلیک روی ویرایش
|
||
|
||
setEditingAddressId(address.id);
|
||
// پر کردن مقادیر فرم با دیتای آدرس انتخاب شده (فرض بر این است که استیت شما setNewAddress نام دارد)
|
||
setNewAddress({
|
||
title: address.title || "",
|
||
recipientName: address.recipientName || "",
|
||
phone: address.phone || "",
|
||
province: address.province || "",
|
||
city: address.city || "",
|
||
postalCode: address.postalCode || "",
|
||
addressLine: address.addressLine || "",
|
||
plaque: address.plaque || "",
|
||
unit: address.unit || "",
|
||
isDefault: address.isDefault || false
|
||
});
|
||
setShowNewAddressForm(true);
|
||
};
|
||
|
||
// 3. تابع فراخوانی API ویرایش
|
||
const handleUpdateAddress = async () => {
|
||
// اضافه کردن این شرط برای جلوگیری از خطای تایپ و توقف اجرا در صورت null بودن ID
|
||
if (!editingAddressId) return;
|
||
|
||
try {
|
||
const response = await updateAddressApi(editingAddressId, newAddress);
|
||
if (response.success) {
|
||
// آپدیت کردن آدرس ویرایش شده در لیست آدرسها
|
||
setAddresses(prevAddresses =>
|
||
prevAddresses.map(addr => addr.id === editingAddressId ? response.data : addr)
|
||
);
|
||
|
||
// خروج از حالت فرم و ریست کردن مقادیر
|
||
setShowNewAddressForm(false);
|
||
setEditingAddressId(null);
|
||
setNewAddress({
|
||
title: "", recipientName: "", phone: "", province: "", city: "", postalCode: "", addressLine: "", plaque: "", unit: "", isDefault: false
|
||
});
|
||
}
|
||
} catch (error) {
|
||
// مدیریت خطا (در صورت نیاز آلرت یا توست نمایش دهید)
|
||
console.error("خطا در بهروزرسانی آدرس:", error);
|
||
}
|
||
};
|
||
|
||
// 4. تابع انصراف یکپارچه شده
|
||
const handleCancelForm = () => {
|
||
setShowNewAddressForm(false);
|
||
setEditingAddressId(null);
|
||
setNewAddress({
|
||
title: "", recipientName: "", phone: "", province: "", city: "", postalCode: "", addressLine: "", plaque: "", unit: "", isDefault: false
|
||
});
|
||
};
|
||
|
||
const handleDeleteAddress = async (addressId: string, e: React.MouseEvent) => {
|
||
e.stopPropagation(); // جلوگیری از انتخاب شدن آدرس هنگام کلیک روی دکمه حذف
|
||
|
||
// گرفتن تاییدیه از کاربر قبل از حذف
|
||
const confirmDelete = window.confirm("آیا از حذف این آدرس اطمینان دارید؟");
|
||
if (!confirmDelete) return;
|
||
|
||
try {
|
||
const response = await deleteAddressApi(addressId);
|
||
|
||
if (response.success) {
|
||
// حذف آدرس از لیست موجود در State
|
||
setAddresses(prevAddresses =>
|
||
prevAddresses.filter(addr => addr.id !== addressId)
|
||
);
|
||
|
||
// اگر آدرسی که پاک شد همان آدرس انتخابشده بود، انتخاب را لغو کن
|
||
if (selectedAddressId === addressId) {
|
||
setSelectedAddressId(null);
|
||
}
|
||
|
||
// اگر آدرسی که پاک شد در حال ویرایش بود، فرم ویرایش را ببند
|
||
if (editingAddressId === addressId) {
|
||
setShowNewAddressForm(false);
|
||
setEditingAddressId(null);
|
||
setNewAddress({
|
||
title: "", recipientName: "", phone: "", province: "", city: "", postalCode: "", addressLine: "", plaque: "", unit: "", isDefault: false
|
||
});
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error("خطا در حذف آدرس:", error);
|
||
// اینجا میتوانید یک Toast یا Alert برای نمایش خطا به کاربر اضافه کنید
|
||
}
|
||
};
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
// محاسبه قیمت کل و تعداد
|
||
const parsePrice = (priceStr?: number | null | string) => {
|
||
if (!priceStr) return 0;
|
||
return Number(priceStr.toString().replace(/,/g, ''));
|
||
};
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
// ۴. استفاده از اطلاعات سرور در صورت لاگین بودن، در غیر این صورت استفاده از Context
|
||
const totalPrice = isAuthenticated && serverSummary
|
||
? serverSummary.totalPrice || serverSummary.total || 0
|
||
: cart.reduce((total, item) => total + (parsePrice(item.price) * item.quantity), 0);
|
||
|
||
const totalItems = isAuthenticated && serverSummary
|
||
? serverSummary.totalQuantity || serverSummary.itemsCount || 0
|
||
: cart.reduce((total, item) => total + item.quantity, 0);
|
||
|
||
const shippingCost = shippingMethod === 'post' ? 45000 : 75000;
|
||
const finalPrice = totalPrice + shippingCost;
|
||
|
||
// حالت در حال بررسی توکن
|
||
if (isLoading) {
|
||
return (
|
||
<div className="min-h-screen flex items-center justify-center bg-gray-50/30">
|
||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[#ffb900]"></div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// اگر کاربر لاگین نبود
|
||
if (!isAuthenticated) {
|
||
return (
|
||
<div className="min-h-screen bg-gray-50/30 flex flex-col items-center justify-center p-6" dir="rtl">
|
||
<div className="bg-white p-12 rounded-[2rem] shadow-sm border border-gray-100 flex flex-col items-center max-w-md w-full text-center">
|
||
<div className="bg-gray-100 w-24 h-24 rounded-full flex items-center justify-center mb-6">
|
||
<Lock size={40} className="text-gray-400" />
|
||
</div>
|
||
<h2 className="text-xl font-bold text-gray-800 mb-3">دسترسی محدود</h2>
|
||
<p className="text-gray-500 mb-8 leading-relaxed text-sm">
|
||
برای ثبت نهایی سفارش و مشاهده این صفحه، باید ابتدا وارد حساب کاربری خود شوید یا ثبتنام کنید.
|
||
</p>
|
||
<Link href="/cart" className="w-full bg-[#ffb900] hover:bg-[#e5a600] text-[#1A2332] font-bold py-3 rounded-xl transition-all flex items-center justify-center gap-2">
|
||
<ChevronRight size={18} />
|
||
بازگشت به سبد خرید
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// رندر اصلی کامپوننت Checkout
|
||
return (
|
||
<main className="bg-gray-50/30 min-h-screen pb-20" dir="rtl">
|
||
<div className="container mx-auto px-4 py-8 max-w-6xl">
|
||
|
||
<div className="mb-10">
|
||
<div className="flex items-center justify-between mb-8">
|
||
<h1 className="text-xl md:text-3xl font-black text-[#1A2332]">
|
||
اطلاعات ارسال
|
||
</h1>
|
||
<Link href="/cart" className="text-sm text-gray-500 hover:text-[#1A2332] flex items-center gap-1 transition-colors">
|
||
<ChevronRight size={16} />
|
||
بازگشت به سبد خرید
|
||
</Link>
|
||
</div>
|
||
|
||
<div className="relative w-full max-w-2xl mx-auto px-2 sm:px-0 mb-12">
|
||
<div className="absolute top-[20px] sm:top-[24px] left-[16.5%] right-[16.5%] h-[2px] bg-gray-200 z-0">
|
||
<div className="h-full bg-[#ffb900] w-[50%] transition-all duration-500 ease-in-out"></div>
|
||
</div>
|
||
|
||
<div className="flex justify-between relative z-10">
|
||
<Link href="/cart" className="flex flex-col items-center w-1/3 group cursor-pointer">
|
||
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-[#ffb900] text-[#1A2332] rounded-full flex items-center justify-center shadow-md mb-2 sm:mb-3 ring-[6px] ring-[#f8fafc] sm:ring-[#f8fafc] transition-transform group-hover:scale-110">
|
||
<ShoppingBag className="w-5 h-5 sm:w-6 sm:h-6" strokeWidth={2} />
|
||
</div>
|
||
<span className="text-[10px] sm:text-sm font-bold text-[#1A2332] text-center">سبد خرید</span>
|
||
</Link>
|
||
|
||
<div className="flex flex-col items-center w-1/3">
|
||
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-[#ffb900] text-[#1A2332] rounded-full flex items-center justify-center shadow-[0_0_15px_rgba(255,185,0,0.4)] mb-2 sm:mb-3 ring-[6px] ring-[#ffb900]/20 transition-all">
|
||
<Truck className="w-4 h-4 sm:w-5 sm:h-5" strokeWidth={2} />
|
||
</div>
|
||
<span className="text-[10px] sm:text-sm font-bold text-[#1A2332] text-center">اطلاعات ارسال</span>
|
||
</div>
|
||
|
||
<div className="flex flex-col items-center w-1/3">
|
||
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-white border-2 border-gray-200 text-gray-400 rounded-full flex items-center justify-center mb-2 sm:mb-3 ring-[6px] ring-[#f8fafc] sm:ring-[#f8fafc]">
|
||
<CreditCard className="w-4 h-4 sm:w-5 sm:h-5" />
|
||
</div>
|
||
<span className="text-[10px] sm:text-sm font-medium text-gray-400 text-center">پرداخت</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex flex-col lg:flex-row gap-6 lg:gap-8">
|
||
{/* ... (فرمهای آدرس گیرنده بدون تغییر) ... */}
|
||
<div className="flex-1 space-y-6">
|
||
<div className="flex-1 space-y-6">
|
||
{isAddressLoading ? (
|
||
<div className="bg-white rounded-[1rem] p-8 shadow-sm flex justify-center items-center">
|
||
<p className="text-gray-500">در حال بارگذاری آدرسها...</p>
|
||
</div>
|
||
) : (
|
||
<>
|
||
{showNewAddressForm ? (
|
||
<div className="bg-white rounded-[1rem] p-6 shadow-sm space-y-6 border border-gray-100">
|
||
<div className="flex justify-between items-center border-b pb-4">
|
||
<button className="text-lg cursor-pointer font-bold text-gray-800">
|
||
{editingAddressId ? 'ویرایش آدرس' : 'افزودن آدرس جدید'}
|
||
</button>
|
||
{addresses.length > 0 && (
|
||
<button
|
||
type="button"
|
||
onClick={handleCancelForm}
|
||
className="text-sm cursor-pointer text-red-500 hover:text-red-700"
|
||
>
|
||
انصراف
|
||
</button>
|
||
)}
|
||
</div>
|
||
|
||
<div className="bg-[#FBFBFB] border border-gray-200 rounded-2xl p-8 shadow-sm space-y-6">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<div className="space-y-2">
|
||
<label className="text-sm text-gray-600 font-medium">
|
||
عنوان آدرس (مثلاً: خانه، محل کار)
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="title"
|
||
value={newAddress.title}
|
||
onChange={handleAddressInputChange}
|
||
className="w-full border border-gray-300 rounded-lg p-3 text-sm bg-white text-gray-800 focus:outline-none focus:border-[#FFB900] focus:ring-1 focus:ring-[#FFB900] transition-all placeholder-gray-400"
|
||
placeholder="خانه"
|
||
/>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<label className="text-sm text-gray-600 font-medium">
|
||
نام و نام خانوادگی گیرنده
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="recipientName"
|
||
value={newAddress.recipientName}
|
||
onChange={handleAddressInputChange}
|
||
className="w-full border border-gray-300 rounded-lg p-3 text-sm bg-white text-gray-800 focus:outline-none focus:border-[#FFB900] focus:ring-1 focus:ring-[#FFB900] transition-all placeholder-gray-400"
|
||
placeholder="نام گیرنده"
|
||
/>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<label className="text-sm text-gray-600 font-medium">شماره موبایل</label>
|
||
<input
|
||
type="tel"
|
||
name="phone"
|
||
value={newAddress.phone}
|
||
onChange={handleAddressInputChange}
|
||
className="w-full border border-gray-300 rounded-lg p-3 text-sm bg-white text-gray-800 focus:outline-none focus:border-[#FFB900] focus:ring-1 focus:ring-[#FFB900] transition-all placeholder-gray-400"
|
||
placeholder="09123456789"
|
||
/>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<label className="text-sm text-gray-600 font-medium">استان</label>
|
||
<input
|
||
type="text"
|
||
name="province"
|
||
value={newAddress.province}
|
||
onChange={handleAddressInputChange}
|
||
className="w-full border border-gray-300 rounded-lg p-3 text-sm bg-white text-gray-800 focus:outline-none focus:border-[#FFB900] focus:ring-1 focus:ring-[#FFB900] transition-all placeholder-gray-400"
|
||
placeholder="تهران"
|
||
/>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<label className="text-sm text-gray-600 font-medium">شهر</label>
|
||
<input
|
||
type="text"
|
||
name="city"
|
||
value={newAddress.city}
|
||
onChange={handleAddressInputChange}
|
||
className="w-full border border-gray-300 rounded-lg p-3 text-sm bg-white text-gray-800 focus:outline-none focus:border-[#FFB900] focus:ring-1 focus:ring-[#FFB900] transition-all placeholder-gray-400"
|
||
placeholder="تهران"
|
||
/>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<label className="text-sm text-gray-600 font-medium">
|
||
کد پستی (۱۰ رقمی)
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="postalCode"
|
||
value={newAddress.postalCode}
|
||
onChange={handleAddressInputChange}
|
||
className="w-full border border-gray-300 rounded-lg p-3 text-sm bg-white text-gray-800 focus:outline-none focus:border-[#FFB900] focus:ring-1 focus:ring-[#FFB900] transition-all placeholder-gray-400"
|
||
placeholder="1234567890"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<label className="text-sm text-gray-600 font-medium">آدرس دقیق پستی</label>
|
||
<textarea
|
||
name="addressLine"
|
||
value={newAddress.addressLine}
|
||
onChange={handleAddressInputChange}
|
||
rows={3}
|
||
className="w-full border border-gray-300 rounded-lg p-3 text-sm bg-white text-gray-800 focus:outline-none focus:border-[#FFB900] focus:ring-1 focus:ring-[#FFB900] transition-all placeholder-gray-400"
|
||
placeholder="خیابان اصلی، کوچه فرعی..."
|
||
/>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-6 md:w-1/2">
|
||
<div className="space-y-2">
|
||
<label className="text-sm text-gray-600 font-medium">پلاک</label>
|
||
<input
|
||
type="text"
|
||
name="plaque"
|
||
value={newAddress.plaque}
|
||
onChange={handleAddressInputChange}
|
||
className="w-full border border-gray-300 rounded-lg p-3 text-sm bg-white text-gray-800 focus:outline-none focus:border-[#FFB900] focus:ring-1 focus:ring-[#FFB900] transition-all"
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<label className="text-sm text-gray-600 font-medium">واحد (اختیاری)</label>
|
||
<input
|
||
type="text"
|
||
name="unit"
|
||
value={newAddress.unit}
|
||
onChange={handleAddressInputChange}
|
||
className="w-full border border-gray-300 rounded-lg p-3 text-sm bg-white text-gray-800 focus:outline-none focus:border-[#FFB900] focus:ring-1 focus:ring-[#FFB900] transition-all"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-4">
|
||
<button
|
||
type="button"
|
||
onClick={editingAddressId ? handleUpdateAddress : handleAddNewAddress}
|
||
className="w-full cursor-pointer md:w-auto bg-[#FFB900] text-white px-10 py-3 rounded-lg font-semibold shadow-md hover:bg-[#e5a000] transition-colors"
|
||
>
|
||
{editingAddressId ? 'ویرایش و ذخیره آدرس' : 'ثبت و ذخیره آدرس'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
) : (
|
||
/* ================= بخش لیست آدرسهای موجود ================= */
|
||
<div className="bg-white rounded-[1rem] p-6 shadow-sm space-y-4">
|
||
<div className="flex justify-between items-center border-b border-[#dcdbdb] pb-4 mb-4">
|
||
<h2 className="text-lg font-bold text-gray-800">انتخاب آدرس ارسال</h2>
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
setEditingAddressId(null);
|
||
setNewAddress({ title: "", recipientName: "", phone: "", province: "", city: "", postalCode: "", addressLine: "", plaque: "", unit: "", isDefault: false });
|
||
setShowNewAddressForm(true);
|
||
}}
|
||
className="text-sm cursor-pointer text-blue-600 hover:text-blue-800 font-medium flex items-center gap-1"
|
||
>
|
||
<span>+</span> افزودن آدرس جدید
|
||
</button>
|
||
</div>
|
||
|
||
{addresses.length === 0 ? (
|
||
<div className="text-center py-6 text-gray-500">
|
||
هیچ آدرسی یافت نشد. لطفاً یک آدرس جدید اضافه کنید.
|
||
</div>
|
||
) : (
|
||
<div className="space-y-3">
|
||
{addresses.map((address) => (
|
||
<label
|
||
key={address.id}
|
||
className={`relative flex flex-col gap-4 p-3 cursor-pointer rounded-2xl border transition-all duration-300 ${selectedAddressId === address.id
|
||
? 'border-[#ffb900] bg-[#ffb900]/[0.03] ring-1 ring-[#ffb900]/20 shadow-sm'
|
||
: 'border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm'
|
||
}`}
|
||
>
|
||
<div className="flex items-start gap-4">
|
||
{/* رادیو باتن */}
|
||
<div className="pt-0.5 relative flex items-center justify-center">
|
||
<input
|
||
type="radio"
|
||
name="selectedAddress"
|
||
value={address.id}
|
||
checked={selectedAddressId === address.id}
|
||
onChange={() => setSelectedAddressId(address.id)}
|
||
// حذف استایل پیشفرض و اعمال بوردر و رنگ سفارشی
|
||
className="peer appearance-none w-[18px] h-[18px] border-[1.5px] border-gray-300 rounded-full checked:border-[#ffb900] checked:bg-[#ffb900] cursor-pointer transition-all focus:outline-none focus:ring-2 focus:ring-[#ffb900]/20 focus:ring-offset-1 bg-white"
|
||
/>
|
||
{/* دایره سفید مرکزی که فقط در حالت انتخاب شده نمایش داده میشود */}
|
||
<div className="pointer-events-none absolute w-[10px] h-[10px] rounded-full bg-white opacity-0 peer-checked:opacity-100 transition-opacity"></div>
|
||
</div>
|
||
|
||
|
||
|
||
<div className="flex-1 space-y-3">
|
||
{/* عنوان و متن آدرس */}
|
||
<div>
|
||
{address.title && (
|
||
<span className="inline-block mb-2 bg-gray-50 text-gray-500 border border-gray-200 text-[11px] font-medium px-2.5 py-0.5 rounded-full">
|
||
{address.title}
|
||
</span>
|
||
)}
|
||
<p className="font-medium text-gray-800 leading-relaxed text-sm md:text-base">
|
||
{address.addressLine}
|
||
</p>
|
||
</div>
|
||
|
||
{/* جزئیات آدرس (مینیمال شده) */}
|
||
<div className="flex flex-wrap items-center text-xs md:text-sm text-gray-500 gap-x-2 gap-y-1.5">
|
||
<span>{address.recipientName}</span>
|
||
<span className="text-gray-300 text-[10px]">●</span>
|
||
<span dir="ltr">{address.phone}</span>
|
||
<span className="text-gray-300 text-[10px]">●</span>
|
||
<span>کد پستی: {address.postalCode}</span>
|
||
<span className="text-gray-300 text-[10px]">●</span>
|
||
<span>
|
||
پلاک {address.plaque}
|
||
{address.unit ? `، واحد ${address.unit}` : ''}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* نوار دکمههای عملیاتی */}
|
||
<div className="flex items-center justify-end gap-2 pt-3 mt-1 border-t border-gray-100/80">
|
||
<button
|
||
type="button"
|
||
onClick={(e) => handleEditClick(address, e)}
|
||
className="px-3 cursor-pointer py-1.5 text-xs font-medium text-gray-500 transition-colors rounded-lg hover:bg-blue-50 hover:text-blue-600 focus:outline-none"
|
||
>
|
||
ویرایش آدرس
|
||
</button>
|
||
|
||
<button
|
||
type="button"
|
||
onClick={(e) => handleDeleteAddress(address.id, e)}
|
||
className="px-3 cursor-pointer py-1.5 text-xs font-medium text-gray-500 transition-colors rounded-lg hover:bg-red-50 hover:text-red-600 focus:outline-none"
|
||
>
|
||
حذف
|
||
</button>
|
||
</div>
|
||
</label>
|
||
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
)}
|
||
</>
|
||
|
||
)}
|
||
|
||
<div className="bg-white rounded-[1rem] p-6 shadow-sm space-y-4">
|
||
<div className="flex items-center gap-3 mb-6 pb-4 ">
|
||
<div className="bg-[#1A2332]/5 p-2 rounded-lg text-[#1A2332]">
|
||
<Truck size={20} />
|
||
</div>
|
||
<h2 className="text-base md:text-lg font-bold text-[#1A2332]">نحوه ارسال</h2>
|
||
</div>
|
||
<div className="space-y-3">
|
||
<label className={`flex items-center justify-between p-4 rounded-xl border-1 cursor-pointer transition-all ${shippingMethod === 'post' ? 'border-[#ffb900] bg-[#ffb900]/3' : 'border-gray-100 hover:border-gray-200 bg-white'}`}>
|
||
<div className="flex items-center gap-3">
|
||
<div className={`w-5 h-5 rounded-full border-1 flex items-center justify-center ${shippingMethod === 'post' ? 'border-[#ffb900]' : 'border-gray-300'}`}>
|
||
{shippingMethod === 'post' && <div className="w-2.5 h-2.5 bg-[#ffb900] rounded-full" />}
|
||
</div>
|
||
<div>
|
||
<div className="font-bold text-[#1A2332]">پست پیشتاز</div>
|
||
<div className="text-xs text-gray-500 mt-1">زمان تحویل: ۳ تا ۵ روز کاری</div>
|
||
</div>
|
||
</div>
|
||
<div className="font-bold text-[#1A2332]">۴۵,۰۰۰ <span className="text-xs font-normal text-gray-500">تومان</span></div>
|
||
<input type="radio" name="shipping" value="post" className="hidden" checked={shippingMethod === 'post'} onChange={() => setShippingMethod('post')} />
|
||
</label>
|
||
<label className={`flex items-center justify-between p-4 rounded-xl border-1 cursor-pointer transition-all ${shippingMethod === 'tipax' ? 'border-[#ffb900] bg-[#ffb900]/3' : 'border-gray-100 hover:border-gray-200 bg-white'}`}>
|
||
<div className="flex items-center gap-3">
|
||
<div className={`w-5 h-5 rounded-full border-1 flex items-center justify-center ${shippingMethod === 'tipax' ? 'border-[#ffb900]' : 'border-gray-300'}`}>
|
||
{shippingMethod === 'tipax' && <div className="w-2.5 h-2.5 bg-[#ffb900] rounded-full" />}
|
||
</div>
|
||
<div>
|
||
<div className="font-bold text-[#1A2332]">تیپاکس (پسکرایه)</div>
|
||
<div className="text-xs text-gray-500 mt-1">زمان تحویل: ۱ تا ۲ روز کاری</div>
|
||
</div>
|
||
</div>
|
||
<div className="font-bold text-[#1A2332]">۷۵,۰۰۰ <span className="text-xs font-normal text-gray-500">تومان</span></div>
|
||
<input type="radio" name="shipping" value="tipax" className="hidden" checked={shippingMethod === 'tipax'} onChange={() => setShippingMethod('tipax')} />
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
<div className="w-full lg:w-[400px] shrink-0">
|
||
<div className="bg-white rounded-[1rem] p-6 md:p-8 shadow-sm sticky top-6">
|
||
<h2 className="text-lg md:text-xl font-bold text-[#1A2332] mb-6">خلاصه سفارش</h2>
|
||
<div className="space-y-4 mb-6">
|
||
<div className="flex justify-between items-center text-xs md:text-sm">
|
||
<span className="text-gray-500">مبلغ کالاها ({totalItems})</span>
|
||
<span className="font-bold text-[#1A2332]">{totalPrice > 0 ? totalPrice.toLocaleString('fa-IR') : '۰'} <span className="text-[10px] font-normal text-gray-500">تومان</span></span>
|
||
</div>
|
||
<div className="flex justify-between items-center text-xs md:text-sm">
|
||
<span className="text-gray-500">هزینه ارسال</span>
|
||
<span className="font-bold text-[#1A2332]">{shippingCost.toLocaleString('fa-IR')} <span className="text-[10px] font-normal text-gray-500">تومان</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* ... بقیه قسمتها (متد ارسال و دکمه پرداخت) بدون تغییر ... */}
|
||
<div className="w-full border-t-2 border-dashed border-gray-100 my-6"></div>
|
||
|
||
|
||
<div className="w-full border-t-2 border-dashed border-gray-100 my-6"></div>
|
||
|
||
<div>
|
||
<PaymentMethodsSection />
|
||
<span className="font-black mt-4 mb-4 justify-center items-center flex gap-4 text-[#1A2332] tracking-tight">
|
||
<span className=" font-bold text-gray-600">مبلغ قابل پرداخت</span>
|
||
<span className="text-base flex items-center md:text-lg font-bold text-[#1A2332]">
|
||
{finalPrice > 0 && finalPrice.toLocaleString('fa-IR')}
|
||
{finalPrice > 0 && (
|
||
<span className="text-xs md:text-sm font-medium text-gray-500 mr-1">تومان</span>
|
||
)}
|
||
</span>
|
||
</span>
|
||
</div>
|
||
|
||
<Link href="/payment" className="w-full bg-[#ffb900] hover:bg-[#e5a600] text-[#1A2332] py-3 md:py-4 rounded-xl text-base text-[1em] transition-all shadow-[0_4px_15px_rgba(255,185,0,0.2)] hover:shadow-[0_6px_20px_rgba(255,185,0,0.3)] flex justify-center items-center gap-2 mb-4">
|
||
پرداخت و ثبت نهایی
|
||
<ChevronLeft size={20} strokeWidth={2} />
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
);
|
||
}
|