fix(profile, checkout): redesign profile mobile layout tabs UX and fix wallet deduction API error
This commit is contained in:
parent
16ccd99010
commit
a6ae1cf30a
@ -94,10 +94,13 @@ export default function CheckoutPage() {
|
||||
date: new Date().toISOString(),
|
||||
status: 'success' as const
|
||||
};
|
||||
await updateProfile({
|
||||
walletBalance: newBalance,
|
||||
transactions: [newTransaction, ...(profile.transactions || [])]
|
||||
});
|
||||
useUserStore.setState((state) => ({
|
||||
profile: {
|
||||
...state.profile,
|
||||
walletBalance: newBalance,
|
||||
transactions: [newTransaction, ...(state.profile.transactions || [])]
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// 2. Register the order in the backend
|
||||
@ -110,9 +113,12 @@ export default function CheckoutPage() {
|
||||
|
||||
// 3. Update user charity total
|
||||
if (charityDonation > 0) {
|
||||
await updateProfile({
|
||||
charityDonationTotal: (profile.charityDonationTotal || 0) + charityDonation
|
||||
});
|
||||
useUserStore.setState((state) => ({
|
||||
profile: {
|
||||
...state.profile,
|
||||
charityDonationTotal: (state.profile.charityDonationTotal || 0) + charityDonation
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// 4. Update pet consumptions for the refill logic
|
||||
|
||||
@ -149,26 +149,26 @@ export default function UserDashboard() {
|
||||
</div>
|
||||
|
||||
<div className="grid lg:grid-cols-12 gap-6 sm:gap-8">
|
||||
{/* Sidebar */}
|
||||
<div className="lg:col-span-4 space-y-4 sm:space-y-6 min-w-0">
|
||||
<div className="bg-white rounded-[1.5rem] sm:rounded-[3rem] border border-medical-gray-200 p-4 sm:p-8 shadow-xl">
|
||||
<div className="flex items-center gap-3 sm:gap-4 mb-6 sm:mb-10">
|
||||
<div className="w-12 h-12 sm:w-16 sm:h-16 bg-canina-blue rounded-xl sm:rounded-3xl flex items-center justify-center text-white shadow-lg flex-shrink-0">
|
||||
<User className="w-6 h-6 sm:w-8 sm:h-8" />
|
||||
{/* Top User Info & Navigation Tabs */}
|
||||
<div className="lg:col-span-12">
|
||||
<div className="bg-white rounded-[1.5rem] sm:rounded-[3rem] border border-medical-gray-200 p-4 sm:p-6 shadow-xl flex flex-col md:flex-row items-stretch md:items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-3 sm:gap-4">
|
||||
<div className="w-12 h-12 sm:w-14 sm:h-14 bg-canina-blue rounded-xl sm:rounded-2xl flex items-center justify-center text-white shadow-lg flex-shrink-0">
|
||||
<User className="w-6 h-6 sm:w-7 sm:h-7" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h2 className="text-base sm:text-xl font-black text-medical-gray-900 truncate">{profile.firstName} {profile.lastName}</h2>
|
||||
<p className="text-[11px] sm:text-sm font-bold text-medical-gray-400 truncate">{profile.email}</p>
|
||||
<h2 className="text-base sm:text-lg font-black text-medical-gray-900 truncate">{profile.firstName} {profile.lastName}</h2>
|
||||
<p className="text-[11px] sm:text-xs font-bold text-medical-gray-400 truncate">{profile.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Responsive Tab Bar: Horizontal Scroll on Mobile, Vertical Stack on Desktop */}
|
||||
<div className="flex lg:flex-col gap-2 overflow-x-auto lg:overflow-x-visible pb-2 lg:pb-0 whitespace-nowrap no-scrollbar -mx-2 px-2 lg:mx-0 lg:px-0">
|
||||
{/* Tab Navigation */}
|
||||
<div className="grid grid-cols-2 sm:flex sm:flex-wrap items-center gap-2 pt-2 md:pt-0 border-t md:border-t-0 border-medical-gray-100">
|
||||
{tabs.map(tab => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id as any)}
|
||||
className={`flex items-center gap-2 px-3 sm:px-4 py-2.5 sm:py-4 rounded-xl sm:rounded-2xl transition-all font-bold text-xs sm:text-sm flex-shrink-0 ${activeTab === tab.id ? "bg-canina-blue text-white shadow-lg shadow-canina-blue/20" : "text-medical-gray-600 hover:bg-medical-gray-50 bg-medical-gray-50/50 lg:bg-transparent"}`}
|
||||
className={`flex items-center justify-center gap-2 px-3 sm:px-4 py-2.5 sm:py-3 rounded-xl sm:rounded-2xl transition-all font-bold text-xs sm:text-sm ${activeTab === tab.id ? "bg-canina-blue text-white shadow-lg shadow-canina-blue/20" : "text-medical-gray-600 hover:bg-medical-gray-100 bg-medical-gray-50"}`}
|
||||
>
|
||||
<div className="flex-shrink-0">{tab.icon}</div>
|
||||
<span>{tab.label}</span>
|
||||
@ -176,68 +176,17 @@ export default function UserDashboard() {
|
||||
))}
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex items-center gap-2 px-3 sm:px-4 py-2.5 sm:py-4 rounded-xl sm:rounded-2xl text-red-500 hover:bg-red-50 transition-all font-bold text-xs sm:text-sm flex-shrink-0 lg:mt-4 bg-red-50/30 lg:bg-transparent"
|
||||
className="flex items-center justify-center gap-2 px-3 sm:px-4 py-2.5 sm:py-3 rounded-xl sm:rounded-2xl text-red-500 hover:bg-red-50 transition-all font-bold text-xs sm:text-sm bg-red-50/40 col-span-2 sm:col-span-1"
|
||||
>
|
||||
<LogOut className="w-4 h-4 sm:w-5 sm:h-5 flex-shrink-0" />
|
||||
<span>خروج</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-medical-gray-900 rounded-[1.5rem] sm:rounded-[3rem] p-5 sm:p-8 text-white relative overflow-hidden shadow-2xl">
|
||||
<div className="absolute top-0 left-0 p-6 opacity-20 pointer-events-none">
|
||||
<Wallet className="w-12 h-12 text-canina-blue" />
|
||||
</div>
|
||||
<div className="relative z-10">
|
||||
<div className="text-[10px] font-black uppercase tracking-widest text-white/70 mb-2">موجودی کیف پول</div>
|
||||
<div className="flex items-baseline gap-2 flex-row-reverse justify-end">
|
||||
<div className="text-2xl sm:text-4xl font-black italic">{toPersian((profile.walletBalance ?? 0).toLocaleString())}</div>
|
||||
<span className="text-xs sm:text-sm not-italic font-bold text-white/80">تومان</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => { setActiveTab("wallet"); setIsTopUpModalOpen(true); }}
|
||||
disabled={isTopUpModalOpen}
|
||||
className="mt-5 sm:mt-8 bg-canina-blue w-full py-3 sm:py-4 rounded-xl sm:rounded-2xl font-black hover:bg-white hover:text-canina-blue transition-all shadow-lg shadow-black/20 flex items-center justify-center gap-2 text-xs sm:text-sm"
|
||||
>
|
||||
شارژ کیف پول
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-pink-500 rounded-[1.5rem] sm:rounded-[3rem] p-5 sm:p-8 text-white relative overflow-hidden shadow-2xl shadow-pink-200">
|
||||
<div className="absolute -top-4 -right-4 opacity-10 pointer-events-none">
|
||||
<Heart className="w-32 sm:w-40 h-32 sm:h-40 fill-white" />
|
||||
</div>
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-center gap-2 sm:gap-3 mb-4 sm:mb-6">
|
||||
<Heart className="w-4 h-4 sm:w-5 sm:h-5 fill-white flex-shrink-0" />
|
||||
<h4 className="text-xs sm:text-sm font-black italic tracking-widest">ردپای مهربانی</h4>
|
||||
</div>
|
||||
<div className="space-y-4 sm:space-y-6">
|
||||
<div>
|
||||
<p className="text-[10px] font-bold text-white/70 mb-1">مجموع کمکهای اهدایی</p>
|
||||
<div className="flex items-baseline gap-2 flex-row-reverse justify-end">
|
||||
<span className="text-xl sm:text-3xl font-black italic">{toPersian(profile.charityDonationTotal?.toLocaleString() || "0")}</span>
|
||||
<span className="text-xs font-bold opacity-60">تومان</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 sm:p-4 bg-white/10 rounded-xl sm:rounded-2xl border border-white/20 backdrop-blur-sm">
|
||||
<div className="flex items-start gap-2.5 sm:gap-3">
|
||||
<div className="w-6 h-6 sm:w-8 sm:h-8 bg-white/20 rounded-lg sm:rounded-xl flex items-center justify-center flex-shrink-0 mt-0.5">
|
||||
<Sparkles className="w-3 h-3 sm:w-4 sm:h-4 text-white" />
|
||||
</div>
|
||||
<p className="text-[10px] sm:text-xs font-black leading-relaxed">
|
||||
همکاری شما باعث تامین هزینه <span className="text-xs sm:text-sm text-yellow-300 mx-1">{toPersian(Math.floor((profile.charityDonationTotal || 0) / 15000).toString())} وعده غذا</span> برای حیوانات بیپناه شده است.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="lg:col-span-8 min-w-0">
|
||||
{/* Main Content Area */}
|
||||
<div className="lg:col-span-8 min-w-0 order-1 lg:order-1">
|
||||
<motion.div
|
||||
key={activeTab}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
@ -636,6 +585,60 @@ export default function UserDashboard() {
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar Widgets (Wallet & Charity) */}
|
||||
<div className="lg:col-span-4 space-y-4 sm:space-y-6 min-w-0 order-2 lg:order-2">
|
||||
<div className="bg-medical-gray-900 rounded-[1.5rem] sm:rounded-[3rem] p-5 sm:p-8 text-white relative overflow-hidden shadow-2xl">
|
||||
<div className="absolute top-0 left-0 p-6 opacity-20 pointer-events-none">
|
||||
<Wallet className="w-12 h-12 text-canina-blue" />
|
||||
</div>
|
||||
<div className="relative z-10">
|
||||
<div className="text-[10px] font-black uppercase tracking-widest text-white/70 mb-2">موجودی کیف پول</div>
|
||||
<div className="flex items-baseline gap-2 flex-row-reverse justify-end">
|
||||
<div className="text-2xl sm:text-4xl font-black italic">{toPersian((profile.walletBalance ?? 0).toLocaleString())}</div>
|
||||
<span className="text-xs sm:text-sm not-italic font-bold text-white/80">تومان</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => { setActiveTab("wallet"); setIsTopUpModalOpen(true); }}
|
||||
disabled={isTopUpModalOpen}
|
||||
className="mt-5 sm:mt-8 bg-canina-blue w-full py-3 sm:py-4 rounded-xl sm:rounded-2xl font-black hover:bg-white hover:text-canina-blue transition-all shadow-lg shadow-black/20 flex items-center justify-center gap-2 text-xs sm:text-sm"
|
||||
>
|
||||
شارژ کیف پول
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-pink-500 rounded-[1.5rem] sm:rounded-[3rem] p-5 sm:p-8 text-white relative overflow-hidden shadow-2xl shadow-pink-200">
|
||||
<div className="absolute -top-4 -right-4 opacity-10 pointer-events-none">
|
||||
<Heart className="w-32 sm:w-40 h-32 sm:h-40 fill-white" />
|
||||
</div>
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-center gap-2 sm:gap-3 mb-4 sm:mb-6">
|
||||
<Heart className="w-4 h-4 sm:w-5 sm:h-5 fill-white flex-shrink-0" />
|
||||
<h4 className="text-xs sm:text-sm font-black italic tracking-widest">ردپای مهربانی</h4>
|
||||
</div>
|
||||
<div className="space-y-4 sm:space-y-6">
|
||||
<div>
|
||||
<p className="text-[10px] font-bold text-white/70 mb-1">مجموع کمکهای اهدایی</p>
|
||||
<div className="flex items-baseline gap-2 flex-row-reverse justify-end">
|
||||
<span className="text-xl sm:text-3xl font-black italic">{toPersian(profile.charityDonationTotal?.toLocaleString() || "0")}</span>
|
||||
<span className="text-xs font-bold opacity-60">تومان</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 sm:p-4 bg-white/10 rounded-xl sm:rounded-2xl border border-white/20 backdrop-blur-sm">
|
||||
<div className="flex items-start gap-2.5 sm:gap-3">
|
||||
<div className="w-6 h-6 sm:w-8 sm:h-8 bg-white/20 rounded-lg sm:rounded-xl flex items-center justify-center flex-shrink-0 mt-0.5">
|
||||
<Sparkles className="w-3 h-3 sm:w-4 sm:h-4 text-white" />
|
||||
</div>
|
||||
<p className="text-[10px] sm:text-xs font-black leading-relaxed">
|
||||
همکاری شما باعث تامین هزینه <span className="text-xs sm:text-sm text-yellow-300 mx-1">{toPersian(Math.floor((profile.charityDonationTotal || 0) / 15000).toString())} وعده غذا</span> برای حیوانات بیپناه شده است.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<OrderDetailsModal
|
||||
|
||||
Loading…
Reference in New Issue
Block a user