fix(app): improve wallet history display, fix order details modal layout and lint errors

This commit is contained in:
parsa aghaei 2026-09-05 15:16:20 +03:30
parent 56e8029fff
commit 2c377cab88
5 changed files with 289 additions and 193 deletions

View File

@ -59,8 +59,8 @@ export default function AnalyticsDeferred({ gtmId, gaId, clarityId }: AnalyticsD
(function (c: any, l: any, a: any, r: any, i: any) {
c[a] =
c[a] ||
function () {
(c[a].q = c[a].q || []).push(arguments);
function (...args: unknown[]) {
(c[a].q = c[a].q || []).push(args);
};
const t = l.createElement(r);
t.async = 1;

View File

@ -386,11 +386,11 @@ export default function OrderDetailsModal({
)}
{/* Content Scrollable */}
<div className="max-h-[60vh] overflow-y-auto px-6 sm:px-10 py-6 space-y-6 custom-scrollbar">
<div className="max-h-[65vh] overflow-y-auto px-6 sm:px-10 py-6 space-y-6 custom-scrollbar">
{/* Summary Stats */}
<div className="grid grid-cols-3 gap-3 sm:gap-4">
<div className="bg-medical-gray-50 p-4 rounded-2xl">
<div className="text-[9px] font-black text-medical-gray-400 uppercase tracking-widest mb-1">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 sm:gap-4">
<div className="bg-medical-gray-50 p-4 rounded-2xl border border-medical-gray-100">
<div className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-1.5">
وضعیت سفارش
</div>
<div className="flex items-center gap-2">
@ -398,155 +398,178 @@ export default function OrderDetailsModal({
<span className="text-xs sm:text-sm font-black text-medical-gray-900">
{getStatusLabel(orderStatus)}
</span>
<h4 className="text-sm font-black text-medical-gray-900 mb-3 flex items-center gap-2">
<ShoppingBag className="w-4 h-4 text-canina-blue" />
سبد اقلام سفارش
</h4>
<div className="space-y-3">
{items.map((item: Record<string, unknown>, idx: number) => {
const prod = item.product as Record<string, unknown> | undefined;
const pName = getProductName(prod);
const pImg = getProductImage(prod);
const pPrice = Number(prod?.priceValue || item.priceValue || 0);
const itemQty = Number(item.quantity || 1);
const pSlug = String(prod?.slug || prod?.id || '');
return (
<div
key={String(prod?.id || idx)}
className="flex items-center justify-between p-4 border border-medical-gray-100 rounded-2xl hover:border-canina-blue transition-all"
>
<div className="flex items-center gap-4">
<div
onClick={() => setZoomedImage(pImg)}
className="w-14 h-14 bg-medical-gray-50 rounded-xl p-1 flex items-center justify-center shrink-0 border border-medical-gray-200 cursor-zoom-in hover:scale-105 transition-transform overflow-hidden"
title="مشاهده تصویر بزرگ"
>
<SafeImage
src={pImg}
alt={pName}
className="w-full h-full"
imgClassName="w-full h-full object-contain"
/>
</div>
<div>
{pSlug ? (
<Link
href={`/shop/${pSlug}`}
onClick={onClose}
className="font-black text-medical-gray-900 hover:text-canina-blue transition-colors text-xs sm:text-sm inline-block font-vazir"
>
{pName}
</Link>
) : (
<h5 className="font-black text-medical-gray-900 text-xs sm:text-sm font-vazir">
{pName}
</h5>
)}
<p className="text-xs text-medical-gray-400 mt-0.5 font-vazir">
{toPersian(itemQty)} عدد × {toPersian(pPrice.toLocaleString())} تومان
</p>
</div>
</div>
<div className="text-left font-black text-canina-blue text-xs sm:text-sm font-vazir">
{toPersian((pPrice * itemQty).toLocaleString())} تومان
</div>
</div>
);
})}
</div>
</div>
{/* Shipping & Payment */}
<div className="grid md:grid-cols-2 gap-6 pt-4 border-t border-medical-gray-100">
<div>
<h4 className="text-sm font-black text-medical-gray-900 mb-3 flex items-center gap-2">
<MapPin className="w-4 h-4 text-canina-blue" />
اطلاعات تحویل و آدرس
</h4>
<div className="bg-medical-gray-50 p-4 rounded-2xl space-y-2 text-xs font-bold text-medical-gray-700">
<div>
<span className="text-medical-gray-400">گیرنده: </span>
{String(shippingAddress.recipientName || shippingAddress.name || 'کاربر')}
</div>
<div>
<span className="text-medical-gray-400">شماره تماس: </span>
<span className="font-mono">{toPersian(String(shippingAddress.phone || shippingAddress.mobile || '-'))}</span>
</div>
<div>
<span className="text-medical-gray-400">نشانی کامل: </span>
{String(shippingAddress.fullAddress || shippingAddress.address || '-')}
</div>
{Boolean(shippingAddress.postalCode) && (
<div>
<span className="text-medical-gray-400">کد پستی: </span>
<span className="font-mono">{toPersian(String(shippingAddress.postalCode))}</span>
</div>
)}
</div>
</div>
<div>
<h4 className="text-sm font-black text-medical-gray-900 mb-3 flex items-center gap-2">
<CreditCard className="w-4 h-4 text-canina-blue" />
اطلاعات و وضعیت پرداخت
</h4>
<div className="bg-medical-gray-50 p-4 rounded-2xl space-y-2.5 text-xs font-bold text-medical-gray-700">
<div className="flex justify-between items-center">
<span className="text-medical-gray-400">شیوه پرداخت:</span>
<span className="text-medical-gray-900">{getPaymentMethodTitle(order.paymentMethod)}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-medical-gray-400">مجموع اقلام:</span>
<span>{toPersian(itemsSubtotal.toLocaleString())} تومان</span>
</div>
{charityAmount > 0 && (
<div className="flex justify-between items-center text-pink-500">
<span>ردپای مهربانی:</span>
<span>{toPersian(charityAmount.toLocaleString())}+ تومان</span>
</div>
)}
<div className="flex justify-between items-center">
<span className="text-medical-gray-400">هزینه ارسال:</span>
<span className="text-green-600">رایگان</span>
</div>
<div className="pt-2 border-t border-medical-gray-200/60 flex justify-between items-center text-sm font-black text-medical-gray-900">
<span>مبلغ نهایی فاکتور:</span>
<span className="text-canina-blue">{toPersian(totalAmount.toLocaleString())} تومان</span>
</div>
</div>
</div>
</div>
</div>
{/* Footer Actions */}
<div className="p-6 sm:px-10 bg-medical-gray-50 border-t border-medical-gray-100 flex flex-col sm:flex-row items-center justify-between gap-4">
<button
onClick={handlePrint}
className="w-full sm:w-auto px-5 py-2.5 bg-white border border-medical-gray-200 text-medical-gray-700 rounded-xl text-xs font-black hover:border-canina-blue hover:text-canina-blue transition-all flex items-center justify-center gap-2 shadow-xs cursor-pointer"
>
<Download className="w-4 h-4" />
<span>چاپ فاکتور رسمی</span>
</button>
<div className="bg-medical-gray-50 p-4 rounded-2xl border border-medical-gray-100">
<div className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-1.5">
کد رهگیری سفارش
</div>
<div className="text-xs sm:text-sm font-mono font-black text-canina-blue">
{toPersian(displayTrackingCode)}
</div>
</div>
<div className="flex items-center gap-3 w-full sm:w-auto">
{isPendingPayment && (
<button
onClick={() => setRetryModalOpen(true)}
className="w-full sm:w-auto px-6 py-2.5 bg-green-600 hover:bg-green-700 text-white rounded-xl text-xs font-black shadow-lg shadow-green-600/20 transition-all flex items-center justify-center gap-2 cursor-pointer"
<div className="bg-medical-gray-50 p-4 rounded-2xl border border-medical-gray-100">
<div className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest mb-1.5">
زمان ثبت سفارش
</div>
<div className="flex items-center gap-1.5 text-xs sm:text-sm font-black text-medical-gray-900">
<Clock className="w-4 h-4 text-medical-gray-400" />
<span>{toPersian(formattedTime)} - {toPersian(formattedDate)}</span>
</div>
</div>
</div>
{/* Items Section */}
<div>
<h4 className="text-sm font-black text-medical-gray-900 mb-3 flex items-center gap-2">
<ShoppingBag className="w-4 h-4 text-canina-blue" />
سبد اقلام سفارش
</h4>
<div className="space-y-3">
{items.map((item: Record<string, unknown>, idx: number) => {
const prod = item.product as Record<string, unknown> | undefined;
const pName = getProductName(prod);
const pImg = getProductImage(prod);
const pPrice = Number(prod?.priceValue || item.priceValue || 0);
const itemQty = Number(item.quantity || 1);
const pSlug = String(prod?.slug || prod?.id || '');
return (
<div
key={String(prod?.id || idx)}
className="flex items-center justify-between p-4 border border-medical-gray-100 rounded-2xl hover:border-canina-blue transition-all"
>
<CreditCard className="w-4 h-4" />
<span>پرداخت سفارش</span>
</button>
<div className="flex items-center gap-4">
<div
onClick={() => setZoomedImage(pImg)}
className="w-14 h-14 bg-medical-gray-50 rounded-xl p-1 flex items-center justify-center shrink-0 border border-medical-gray-200 cursor-zoom-in hover:scale-105 transition-transform overflow-hidden"
title="مشاهده تصویر بزرگ"
>
<SafeImage
src={pImg}
alt={pName}
className="w-full h-full"
imgClassName="w-full h-full object-contain"
/>
</div>
<div>
{pSlug ? (
<Link
href={`/shop/${pSlug}`}
onClick={onClose}
className="font-black text-medical-gray-900 hover:text-canina-blue transition-colors text-xs sm:text-sm inline-block font-vazir"
>
{pName}
</Link>
) : (
<h5 className="font-black text-medical-gray-900 text-xs sm:text-sm font-vazir">
{pName}
</h5>
)}
<p className="text-xs text-medical-gray-400 mt-0.5 font-vazir">
{toPersian(itemQty)} عدد × {toPersian(pPrice.toLocaleString())} تومان
</p>
</div>
</div>
<div className="text-left font-black text-canina-blue text-xs sm:text-sm font-vazir">
{toPersian((pPrice * itemQty).toLocaleString())} تومان
</div>
</div>
);
})}
</div>
</div>
{/* Shipping & Payment */}
<div className="grid md:grid-cols-2 gap-6 pt-4 border-t border-medical-gray-100">
<div>
<h4 className="text-sm font-black text-medical-gray-900 mb-3 flex items-center gap-2">
<MapPin className="w-4 h-4 text-canina-blue" />
اطلاعات تحویل و آدرس
</h4>
<div className="bg-medical-gray-50 p-4 rounded-2xl space-y-2 text-xs font-bold text-medical-gray-700">
<div>
<span className="text-medical-gray-400">گیرنده: </span>
{String(shippingAddress.recipientName || shippingAddress.name || 'کاربر')}
</div>
<div>
<span className="text-medical-gray-400">شماره تماس: </span>
<span className="font-mono">{toPersian(String(shippingAddress.phone || shippingAddress.mobile || '-'))}</span>
</div>
<div>
<span className="text-medical-gray-400">نشانی کامل: </span>
{String(shippingAddress.fullAddress || shippingAddress.address || '-')}
</div>
{Boolean(shippingAddress.postalCode) && (
<div>
<span className="text-medical-gray-400">کد پستی: </span>
<span className="font-mono">{toPersian(String(shippingAddress.postalCode))}</span>
</div>
)}
<button
onClick={onClose}
className="w-full sm:w-auto px-6 py-2.5 bg-medical-gray-900 hover:bg-canina-blue text-white rounded-xl text-xs font-black transition-all cursor-pointer flex items-center justify-center gap-1.5"
>
<span>بازگشت</span>
<X className="w-4 h-4" />
</button>
</div>
</div>
<div>
<h4 className="text-sm font-black text-medical-gray-900 mb-3 flex items-center gap-2">
<CreditCard className="w-4 h-4 text-canina-blue" />
اطلاعات و وضعیت پرداخت
</h4>
<div className="bg-medical-gray-50 p-4 rounded-2xl space-y-2.5 text-xs font-bold text-medical-gray-700">
<div className="flex justify-between items-center">
<span className="text-medical-gray-400">شیوه پرداخت:</span>
<span className="text-medical-gray-900">{getPaymentMethodTitle(order.paymentMethod)}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-medical-gray-400">مجموع اقلام:</span>
<span>{toPersian(itemsSubtotal.toLocaleString())} تومان</span>
</div>
{charityAmount > 0 && (
<div className="flex justify-between items-center text-pink-500">
<span>ردپای مهربانی:</span>
<span>{toPersian(charityAmount.toLocaleString())}+ تومان</span>
</div>
)}
<div className="flex justify-between items-center">
<span className="text-medical-gray-400">هزینه ارسال:</span>
<span className="text-green-600">رایگان</span>
</div>
<div className="pt-2 border-t border-medical-gray-200/60 flex justify-between items-center text-sm font-black text-medical-gray-900">
<span>مبلغ نهایی فاکتور:</span>
<span className="text-canina-blue">{toPersian(totalAmount.toLocaleString())} تومان</span>
</div>
</div>
</div>
</div>
</div>
{/* Footer Actions */}
<div className="p-6 sm:px-10 bg-medical-gray-50 border-t border-medical-gray-100 flex flex-col sm:flex-row items-center justify-between gap-4">
<button
onClick={handlePrint}
className="w-full sm:w-auto px-5 py-2.5 bg-white border border-medical-gray-200 text-medical-gray-700 rounded-xl text-xs font-black hover:border-canina-blue hover:text-canina-blue transition-all flex items-center justify-center gap-2 shadow-xs cursor-pointer"
>
<Download className="w-4 h-4" />
<span>چاپ فاکتور رسمی</span>
</button>
<div className="flex items-center gap-3 w-full sm:w-auto">
{isPendingPayment && (
<button
onClick={() => setRetryModalOpen(true)}
className="w-full sm:w-auto px-6 py-2.5 bg-green-600 hover:bg-green-700 text-white rounded-xl text-xs font-black shadow-lg shadow-green-600/20 transition-all flex items-center justify-center gap-2 cursor-pointer"
>
<CreditCard className="w-4 h-4" />
<span>پرداخت سفارش</span>
</button>
)}
<button
onClick={onClose}
className="w-full sm:w-auto px-6 py-2.5 bg-medical-gray-900 hover:bg-canina-blue text-white rounded-xl text-xs font-black transition-all cursor-pointer flex items-center justify-center gap-1.5"
>
<span>بازگشت</span>
<X className="w-4 h-4" />
</button>
</div>
</div>

View File

@ -55,6 +55,16 @@ export default function ProductImageZoomModal({
};
}, [isOpen, activeImage, resetZoom]);
const navigateImage = useCallback((direction: number) => {
if (!images || images.length <= 1) return;
const currentIndex = images.indexOf(activeImage);
if (currentIndex === -1) return;
let nextIndex = (currentIndex + direction) % images.length;
if (nextIndex < 0) nextIndex = images.length - 1;
onSelectImage(images[nextIndex]);
resetZoom();
}, [images, activeImage, onSelectImage, resetZoom]);
// Keyboard navigation & zoom shortcuts
useEffect(() => {
if (!isOpen) return;
@ -81,7 +91,7 @@ export default function ProductImageZoomModal({
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [isOpen, onClose, images, activeImage, resetZoom]);
}, [isOpen, onClose, navigateImage, resetZoom]);
const handleZoomIn = () => {
setScale((prev) => Math.min(prev + 0.5, 4));
@ -158,15 +168,7 @@ export default function ProductImageZoomModal({
}
};
const navigateImage = (direction: number) => {
if (!images || images.length <= 1) return;
const currentIndex = images.indexOf(activeImage);
if (currentIndex === -1) return;
let nextIndex = (currentIndex + direction) % images.length;
if (nextIndex < 0) nextIndex = images.length - 1;
onSelectImage(images[nextIndex]);
resetZoom();
};
if (!isOpen) return null;

View File

@ -1,7 +1,7 @@
"use client";
import React, { useState, useEffect } from "react";
import { motion, AnimatePresence } from "motion/react";
import { User, ShoppingBag, Wallet, MapPin, LogOut, ChevronRight, Package, Calendar, UserCircle, ShoppingCart, Trash2, Edit2, Phone, Hash, CheckCircle2, ArrowUpCircle, ArrowDownCircle, Info, Clock, CheckCircle, Heart, Sparkles, MessageSquare, Send, Stethoscope, FileText, Lock, Key, Eye, EyeOff, ShieldCheck, Dog, X, Plus, CreditCard, Activity } from "lucide-react";
import { User, ShoppingBag, Wallet, MapPin, LogOut, ChevronRight, Package, Calendar, UserCircle, ShoppingCart, Trash2, Edit2, Phone, Hash, CheckCircle2, ArrowUpCircle, ArrowDownCircle, Info, Clock, CheckCircle, Heart, Sparkles, MessageSquare, Send, Stethoscope, FileText, Lock, Key, Eye, EyeOff, ShieldCheck, Dog, X, Plus, CreditCard, Activity, RotateCcw } from "lucide-react";
import { OrderRowSkeleton } from "./Skeleton";
import { useUserStore, Address } from "../lib/store/userStore";
import { useCartStore } from "../lib/store/cartStore";
@ -987,37 +987,104 @@ export default function UserDashboard() {
<p className="text-medical-gray-400 font-bold">تراکنشی یافت نشد.</p>
</div>
) : (
(profile?.transactions || []).map((trx) => (
<div key={trx.id} className="p-4 sm:p-6 bg-white border border-medical-gray-100 rounded-[1.5rem] sm:rounded-[2rem] flex flex-col sm:flex-row sm:items-center justify-between gap-4 sm:gap-6 hover:shadow-lg hover:border-canina-blue/10 transition-all group">
<div className="flex items-center gap-3 sm:gap-4">
<div className={cn(
"w-10 h-10 sm:w-12 sm:h-12 rounded-xl sm:rounded-2xl flex items-center justify-center transition-colors flex-shrink-0",
trx.type === 'top_up' ? "bg-green-50 text-green-500 group-hover:bg-green-500 group-hover:text-white" : "bg-red-50 text-red-500 group-hover:bg-red-500 group-hover:text-white"
)}>
{trx.type === 'top_up' ? <ArrowUpCircle className="w-5 h-5 sm:w-6 sm:h-6" /> : <ShoppingCart className="w-5 h-5 sm:w-6 sm:h-6" />}
</div>
<div className="min-w-0">
<div className="text-xs sm:text-sm font-black text-medical-gray-900 truncate">{trx.type === 'top_up' ? "افزایش موجودی (شارژ)" : "پرداخت سفارش"}</div>
<div className="text-[10px] font-bold text-medical-gray-400 flex flex-wrap items-center gap-1 sm:gap-2 mt-0.5">
<span className="truncate">کد: {trx.id}</span>
<span className="w-1 h-1 bg-medical-gray-200 rounded-full hidden sm:inline-block" />
<span>{toPersian(new Date(trx.date).toLocaleDateString("fa-IR"))}</span>
</div>
</div>
</div>
<div className="text-right sm:text-left flex sm:flex-col justify-between items-center sm:items-end border-t sm:border-t-0 pt-2 sm:pt-0 border-medical-gray-50">
<div className={cn(
"text-base sm:text-lg font-black italic",
trx.type === 'top_up' ? "text-green-600" : "text-red-500"
)}>
{trx.type === 'top_up' ? "+" : ""}{toPersian(trx.amount.toLocaleString())} <span className="text-xs not-italic">تومان</span>
</div>
<div className="text-[9px] font-black uppercase text-green-500 tracking-widest sm:mt-1">
تایید شده
</div>
</div>
</div>
))
(profile?.transactions || []).map((trx) => {
const desc = trx.description || "";
const isRefund = trx.type === 'refund' || desc.includes('استرداد') || desc.includes('لغو') || desc.includes('عودت');
const isAdmin = desc.includes('مدیر') || desc.includes('دستی');
const isOnlineTopUp = trx.type === 'top_up' && !isRefund && !isAdmin;
const isPurchase = trx.type === 'purchase' || desc.includes('پرداخت سفارش');
let title = "تراکنش کیف پول";
let badgeText = "تراکنش";
let badgeClass = "bg-medical-gray-100 text-medical-gray-600 border-medical-gray-200";
let iconBgClass = "bg-medical-gray-50 text-medical-gray-600 group-hover:bg-medical-gray-700 group-hover:text-white";
let IconComponent = ArrowUpCircle;
let isPositive = trx.type === 'top_up' || trx.type === 'refund';
if (isRefund) {
title = "استرداد وجه سفارش";
badgeText = "استرداد وجه";
badgeClass = "bg-blue-50 text-blue-700 border-blue-200/60";
iconBgClass = "bg-blue-50 text-blue-600 group-hover:bg-blue-600 group-hover:text-white";
IconComponent = RotateCcw;
isPositive = true;
} else if (isAdmin) {
title = isPositive ? "شارژ توسط مدیریت" : "کسر توسط مدیریت";
badgeText = "تغییر توسط پشتیبانی";
badgeClass = "bg-amber-50 text-amber-700 border-amber-200/60";
iconBgClass = "bg-amber-50 text-amber-600 group-hover:bg-amber-600 group-hover:text-white";
IconComponent = ShieldCheck;
} else if (isOnlineTopUp) {
title = "افزایش اعتبار آنلاین (درگاه پرداخت)";
badgeText = "شارژ درگاه";
badgeClass = "bg-emerald-50 text-emerald-700 border-emerald-200/60";
iconBgClass = "bg-emerald-50 text-emerald-600 group-hover:bg-emerald-600 group-hover:text-white";
IconComponent = ArrowUpCircle;
isPositive = true;
} else if (isPurchase) {
title = "پرداخت سفارش از کیف پول";
badgeText = "پرداخت سفارش";
badgeClass = "bg-rose-50 text-rose-700 border-rose-200/60";
iconBgClass = "bg-rose-50 text-rose-600 group-hover:bg-rose-600 group-hover:text-white";
IconComponent = ShoppingCart;
isPositive = false;
}
return (
<div key={trx.id} className="p-4 sm:p-6 bg-white border border-medical-gray-100 rounded-[1.5rem] sm:rounded-[2rem] flex flex-col sm:flex-row sm:items-center justify-between gap-4 sm:gap-6 hover:shadow-lg hover:border-canina-blue/20 transition-all group">
<div className="flex items-start sm:items-center gap-3 sm:gap-4 flex-1 min-w-0">
<div className={cn(
"w-10 h-10 sm:w-12 sm:h-12 rounded-xl sm:rounded-2xl flex items-center justify-center transition-colors flex-shrink-0 mt-0.5 sm:mt-0",
iconBgClass
)}>
<IconComponent className="w-5 h-5 sm:w-6 sm:h-6" />
</div>
<div className="min-w-0 flex-1">
<div className="flex flex-wrap items-center gap-2 mb-1">
<span className="text-xs sm:text-sm font-black text-medical-gray-900">{title}</span>
<span className={cn("text-[10px] px-2.5 py-0.5 rounded-full border font-black", badgeClass)}>
{badgeText}
</span>
</div>
{desc && (
<p className="text-xs font-bold text-medical-gray-700 line-clamp-2 leading-relaxed mb-1.5">
{desc}
</p>
)}
<div className="text-[10px] font-bold text-medical-gray-400 flex flex-wrap items-center gap-x-2.5 gap-y-1">
<span className="font-mono text-medical-gray-500">کد: {trx.id}</span>
{trx.transactionReference && (
<>
<span className="w-1 h-1 bg-medical-gray-200 rounded-full" />
<span className="font-mono text-canina-blue">مرجع/پیگیری: {trx.transactionReference}</span>
</>
)}
<span className="w-1 h-1 bg-medical-gray-200 rounded-full hidden sm:inline-block" />
<span>{toPersian(new Date(trx.date).toLocaleDateString("fa-IR"))}</span>
</div>
</div>
</div>
<div className="text-right sm:text-left flex sm:flex-col justify-between items-center sm:items-end border-t sm:border-t-0 pt-3 sm:pt-0 border-medical-gray-50 flex-shrink-0">
<div className={cn(
"text-base sm:text-lg font-black italic",
isPositive ? "text-emerald-600" : "text-rose-500"
)}>
{isPositive ? "+" : "-"}{toPersian(trx.amount.toLocaleString())} <span className="text-xs not-italic">تومان</span>
</div>
<div className={cn(
"text-[10px] font-black tracking-wide sm:mt-1 px-2 py-0.5 rounded-full",
trx.status === 'failed' ? "bg-rose-50 text-rose-600" :
trx.status === 'pending' ? "bg-amber-50 text-amber-600" :
"bg-emerald-50 text-emerald-600"
)}>
{trx.status === 'failed' ? "ناموفق" : trx.status === 'pending' ? "در انتظار تایید" : "موفق"}
</div>
</div>
</div>
);
})
)}
</div>
</div>

View File

@ -24,10 +24,12 @@ export interface Address {
export interface Transaction {
id: string;
type: 'top_up' | 'purchase';
type: 'top_up' | 'purchase' | 'refund';
amount: number;
date: string;
status: 'success' | 'failed' | 'pending';
description?: string;
transactionReference?: string;
}
interface UserProfile {
@ -237,12 +239,14 @@ export const useUserStore = create<UserStore>()(
const pd = profileData as unknown as Record<string, unknown>;
const backendWallet = Number(profileData.walletBalance || 0);
const backendCharity = Number(profileData.charityDonationTotal || 0);
const backendTransactions = (pd.walletTransactions as Array<{ id: string; type: string; amount: number; createdAt: string; status: string }>)?.map((t) => ({
const backendTransactions = (pd.walletTransactions as Array<{ id: string; type: string; amount: number; createdAt: string; status: string; description?: string; transactionReference?: string }>)?.map((t) => ({
id: t.id,
type: (t.type === 'deposit' ? 'top_up' : 'purchase') as Transaction['type'],
type: (t.type === 'deposit' ? 'top_up' : t.type === 'refund' ? 'refund' : 'purchase') as Transaction['type'],
amount: Number(t.amount),
date: t.createdAt,
status: (t.status === 'completed' ? 'success' : t.status === 'failed' ? 'failed' : 'pending') as Transaction['status']
status: (t.status === 'completed' ? 'success' : t.status === 'failed' ? 'failed' : 'pending') as Transaction['status'],
description: t.description || undefined,
transactionReference: t.transactionReference || undefined
})) || [];
return {