feat(admin): update order management and SMS rules settings UI with lint fixes

This commit is contained in:
parsa aghaei 2026-09-05 15:16:12 +03:30
parent 5e3e3958c5
commit 56e8029fff
2 changed files with 615 additions and 618 deletions

View File

@ -102,6 +102,9 @@ export interface Order {
total?: number;
isRefill?: boolean;
shippingAddress?: string;
refundStatus?: string;
refundedAmount?: number;
refundMethod?: string;
}
export const ORDER_STATUS_MAP: Record<string, { label: string; color: string; icon: React.ElementType }> = {
@ -242,8 +245,12 @@ export default function Orders() {
setRefundModalOrder(order);
const amt = Number(order.totalAmount || order.total || 0);
setRefundAmount(String(amt));
// If order was paid by wallet, default to wallet. If online, let admin choose.
setRefundTarget(order.paymentMethod === 'wallet' ? 'wallet' : 'zibal');
// If order was already refunded to wallet, default/force to gateway
if (order.refundStatus === 'wallet') {
setRefundTarget('zibal');
} else {
setRefundTarget(order.paymentMethod === 'wallet' ? 'wallet' : 'zibal');
}
setRefundReason(`استرداد سفارش #${order.trackingNumber || order.id.slice(0, 8)}`);
updateUrlParams({ modal: 'refund', orderId: order.id });
};
@ -440,16 +447,15 @@ export default function Orders() {
}
const res = await api.post('/payment/admin/refund', {
orderId: refundModalOrder.id,
trackId,
amount: refundAmount ? Number(refundAmount) * 10 : undefined,
tryReverse: true,
description: refundReason || `استرداد وجه سفارش #${refundModalOrder.trackingNumber || refundModalOrder.id.slice(0, 8)}`,
});
if (res.data?.result === 1 || res.data?.status === 1 || res.data?.success) {
toast.success('درخواست استرداد وجه به درگاه زیبال با موفقیت ارسال شد.');
// Also mark order as cancelled
await api.put(`/admin/orders/${refundModalOrder.id}/status`, { status: 'cancelled' });
if (res.data?.result === 1 || res.data?.status === 1 || res.data?.result === 100 || res.data?.success) {
toast.success('درخواست استرداد وجه به درگاه زیبال با موفقیت ارسال شد و وضعیت سفارش به‌روزرسانی شد.');
setRefundModalOrder(null);
await fetchOrders();
} else {
@ -790,6 +796,22 @@ export default function Orders() {
<CreditCard className="w-3 h-3 shrink-0" />
<span className="font-vazir">{payInfo.label}</span>
</span>
{order.refundStatus && (
<span
className={`text-[10px] font-bold font-vazir px-2 py-0.5 rounded-md border flex items-center gap-1 shadow-sm ${
order.refundStatus === 'gateway'
? 'bg-rose-50 text-rose-700 border-rose-200'
: 'bg-amber-50 text-amber-700 border-amber-200'
}`}
>
<Undo2 className="w-3 h-3 shrink-0" />
<span className="font-vazir">
{order.refundStatus === 'gateway'
? 'مسترد به درگاه'
: 'مسترد به کیف‌پول'}
</span>
</span>
)}
{order.isRefill && (
<span className="text-[10px] font-bold font-vazir px-2 py-0.5 rounded-md bg-emerald-50 text-emerald-700 border border-emerald-200 shadow-sm">
تمدید ۵٪-
@ -855,16 +877,16 @@ export default function Orders() {
className="text-gray-600 hover:bg-gray-100"
/>
{(order.status === 'cancelled' || order.status === 'pending_payment') && (
{(order.status === 'cancelled' || order.status === 'pending_payment') && order.refundStatus !== 'gateway' && (
<Button
variant="ghost"
size="xs"
startIcon={Undo2}
onClick={() => openOrderRefundModal(order)}
disabled={isProcessing}
className="text-rose-600 hover:bg-rose-50"
className={`hover:bg-rose-50 ${order.refundStatus === 'wallet' ? 'text-amber-700 hover:bg-amber-50' : 'text-rose-600'}`}
>
استرداد
{order.refundStatus === 'wallet' ? 'استرداد بانکی' : 'استرداد'}
</Button>
)}
</div>
@ -1018,17 +1040,15 @@ export default function Orders() {
{(() => {
let addrText = selectedOrder.address?.fullAddress || selectedOrder.shippingAddress || 'ثبت نشده';
let postCode = selectedOrder.address?.postalCode;
let prov = selectedOrder.address?.province;
let ct = selectedOrder.address?.city;
if (typeof selectedOrder.shippingAddress === 'string' && selectedOrder.shippingAddress.trim().startsWith('{')) {
try {
const parsed = JSON.parse(selectedOrder.shippingAddress);
addrText = parsed.formatted || `${parsed.province || ''}، ${parsed.city || ''}، ${parsed.address || ''}`;
if (parsed.postalCode) postCode = parsed.postalCode;
if (parsed.province) prov = parsed.province;
if (parsed.city) ct = parsed.city;
} catch {}
} catch {
// ignore malformed json
}
}
return (
@ -1115,6 +1135,16 @@ export default function Orders() {
<span>روش پرداخت:</span>
<span className="text-white font-bold">{selectedOrder.paymentMethod === 'wallet' ? 'کیف پول الکترونیک' : 'درگاه پرداخت آنلاین'}</span>
</div>
{selectedOrder.refundStatus && (
<div className="flex justify-between font-bold text-amber-300 pt-1 border-t border-gray-800/80">
<span>وضعیت استرداد وجه:</span>
<span>
{selectedOrder.refundStatus === 'gateway'
? `مسترد به درگاه بانکی (${toPersianDigits(Number(selectedOrder.refundedAmount || selectedOrder.totalAmount || 0).toLocaleString())} تومان)`
: `مسترد به کیف‌پول (${toPersianDigits(Number(selectedOrder.refundedAmount || selectedOrder.totalAmount || 0).toLocaleString())} تومان)`}
</span>
</div>
)}
<div className="flex justify-between text-sm sm:text-base font-black text-amber-400 pt-2 border-t border-gray-800">
<span>مبلغ نهایی پرداخت شده:</span>
<span>{toPersianDigits(Number(selectedOrder.totalAmount || selectedOrder.total || 0).toLocaleString())} تومان</span>
@ -1157,14 +1187,22 @@ export default function Orders() {
{/* Destination selector */}
<div>
<label className="block font-bold text-gray-700 mb-2">روش عودت و بازگشت وجه:</label>
{refundModalOrder.refundStatus === 'wallet' && (
<div className="mb-3 p-3 bg-amber-50 border border-amber-200 rounded-xl text-amber-800 text-[11px] leading-relaxed">
<strong>توجه:</strong> وجه این سفارش قبلاً به کیف پول کاربر عودت داده شده است. در صورت ثبت استرداد درگاه زیبال، مبلغ استرداد به صورت خودکار از موجودی کیف پول کاربر کسر خواهد شد تا از استرداد مضاعف جلوگیری شود.
</div>
)}
<div className="grid grid-cols-2 gap-3">
<button
type="button"
disabled={refundModalOrder.refundStatus === 'wallet'}
onClick={() => setRefundTarget('wallet')}
className={`p-3.5 rounded-2xl border text-right transition-all cursor-pointer flex flex-col gap-1.5 ${
refundTarget === 'wallet'
? 'border-purple-600 bg-purple-50/60 ring-2 ring-purple-600/20'
: 'border-gray-200 hover:border-gray-300'
className={`p-3.5 rounded-2xl border text-right transition-all flex flex-col gap-1.5 ${
refundModalOrder.refundStatus === 'wallet'
? 'opacity-40 cursor-not-allowed bg-gray-50 border-gray-200'
: refundTarget === 'wallet'
? 'border-purple-600 bg-purple-50/60 ring-2 ring-purple-600/20 cursor-pointer'
: 'border-gray-200 hover:border-gray-300 cursor-pointer'
}`}
>
<div className="flex items-center gap-2 font-black text-gray-900">
@ -1172,7 +1210,9 @@ export default function Orders() {
<span>شارژ کیف پول کاربر</span>
</div>
<p className="text-[11px] text-gray-500 font-medium leading-relaxed">
افزایش فوری اعتبار کیف پول جهت خریدهای بعدی مشتری
{refundModalOrder.refundStatus === 'wallet'
? 'قبلاً به کیف پول مسترد شده است'
: 'افزایش فوری اعتبار کیف پول جهت خریدهای بعدی مشتری'}
</p>
</button>

File diff suppressed because it is too large Load Diff