Files
parsshop/app/checkout/page.tsx
2026-04-29 12:44:33 +03:30

400 lines
29 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useCart } from "@/components/context/cartcontext";
import Link from "next/link";
import { useState, useEffect } from "react";
import { ShoppingBag, ChevronLeft, ChevronRight, Truck, CreditCard, Lock } from "lucide-react";
import PaymentMethodsSection from "@/components/PaymentMethods";
import { getCartApi } from "@/public/src/services/cart/api";
import AddressCard from "@/components/AddressCard";
import { useAddressContext } from "@/components/context/Addresscontext";
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, isAddressLoading, showNewAddressForm, setShowNewAddressForm,
editingAddressId, newAddress, handleAddressInputChange,
handleAddNewAddress, handleUpdateAddress, handleCancelForm
} = useAddressContext();
useEffect(() => {
const initializeCheckout = async () => {
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);
}
} else {
setIsAuthenticated(false);
}
setIsLoading(false);
};
initializeCheckout();
}, []);
const parsePrice = (priceStr?: number | null | string) => {
if (!priceStr) return 0;
return Number(priceStr.toString().replace(/,/g, ''));
};
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>
);
}
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 border-gray-300 b-4">
<button className="text-lg cursor-pointer mb-4 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={() => 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) => (
<AddressCard
key={address.id}
address={address}
/>
))}
</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>
<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>
);
}