"use client"; import React, { useState, useEffect } from "react"; import { motion } from "motion/react"; import { ChevronRight, ChevronLeft, CreditCard, Truck, ShieldCheck, Wallet, Clock, ArrowRight, Heart, PlusCircle, Copy } from "lucide-react"; import { toPersian, cn } from "../lib/utils"; import { PRODUCTS } from "../lib/data/products"; import { useCartStore } from "../lib/store/cartStore"; import { usePetStore } from "../lib/store/usePetStore"; import { useUserStore } from "../lib/store/userStore"; import { useSettingsStore } from "../lib/store/settingsStore"; import TopUpModal from "./TopUpModal"; import AddressModal from "./AddressModal"; import SearchableSelect from "./SearchableSelect"; import { IRAN_PROVINCES, PROVINCE_CITIES } from "../lib/data/provinces"; import { toast } from "sonner"; import { useRouter } from 'next/navigation'; export default function CheckoutPage() { const router = useRouter(); const { items, getTotal, getSubtotal, isSubscribed, charityDonation, setCharityDonation, addOrder, clearCart } = useCartStore(); const { getActivePet, updatePet } = usePetStore(); const { profile, isLoggedIn } = useUserStore(); const [loading, setLoading] = useState(false); const [useRoundUp, setUseRoundUp] = useState(false); const [paymentMethod, setPaymentMethod] = useState('card'); const [showTopUpModal, setShowTopUpModal] = useState(false); const [showAddressModal, setShowAddressModal] = useState(false); const [selectedAddressId, setSelectedAddressId] = useState(''); // Custom manual address state if guest or not using stored addresses const [manualName, setManualName] = useState(''); const [manualPhone, setManualPhone] = useState(''); const [manualProvince, setManualProvince] = useState(''); const [manualCity, setManualCity] = useState(''); const [manualZipCode, setManualZipCode] = useState(''); const [manualAddress, setManualAddress] = useState(''); const handleCopy = (text: string, title: string) => { navigator.clipboard.writeText(text); toast.success(`${title} با موفقیت کپی شد!`); }; useEffect(() => { Promise.resolve().then(() => { // Set default selected address if logged in if (isLoggedIn && profile.addresses && profile.addresses.length > 0) { const def = profile.addresses.find(a => a.isDefault) || profile.addresses[0]; setSelectedAddressId(def.id); } else if (isLoggedIn && profile.firstName) { // Pre-fill profile info for manual fields setManualName(`${profile.firstName} ${profile.lastName}`.trim()); setManualPhone(profile.mobile || ''); } }); }, [isLoggedIn, profile]); const roundStep = Number(useSettingsStore((state) => state.getText('CHARITY_ROUND_STEP', '10000'))) || 10000; const shippingFeeSetting = useSettingsStore((state) => state.getText('shipping_fee', '0')); const shippingFee = Number(shippingFeeSetting) || 0; const subtotal = getSubtotal(); const remainder = subtotal % roundStep; const roundUpDiff = remainder === 0 ? roundStep : roundStep - remainder; const handleCharityToggle = () => { if (!useRoundUp) { setCharityDonation(roundUpDiff); setUseRoundUp(true); } else { setCharityDonation(0); setUseRoundUp(false); } }; const handleFinalize = async () => { const totalAmount = getTotal() + shippingFee; setLoading(true); const activePet = getActivePet(); try { // 1. Deduct wallet balance if payment method is wallet if (paymentMethod === 'wallet') { const walletDeducted = Math.min(profile.walletBalance || 0, totalAmount); if (walletDeducted > 0) { const newBalance = (profile.walletBalance || 0) - walletDeducted; const newTransaction = { id: `TR-${Math.floor(Math.random() * 90000) + 10000}`, type: 'purchase' as const, amount: walletDeducted, date: new Date().toISOString(), status: 'success' as const }; useUserStore.setState((state) => ({ profile: { ...state.profile, walletBalance: newBalance, transactions: [newTransaction, ...(state.profile.transactions || [])] } })); } } // 2. Register the order in the backend const selectedAddr = profile.addresses?.find(a => a.id === selectedAddressId); let shippingAddressStr = ''; if (selectedAddr) { shippingAddressStr = `${selectedAddr.province}، ${selectedAddr.city}، ${selectedAddr.detail}${selectedAddr.zipCode ? ` (کد پستی: ${selectedAddr.zipCode})` : ''} (گیرنده: ${selectedAddr.receptorName} - ${toPersian(selectedAddr.phone)})`; } else if (manualAddress) { const provCity = [manualProvince, manualCity].filter(Boolean).join('، '); shippingAddressStr = `${provCity ? provCity + '، ' : ''}${manualAddress}${manualZipCode ? ` (کد پستی: ${manualZipCode})` : ''}${manualName ? ` (گیرنده: ${manualName}${manualPhone ? ` - ${toPersian(manualPhone)}` : ''})` : ''}`; } // Check wallet balance if payment method is wallet if (paymentMethod === 'wallet' && (profile.walletBalance || 0) < totalAmount) { toast.error(`موجودی کیف پول شما (${(profile.walletBalance || 0).toLocaleString('fa-IR')} تومان) کمتر از مبلغ نهایی سفارش (${totalAmount.toLocaleString('fa-IR')} تومان) است. لطفاً ابتدا کیف پول خود را شارژ کنید.`); setLoading(false); return; } // Check shipping address requirement if (!selectedAddressId && !manualAddress) { toast.error("لطفاً یک آدرس برای ارسال سفارش انتخاب کرده یا آدرس جدید وارد کنید."); setLoading(false); return; } const orderId = await addOrder({ items: [...items], total: totalAmount, charityDonation: charityDonation, paymentMethod: paymentMethod, isRefill: isSubscribed, petId: activePet?.id, shippingAddress: shippingAddressStr }); // Re-fetch user profile from backend to sync PostgreSQL wallet balance and charity donation total const { fetchProfile } = useUserStore.getState(); await fetchProfile(); // Update pet consumptions and charity contributions for active pet const targetPet = activePet || usePetStore.getState().pets[0]; if (targetPet) { const newConsumptions = [...(targetPet.consumptions || [])]; items.forEach(item => { const existing = newConsumptions.find(c => c.productId === item.product.id); if (existing) { existing.remaining += item.product.packageSize * item.quantity; existing.packageSize = item.product.packageSize; } else { newConsumptions.push({ productId: item.product.id, packageSize: item.product.packageSize, remaining: item.product.packageSize * item.quantity }); } }); updatePet(targetPet.id, { consumptions: newConsumptions }); } clearCart(); toast.success("سفارش شما با موفقیت ثبت شد!"); router.push(`/checkout/success/${orderId}`); } catch (err: unknown) { const errorObj = err as { response?: { data?: { message?: string; details?: Array<{ message?: string }> } }; message?: string }; const backendMessage = errorObj.response?.data?.message || errorObj.response?.data?.details?.[0]?.message || errorObj.message; if (backendMessage && !backendMessage.includes("Request failed")) { toast.error(`خطا در ثبت سفارش: ${backendMessage}`); } } finally { setLoading(false); } }; if (items.length === 0) { return (

سبد خرید شما خالی است

); } return (
{/* Mobile Navigation & Breadcrumbs */}
router.push('/shop')}>سبد خرید نهایی‌سازی سفارش
{/* Desktop Breadcrumbs (Hidden on Mobile) */}
router.push('/')}>خانه درگاه پرداخت امن و نهایی‌سازی
{/* Main Form */}
{/* Step 1: Shipping */}
۱

اطلاعات ارسال

{isLoggedIn && profile.addresses && profile.addresses.length > 0 ? (

انتخاب آدرس جهت تحویل سفارش:

{profile.addresses.map((addr) => (
setSelectedAddressId(addr.id)} className={cn( "p-4 rounded-2xl border-2 cursor-pointer transition-all flex items-start gap-3", selectedAddressId === addr.id ? "border-canina-blue bg-canina-blue/5" : "border-medical-gray-100 hover:border-medical-gray-200" )} > setSelectedAddressId(addr.id)} className="mt-1" />
{addr.title} {addr.isDefault && ( پیش‌فرض )}

{addr.province}، {addr.city}، {addr.detail}

گیرنده: {addr.receptorName} | همراه: {toPersian(addr.phone)}

))}
) : (
{isLoggedIn && (
)}
setManualName(e.target.value)} className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20" placeholder="مثلاً: پارسا آقایی" />
setManualPhone(e.target.value.replace(/[^0-9]/g, ''))} className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20 dir-ltr text-right" placeholder="۰۹۱۲XXXXXXX" />
{ const cities = PROVINCE_CITIES[prov] || []; setManualProvince(prov); setManualCity(cities[0] || ''); }} />
{manualProvince && PROVINCE_CITIES[manualProvince] ? ( setManualCity(c)} /> ) : ( setManualCity(e.target.value)} className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-3 sm:py-4 px-4 sm:px-6 text-xs sm:text-sm focus:outline-none focus:ring-2 focus:ring-canina-blue/20" placeholder="نام شهر..." /> )}