526 lines
28 KiB
TypeScript
526 lines
28 KiB
TypeScript
"use client";
|
||
import React, { useState, useEffect } from "react";
|
||
import { motion } from "motion/react";
|
||
import {
|
||
ChevronRight,
|
||
ChevronLeft,
|
||
MapPin,
|
||
CreditCard,
|
||
Truck,
|
||
ShieldCheck,
|
||
Calendar,
|
||
Wallet,
|
||
Clock,
|
||
ArrowRight,
|
||
CheckCircle2,
|
||
Heart,
|
||
PlusCircle
|
||
} from "lucide-react";
|
||
import { toPersian, cn } from "../lib/utils";
|
||
import { useCartStore } from "../lib/store/cartStore";
|
||
import { usePetStore } from "../lib/store/usePetStore";
|
||
import { useUserStore } from "../lib/store/userStore";
|
||
import { Product, PRODUCTS } from "../lib/data/products";
|
||
import { productService } from "../lib/services/productService";
|
||
import { toast } from "sonner";
|
||
import { useRouter } from 'next/navigation';
|
||
|
||
export default function CheckoutPage() {
|
||
const router = useRouter();
|
||
const { items, getTotal, getSubtotal, getDiscount, isSubscribed, charityDonation, setCharityDonation, addOrder, clearCart } = useCartStore();
|
||
const { getActivePet, updatePet } = usePetStore();
|
||
const { profile, isLoggedIn, updateProfile } = useUserStore();
|
||
const [step, setStep] = useState(1);
|
||
const [loading, setLoading] = useState(false);
|
||
const [useRoundUp, setUseRoundUp] = useState(false);
|
||
const [dbProducts, setDbProducts] = useState<Product[]>([]);
|
||
const [paymentMethod, setPaymentMethod] = useState<string>('online');
|
||
|
||
const [selectedAddressId, setSelectedAddressId] = useState<string>('');
|
||
const [showNewAddressForm, setShowNewAddressForm] = useState(false);
|
||
|
||
// Custom manual address state if guest or not using stored addresses
|
||
const [manualName, setManualName] = useState('');
|
||
const [manualPhone, setManualPhone] = useState('');
|
||
const [manualAddress, setManualAddress] = useState('');
|
||
|
||
useEffect(() => {
|
||
productService.getProducts({ limit: 999 }).then(res => setDbProducts(res.data));
|
||
|
||
// 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 || '');
|
||
} else {
|
||
setSelectedAddressId('');
|
||
}
|
||
}, [profile, isLoggedIn]);
|
||
|
||
const subtotal = getSubtotal();
|
||
const remainder = subtotal % 10000;
|
||
const roundUpDiff = remainder === 0 ? 10000 : 10000 - remainder;
|
||
|
||
const handleCharityToggle = () => {
|
||
if (!useRoundUp) {
|
||
setCharityDonation(roundUpDiff);
|
||
setUseRoundUp(true);
|
||
} else {
|
||
setCharityDonation(0);
|
||
setUseRoundUp(false);
|
||
}
|
||
};
|
||
|
||
const handleFinalize = async () => {
|
||
const totalAmount = getTotal();
|
||
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);
|
||
const shippingAddressStr = selectedAddr
|
||
? `${selectedAddr.province}، ${selectedAddr.city}، ${selectedAddr.detail} (گیرنده: ${selectedAddr.receptorName} - ${toPersian(selectedAddr.phone)})`
|
||
: manualAddress || undefined;
|
||
|
||
const orderId = await addOrder({
|
||
items: [...items],
|
||
total: totalAmount,
|
||
charityDonation: charityDonation,
|
||
petId: activePet?.id,
|
||
shippingAddress: shippingAddressStr
|
||
});
|
||
|
||
// 3. Update user charity total
|
||
if (charityDonation > 0) {
|
||
useUserStore.setState((state) => ({
|
||
profile: {
|
||
...state.profile,
|
||
charityDonationTotal: (state.profile.charityDonationTotal || 0) + charityDonation
|
||
}
|
||
}));
|
||
}
|
||
|
||
// 4. Update pet consumptions and charity contributions for active pet
|
||
if (activePet) {
|
||
const newConsumptions = [...(activePet.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(activePet.id, {
|
||
consumptions: newConsumptions
|
||
});
|
||
}
|
||
|
||
clearCart();
|
||
toast.success("سفارش شما با موفقیت ثبت شد!");
|
||
router.push(`/checkout/success/${orderId}`);
|
||
} catch (err: any) {
|
||
toast.error(err.message || "خطا در ثبت سفارش. لطفاً مجدداً تلاش کنید.");
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
if (items.length === 0) {
|
||
return (
|
||
<div className="min-h-screen bg-medical-gray-50 flex items-center justify-center p-6" dir="rtl">
|
||
<div className="text-center space-y-6">
|
||
<div className="w-24 h-24 bg-white rounded-full flex items-center justify-center mx-auto text-medical-gray-200">
|
||
<ShieldCheck className="w-12 h-12" />
|
||
</div>
|
||
<h2 className="text-2xl font-black text-medical-gray-900 italic">سبد خرید شما خالی است</h2>
|
||
<button onClick={() => router.push('/shop')} className="text-canina-blue font-black underline underline-offset-8">بازگشت به فروشگاه</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-medical-gray-50 py-12 px-6 font-vazir" dir="rtl">
|
||
<div className="max-w-7xl mx-auto">
|
||
|
||
{/* Mobile Navigation & Breadcrumbs */}
|
||
<div className="lg:hidden mb-10 space-y-4">
|
||
<button
|
||
onClick={() => router.back()}
|
||
className="flex items-center gap-2 text-medical-gray-500 font-bold hover:text-canina-blue transition-colors"
|
||
>
|
||
<ChevronRight className="w-5 h-5" />
|
||
<span>بازگشت به سبد خرید</span>
|
||
</button>
|
||
|
||
<div className="flex items-center gap-2 text-[10px] font-black text-medical-gray-400 uppercase tracking-widest overflow-x-auto whitespace-nowrap pb-2">
|
||
<span className="cursor-pointer hover:text-canina-blue" onClick={() => router.push('/shop')}>سبد خرید</span>
|
||
<ChevronLeft className="w-2.5 h-2.5" />
|
||
<span className="text-canina-blue">نهاییسازی سفارش</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Desktop Breadcrumbs (Hidden on Mobile) */}
|
||
<div className="hidden lg:flex items-center gap-2 text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-10">
|
||
<span className="cursor-pointer hover:text-canina-blue" onClick={() => router.push('/')}>خانه</span>
|
||
<ChevronLeft className="w-2.5 h-2.5" />
|
||
<span className="text-canina-blue">درگاه پرداخت امن و نهاییسازی</span>
|
||
</div>
|
||
|
||
<div className="grid lg:grid-cols-12 gap-8 lg:gap-12">
|
||
{/* Main Form */}
|
||
<div className="lg:col-span-8 space-y-6 sm:space-y-8">
|
||
{/* Step 1: Shipping */}
|
||
<section className="bg-white rounded-[2rem] sm:rounded-[3rem] border border-medical-gray-200 p-5 sm:p-10 overflow-hidden relative">
|
||
<div className="absolute top-0 right-0 w-2.5 sm:w-3 h-full bg-canina-blue opacity-20" />
|
||
<div className="flex items-center gap-3 sm:gap-4 mb-6 sm:mb-10">
|
||
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-medical-gray-900 text-white rounded-2xl flex items-center justify-center font-black text-sm sm:text-base">۱</div>
|
||
<h2 className="text-xl sm:text-2xl font-black text-medical-gray-900 italic">اطلاعات ارسال</h2>
|
||
</div>
|
||
|
||
{isLoggedIn && profile.addresses && profile.addresses.length > 0 && !showNewAddressForm ? (
|
||
<div className="space-y-4">
|
||
<p className="text-xs font-bold text-medical-gray-500 mb-2">انتخاب از بین آدرسهای ذخیرهشده شما:</p>
|
||
<div className="grid gap-3">
|
||
{profile.addresses.map((addr) => (
|
||
<div
|
||
key={addr.id}
|
||
onClick={() => 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"
|
||
)}
|
||
>
|
||
<input
|
||
type="radio"
|
||
name="selectedAddress"
|
||
checked={selectedAddressId === addr.id}
|
||
onChange={() => setSelectedAddressId(addr.id)}
|
||
className="mt-1"
|
||
/>
|
||
<div className="flex-1 min-w-0">
|
||
<div className="flex items-center gap-2 mb-1">
|
||
<span className="font-black text-sm text-medical-gray-900">{addr.title}</span>
|
||
{addr.isDefault && (
|
||
<span className="bg-canina-blue/10 text-canina-blue text-[9px] font-black px-2 py-0.5 rounded-full">پیشفرض</span>
|
||
)}
|
||
</div>
|
||
<p className="text-xs text-medical-gray-600 leading-relaxed truncate">{addr.province}، {addr.city}، {addr.detail}</p>
|
||
<p className="text-[10px] text-medical-gray-400 font-bold mt-1">گیرنده: {addr.receptorName} | همراه: {toPersian(addr.phone)}</p>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
<button
|
||
onClick={() => setShowNewAddressForm(true)}
|
||
className="mt-4 flex items-center gap-2 text-canina-blue font-black text-xs hover:underline"
|
||
>
|
||
<PlusCircle className="w-4 h-4" />
|
||
<span>استفاده از آدرس جدید</span>
|
||
</button>
|
||
</div>
|
||
) : (
|
||
<div className="space-y-4">
|
||
{profile.addresses && profile.addresses.length > 0 && (
|
||
<button
|
||
onClick={() => setShowNewAddressForm(false)}
|
||
className="mb-4 text-xs font-bold text-canina-blue hover:underline"
|
||
>
|
||
← بازگشت به آدرسهای ذخیرهشده
|
||
</button>
|
||
)}
|
||
<div className="grid md:grid-cols-2 gap-4 sm:gap-6">
|
||
<div className="space-y-2">
|
||
<label htmlFor="chk-fullname" className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">نام و نام خانوادگی</label>
|
||
<input
|
||
id="chk-fullname"
|
||
name="name"
|
||
type="text"
|
||
autoComplete="name"
|
||
value={manualName}
|
||
onChange={(e) => 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="مثلاً: پارسا آقایی"
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<label htmlFor="chk-tel" className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">شماره تماس</label>
|
||
<input
|
||
id="chk-tel"
|
||
name="tel"
|
||
type="tel"
|
||
autoComplete="tel"
|
||
value={manualPhone}
|
||
onChange={(e) => 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"
|
||
placeholder="۰۹۱۲XXXXXXX"
|
||
/>
|
||
</div>
|
||
<div className="md:col-span-2 space-y-2">
|
||
<label htmlFor="chk-street" className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest block">آدرس دقیق پستی</label>
|
||
<textarea
|
||
id="chk-street"
|
||
name="street-address"
|
||
autoComplete="street-address"
|
||
value={manualAddress}
|
||
onChange={(e) => setManualAddress(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 h-28 sm:h-32"
|
||
placeholder="استان، شهر، خیابان..."
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</section>
|
||
|
||
{/* Step 2: Payment */}
|
||
<section className="bg-white rounded-[2rem] sm:rounded-[3rem] border border-medical-gray-200 p-5 sm:p-10 overflow-hidden relative">
|
||
<div className="absolute top-0 right-0 w-2.5 sm:w-3 h-full bg-medical-gray-900 opacity-20" />
|
||
<div className="flex items-center gap-3 sm:gap-4 mb-6 sm:mb-10">
|
||
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-medical-gray-900 text-white rounded-2xl flex items-center justify-center font-black text-sm sm:text-base">۲</div>
|
||
<h2 className="text-xl sm:text-2xl font-black text-medical-gray-900 italic">شیوه پرداخت</h2>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 sm:gap-4">
|
||
{[
|
||
{ id: 'online', label: 'پرداخت آنلاین', desc: 'کارتهای بانکی عضو شتاب', icon: <CreditCard className="w-5 h-5 sm:w-6 sm:h-6" /> },
|
||
{
|
||
id: 'wallet',
|
||
label: 'اعتبار حساب (کیف پول)',
|
||
desc: `موجودی: ${toPersian((profile.walletBalance || 0).toLocaleString())} تومان`,
|
||
icon: <Wallet className="w-5 h-5 sm:w-6 sm:h-6" />
|
||
},
|
||
{ id: 'cod', label: 'پرداخت در محل', desc: 'فقط تهران', icon: <Truck className="w-5 h-5 sm:w-6 sm:h-6" /> }
|
||
].map((pay) => (
|
||
<div
|
||
key={pay.id}
|
||
onClick={() => setPaymentMethod(pay.id)}
|
||
className={cn("p-4 sm:p-6 rounded-2xl sm:rounded-3xl border-2 cursor-pointer transition-all group flex sm:flex-col items-center sm:items-start gap-4 sm:gap-0", paymentMethod === pay.id ? "border-canina-blue bg-canina-blue/5" : "border-medical-gray-100 hover:border-canina-blue")}
|
||
>
|
||
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-medical-gray-50 rounded-xl sm:rounded-2xl flex items-center justify-center text-medical-gray-400 group-hover:text-canina-blue group-hover:bg-canina-blue/10 sm:mb-4 transition-all flex-shrink-0">
|
||
{pay.icon}
|
||
</div>
|
||
<div className="min-w-0 flex-1">
|
||
<h4 className="font-black text-xs sm:text-sm text-medical-gray-900 mb-0.5 sm:mb-1 truncate">{pay.label}</h4>
|
||
<p className={cn("text-[10px] font-bold truncate", pay.id === 'wallet' ? "text-canina-blue" : "text-medical-gray-400")}>{pay.desc}</p>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
{/* Step 3: Charity Section */}
|
||
<section className="bg-white rounded-[2rem] sm:rounded-[3rem] border border-medical-gray-200 p-5 sm:p-10 overflow-hidden relative">
|
||
<div className="absolute top-0 right-0 w-2.5 sm:w-3 h-full bg-pink-500 opacity-20" />
|
||
<div className="flex items-center justify-between mb-6 sm:mb-8">
|
||
<div className="flex items-center gap-3 sm:gap-4">
|
||
<div className="w-10 h-10 sm:w-12 sm:h-12 bg-pink-500 text-white rounded-2xl flex items-center justify-center flex-shrink-0">
|
||
<Heart className="w-5 h-5 sm:w-6 sm:h-6 fill-white" />
|
||
</div>
|
||
<div>
|
||
<h2 className="text-xl sm:text-2xl font-black text-medical-gray-900 italic">ردپای مهربانی</h2>
|
||
<p className="text-[10px] sm:text-xs font-bold text-pink-500 mt-0.5">سهم شما در حمایت از حیوانات بیپناه</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-pink-50 rounded-[2rem] sm:rounded-[2.5rem] p-5 sm:p-8 border border-pink-100">
|
||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 sm:gap-6">
|
||
<div className="flex-1">
|
||
<h4 className="text-base sm:text-lg font-black text-medical-gray-900 italic mb-1 sm:mb-2">رند کردن مبلغ و کمک به پناهگاه</h4>
|
||
<p className="text-xs sm:text-sm text-medical-gray-600 leading-relaxed font-medium"> مابقی مبلغ تا ۱۰,۰۰۰ تومان بعدی صرف تامین غذا و دارو برای حیوانات بی-سرپرست میشود.</p>
|
||
</div>
|
||
|
||
<button
|
||
onClick={handleCharityToggle}
|
||
className={cn(
|
||
"w-14 h-14 sm:w-20 sm:h-20 rounded-full flex items-center justify-center transition-all shadow-lg self-end sm:self-center",
|
||
useRoundUp ? "bg-pink-500 text-white shadow-pink-200" : "bg-white text-medical-gray-300 border-2 border-medical-gray-100"
|
||
)}
|
||
>
|
||
<PlusCircle className={cn("w-7 h-7 sm:w-10 sm:h-10", useRoundUp ? "rotate-45" : "rotate-0")} />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
{useRoundUp && (
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 10 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
className="mt-6 pt-6 border-t border-pink-100 flex items-center justify-between"
|
||
>
|
||
<span className="text-sm font-black text-pink-600 italic">مبلغ اهدایی شما:</span>
|
||
<span className="text-lg font-black font-vazir text-pink-500">{toPersian(charityDonation.toLocaleString())} تومان</span>
|
||
</motion.div>
|
||
)}
|
||
|
||
<p className="mt-6 text-[10px] text-center text-medical-gray-400 font-bold">«با خرید این محصول، شما هم در درمان یک حیوان پناهگاهی سهیم شدید.»</p>
|
||
</section>
|
||
</div>
|
||
|
||
{/* Sidebar Summary */}
|
||
<div className="lg:col-span-4 space-y-8">
|
||
<div className="bg-medical-gray-900 rounded-[3rem] p-10 text-white shadow-2xl sticky top-32">
|
||
<h3 className="text-2xl font-black italic mb-8 border-b border-white/20 pb-4 text-white">خلاصه سفارش</h3>
|
||
|
||
<div className="space-y-6 mb-10 overflow-y-auto max-h-60 pr-2">
|
||
{items.map((item) => (
|
||
<div key={item.product.id} className="flex justify-between items-start gap-4">
|
||
<div className="flex-1">
|
||
<p className="text-sm font-black text-white leading-tight">{item.product.name}</p>
|
||
<p className="text-[10px] text-white/60 font-bold mt-1">تعداد: {toPersian(item.quantity)} بسته</p>
|
||
</div>
|
||
<span className="text-xs font-mono font-bold font-vazir text-white">{toPersian((item.product.priceValue * item.quantity).toLocaleString())} تومان</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<div className="space-y-4 pt-8 border-t border-white/20">
|
||
<div className="flex justify-between text-sm font-bold text-white/80">
|
||
<span>مجموع اقلام</span>
|
||
<span className="font-vazir text-white">{toPersian(getSubtotal().toLocaleString())} تومان</span>
|
||
</div>
|
||
{isSubscribed && (
|
||
<div className="flex justify-between text-sm font-bold text-green-400">
|
||
<span>تخفیف اشتراک (۵٪-)</span>
|
||
<span className="font-vazir text-green-400">{toPersian(getDiscount().toLocaleString())} تومان</span>
|
||
</div>
|
||
)}
|
||
{charityDonation > 0 && (
|
||
<div className="flex justify-between text-sm font-bold text-pink-400">
|
||
<span>ردپای مهربانی</span>
|
||
<span className="font-vazir text-pink-400">{toPersian(charityDonation.toLocaleString())} تومان+</span>
|
||
</div>
|
||
)}
|
||
<div className="flex justify-between text-sm font-bold text-white/80">
|
||
<span>هزینه ارسال</span>
|
||
<span className="text-green-400 uppercase tracking-widest text-[10px] font-black">رایگان</span>
|
||
</div>
|
||
<div className="flex justify-between items-end pt-4">
|
||
<span className="text-xl font-black italic text-white">مبلغ نهایی</span>
|
||
<div className="text-left">
|
||
<p className="text-3xl font-black text-canina-gold leading-none font-vazir">{toPersian(getTotal().toLocaleString())}</p>
|
||
<p className="text-[10px] text-white/50 font-bold mt-1">تومان</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Discount Code */}
|
||
<div className="mt-8 pt-8 border-t border-white/10">
|
||
<form
|
||
className="flex gap-2"
|
||
onSubmit={async (e) => {
|
||
e.preventDefault();
|
||
const code = (e.currentTarget.elements.namedItem('coupon') as HTMLInputElement).value;
|
||
const success = await useCartStore.getState().applyCoupon(code);
|
||
if (success) {
|
||
toast.success("تبریک! کد تخفیف با موفقیت اعمال شد.");
|
||
} else {
|
||
toast.error("کد تخفیف معتبر نیست.");
|
||
}
|
||
}}
|
||
>
|
||
<input
|
||
id="chk-coupon"
|
||
name="coupon"
|
||
type="text"
|
||
autoComplete="off"
|
||
placeholder="کد تخفیف (مثلاً: CANINO2024)"
|
||
className="flex-1 bg-white/5 border border-white/10 rounded-xl px-4 py-3 text-xs focus:ring-1 focus:ring-canina-blue outline-none"
|
||
/>
|
||
<button type="submit" className="bg-white/10 px-4 py-3 rounded-xl text-[10px] font-black uppercase tracking-widest hover:bg-white/20 transition-all">اعمال</button>
|
||
</form>
|
||
</div>
|
||
|
||
{/* Refill Estimates */}
|
||
<div className="mt-10 p-6 bg-white/5 rounded-3xl border border-white/10 space-y-4">
|
||
<div className="flex items-center gap-3">
|
||
<Clock className="w-5 h-5 text-canina-blue" />
|
||
<h4 className="text-xs font-black uppercase tracking-widest font-vazir">تخمین اتمام مصرف (هوش مصنوعی)</h4>
|
||
</div>
|
||
<div className="space-y-2">
|
||
{items.map(item => {
|
||
const activePet = usePetStore.getState().getActivePet();
|
||
// Re-hydrate product to ensure methods like calculateDosage exist from static config
|
||
const staticProduct = PRODUCTS.find(p => p.id === item.product.id || p.artNo === item.product.artNo);
|
||
const calculateFn = staticProduct?.calculateDosage || item.product.calculateDosage;
|
||
|
||
const dose = activePet && typeof calculateFn === 'function'
|
||
? calculateFn(activePet.weight, activePet.age <= 1)
|
||
: { quantity: 1 };
|
||
|
||
const days = Math.floor((staticProduct?.packageSize || item.product.packageSize || 50) / (dose.quantity || 1));
|
||
|
||
return (
|
||
<div key={item.product.id} className="flex items-center justify-between text-[10px] font-medium font-vazir">
|
||
<span className="text-white/40">{item.product.name}</span>
|
||
<span className="text-canina-blue">~ {toPersian(days)} روز دیگر</span>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
|
||
<button
|
||
onClick={handleFinalize}
|
||
disabled={loading}
|
||
className="w-full bg-canina-blue text-white py-6 rounded-2xl font-black text-xl mt-10 hover:scale-[1.02] transition-all shadow-xl shadow-canina-blue/20 flex items-center justify-center gap-3 disabled:opacity-50 disabled:cursor-not-allowed"
|
||
>
|
||
{loading ? (
|
||
<>
|
||
<div className="w-6 h-6 border-4 border-white/20 border-t-white rounded-full animate-spin" />
|
||
در حال ثبت...
|
||
</>
|
||
) : (
|
||
<>
|
||
پرداخت و تکمیل {step === 1 ? 'سفارش' : 'مرحله'}
|
||
<ArrowRight className="w-6 h-6 rotate-180" />
|
||
</>
|
||
)}
|
||
</button>
|
||
|
||
<div className="mt-6 flex items-center justify-center gap-2 text-[10px] font-black text-white/20 tracking-widest uppercase">
|
||
<ShieldCheck className="w-4 h-4" />
|
||
تضمین امنیت تراکنش بانکی
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|