canina/frontend/admin-panel/src/components/TransactionReceiptModal.tsx
2026-08-22 17:01:13 +03:30

236 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from 'react';
import { Printer, X, CheckCircle2, ShieldCheck, Copy } from 'lucide-react';
import { toast } from 'react-hot-toast';
import Button from './ui/Button';
export interface TransactionReceiptData {
trackId?: string | number;
refNumber?: string | number;
amountRials?: number | string;
amountTomans?: number | string;
statusText?: string;
orderNumber?: string;
orderId?: string;
cardNumber?: string;
paidAt?: string;
createdAt?: string;
fee?: number | string;
mobile?: string;
description?: string;
psp?: string;
ip?: string;
}
interface TransactionReceiptModalProps {
isOpen: boolean;
onClose: () => void;
data: TransactionReceiptData | null;
}
export default function TransactionReceiptModal({
isOpen,
onClose,
data,
}: TransactionReceiptModalProps) {
if (!isOpen || !data) return null;
const handlePrint = () => {
window.print();
};
const handleCopy = (text: string, label: string) => {
if (!text) return;
navigator.clipboard.writeText(text);
toast.success(`${label} کپی شد`);
};
const rials =
data.amountRials !== undefined
? Number(data.amountRials)
: (Number(data.amountTomans || 0) * 10);
const tomans =
data.amountTomans !== undefined
? Number(data.amountTomans)
: Math.floor(Number(data.amountRials || 0) / 10);
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm print:p-0 print:bg-white">
<div className="bg-white rounded-3xl max-w-sm w-full shadow-2xl border border-gray-100 overflow-hidden print:border-none print:shadow-none font-vazir" dir="rtl">
{/* Modal Header */}
<div className="p-4 border-b border-gray-100 flex items-center justify-between print:hidden">
<h3 className="font-black text-sm text-gray-900 flex items-center gap-1.5">
<ShieldCheck className="w-4 h-4 text-emerald-600" />
<span>رسید تراکنش {data.trackId || ''}</span>
</h3>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-700 font-bold p-1 cursor-pointer"
>
<X className="w-5 h-5" />
</button>
</div>
{/* The Zigzag Receipt Slip (Matching Zibal screenshot exactly) */}
<div className="p-6 space-y-5 bg-white relative">
{/* Top Jagged Border (CSS Jagged effect) */}
<div className="w-full h-3 bg-indigo-50/50 -mt-6 mb-3 flex justify-between overflow-hidden opacity-60">
{Array.from({ length: 24 }).map((_, i) => (
<div
key={i}
className="w-0 h-0 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-t-[8px] border-t-white"
/>
))}
</div>
{/* Amount Display */}
<div className="text-center space-y-1">
<div className="text-2xl font-black text-emerald-600 font-mono">
{rials.toLocaleString('fa-IR')}{' '}
<span className="text-sm font-black text-emerald-700">ریال</span>
</div>
<p className="text-[11px] font-bold text-gray-400 font-mono">
({tomans.toLocaleString('fa-IR')} تومان)
</p>
</div>
{/* Data List (Aligned exactly like Zibal slip) */}
<div className="space-y-2.5 text-xs">
{/* Track ID */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<button
type="button"
onClick={() => handleCopy(String(data.trackId || ''), 'شماره تراکنش')}
className="text-indigo-600 hover:text-indigo-800 flex items-center gap-1 font-mono font-bold cursor-pointer"
title="کپی"
>
<Copy className="w-3 h-3" />
<span>{data.trackId || '-'}</span>
</button>
<span className="text-gray-500 font-bold">شماره تراکنش</span>
</div>
{/* Reference Number */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<button
type="button"
onClick={() => handleCopy(String(data.refNumber || ''), 'شناسه مرجع')}
className="text-indigo-600 hover:text-indigo-800 flex items-center gap-1 font-mono font-bold cursor-pointer"
title="کپی"
>
<Copy className="w-3 h-3" />
<span>{data.refNumber || '-'}</span>
</button>
<span className="text-gray-500 font-bold">شناسه مرجع</span>
</div>
{/* Paid At */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<span className="font-mono text-gray-700 font-bold dir-ltr">
{data.paidAt || data.createdAt || '-'}
</span>
<span className="text-gray-500 font-bold">تاریخ پرداخت</span>
</div>
{/* Status */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<span className="text-emerald-600 font-black flex items-center gap-1">
<span className="w-2 h-2 rounded-full bg-emerald-500 inline-block"></span>
<span>{data.statusText || 'پرداخت موفق'}</span>
</span>
<span className="text-gray-500 font-bold">وضعیت</span>
</div>
{/* Order ID */}
<div className="flex items-center justify-between py-1.5 px-2 bg-gray-50/80 rounded-lg">
<span className="font-mono font-bold text-gray-900">
{data.orderNumber || data.orderId || '-'}
</span>
<span className="text-gray-500 font-bold">شناسه سفارش</span>
</div>
{/* Card Number */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<span className="font-mono text-gray-800 font-bold dir-ltr">
{data.cardNumber || '-'}
</span>
<span className="text-gray-500 font-bold">شماره کارت</span>
</div>
{/* Creation Date */}
<div className="flex items-center justify-between py-1.5 px-2 bg-gray-50/80 rounded-lg">
<span className="font-mono font-bold text-gray-700 dir-ltr">
{data.createdAt || data.paidAt || '-'}
</span>
<span className="text-gray-500 font-bold">تاریخ ایجاد</span>
</div>
{/* Mobile */}
<div className="flex items-center justify-between py-1.5 px-2 bg-gray-50/80 rounded-lg">
<span className="font-mono font-bold text-gray-800 dir-ltr">
{data.mobile || '-'}
</span>
<span className="text-gray-500 font-bold">شماره موبایل</span>
</div>
{/* Description */}
<div className="flex items-center justify-between py-1 border-b border-gray-50">
<span className="text-gray-800 font-bold max-w-[180px] truncate text-left">
{data.description || 'پرداخت آنلاین - پت شاپ کنینا'}
</span>
<span className="text-gray-500 font-bold">توضیحات</span>
</div>
{/* PSP */}
<div className="flex items-center justify-between py-1.5 px-2 bg-gray-50/80 rounded-lg">
<span className="font-bold text-indigo-700 text-[11px]">
{data.psp || 'زیبال / به‌پرداخت ملت'}
</span>
<span className="text-gray-500 font-bold">PSP</span>
</div>
{/* Real IP */}
<div className="flex items-center justify-between py-1">
<button
type="button"
onClick={() => handleCopy(String(data.ip || ''), 'آی‌پی کاربر')}
className="text-indigo-600 hover:text-indigo-800 flex items-center gap-1 font-mono font-bold cursor-pointer dir-ltr"
title="کپی آی‌پی"
>
<Copy className="w-3 h-3" />
<span>{data.ip || '-'}</span>
</button>
<span className="text-gray-500 font-bold">IP</span>
</div>
</div>
{/* Bottom Jagged Border */}
<div className="w-full h-3 bg-indigo-50/50 -mb-6 mt-4 flex justify-between overflow-hidden opacity-60">
{Array.from({ length: 24 }).map((_, i) => (
<div
key={i}
className="w-0 h-0 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-b-[8px] border-b-white"
/>
))}
</div>
</div>
{/* Actions / Print Footer */}
<div className="p-4 bg-gray-50/80 border-t border-gray-100 flex items-center justify-between print:hidden">
<Button variant="outline" size="sm" onClick={onClose}>
بستن
</Button>
<Button
variant="outline"
size="sm"
startIcon={Printer}
onClick={handlePrint}
className="border-gray-300 hover:border-indigo-600 text-indigo-600 hover:bg-indigo-50"
>
چاپ رسید
</Button>
</div>
</div>
</div>
);
}