// refund.jsx — Refund Management Page for Hibank QRIS BackOffice function RefundPage({ merchants, primary, secondary }) { const TEAL = primary || '#0056D2'; const ORANGE = secondary || '#cf5a27'; const GREEN = '#74b50c'; const RED = '#ed3151'; const AMBER = '#d9920b'; // State const [refunds, setRefunds] = React.useState([ { id: 'REFUND-001', txId: 'TX-260624-8831', mid: 'MRC-00244', name: 'Batik Lestari Pekalongan', customer: 'Budi Santoso', amount: 45000, time: '2026-06-24T09:12:00', reason: 'Double payment by customer', status: 'pending', refNo: '—' }, { id: 'REFUND-002', txId: 'TX-260624-8832', mid: 'MRC-00245', name: 'Toko Bangunan Maju Jaya', customer: 'Siti Rahayu', amount: 125000, time: '2026-06-24T10:15:00', reason: 'Wrong nominal input', status: 'success', refNo: 'REF-RFD-8821921' }, { id: 'REFUND-003', txId: 'TX-260624-8833', mid: 'MRC-00246', name: 'Kopi Senja Nusantara', customer: 'Andi Wijaya', amount: 35000, time: '2026-06-24T10:20:00', reason: 'Product out of stock', status: 'rejected', refNo: '—', rejectReason: 'Transactions settled & items delivered' } ]); const [selectedRefund, setSelectedRefund] = React.useState(null); const [showAddModal, setShowAddModal] = React.useState(false); const [rejectingRefund, setRejectingRefund] = React.useState(null); const [rejectReasonInput, setRejectReasonInput] = React.useState(''); const [toast, setToast] = React.useState(null); // Form states const [formMerchantId, setFormMerchantId] = React.useState(''); const [formTxId, setFormTxId] = React.useState(''); const [formCustomer, setFormCustomer] = React.useState(''); const [formAmount, setFormAmount] = React.useState(''); const [formReason, setFormReason] = React.useState(''); const activeMerchants = merchants ? merchants.filter(m => m.status === 'approved' || m.status === 'active' || m.status === 'pending_l2') : []; const fmtRp = (v) => { if (v === null || v === undefined) return '—'; return 'Rp ' + Number(v).toLocaleString('id-ID'); }; const fmtDate = (iso) => { const d = new Date(iso); return d.toLocaleDateString('id-ID', { day: '2-digit', month: 'short', year: 'numeric' }) + ' ' + d.toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit' }); }; const triggerToast = (msg, kind = 'success') => { setToast({ msg, kind }); setTimeout(() => setToast(null), 3000); }; // Action: Approve refund const handleApproveRefund = (refundId) => { setRefunds(prev => prev.map(r => { if (r.id !== refundId) return r; const randRef = 'REF-RFD-' + Math.floor(1000000 + Math.random() * 9000000); return { ...r, status: 'success', refNo: randRef }; })); triggerToast(`Pengajuan refund ${refundId} berhasil disetujui.`, 'success'); }; // Action: Reject refund const handleRejectRefundSubmit = (e) => { e.preventDefault(); if (!rejectingRefund || !rejectReasonInput.trim()) return; setRefunds(prev => prev.map(r => { if (r.id !== rejectingRefund.id) return r; return { ...r, status: 'rejected', rejectReason: rejectReasonInput.trim() }; })); triggerToast(`Pengajuan refund ${rejectingRefund.id} ditolak.`, 'success'); // Close modal setRejectingRefund(null); setRejectReasonInput(''); }; // Action: Add new refund request const handleAddRefundSubmit = (e) => { e.preventDefault(); if (!formMerchantId || !formTxId || !formCustomer || !formAmount.trim()) return; const m = activeMerchants.find(merchant => merchant.id === formMerchantId); if (!m) return; const newId = 'REFUND-' + Math.floor(100 + Math.random() * 900); const newRefund = { id: newId, txId: formTxId, mid: m.id, name: m.merchantName, customer: formCustomer, amount: Number(formAmount), time: new Date().toISOString(), reason: formReason || 'Permintaan refund merchant', status: 'pending', refNo: '—' }; setRefunds(prev => [newRefund, ...prev]); setShowAddModal(false); triggerToast(`Pengajuan refund ${newId} berhasil ditambahkan.`, 'success'); // Reset Form setFormMerchantId(''); setFormTxId(''); setFormCustomer(''); setFormAmount(''); setFormReason(''); }; // Totals calculations const totalVolume = refunds.filter(r => r.status === 'success').reduce((acc, curr) => acc + curr.amount, 0); const pendingCount = refunds.filter(r => r.status === 'pending').length; const successCount = refunds.filter(r => r.status === 'success').length; const rejectedCount = refunds.filter(r => r.status === 'rejected').length; return (
Kelola, validasi, dan setujui pengembalian dana transaksi QRIS nasabah berdasarkan permohonan merchant.
| ID Refund | ID Transaksi | Nama Merchant | Nama Pelanggan | Nominal Refund | Waktu Diajukan | Status | Aksi |
|---|---|---|---|---|---|---|---|
| {r.id} | {r.txId} |
{r.name}
{r.mid}
|
{r.customer} | {fmtRp(r.amount)} | {fmtDate(r.time)} | {isPending ? 'MENUNGGU' : r.status.toUpperCase()} |
|