// disbursement.jsx — Disbursement Management Page for Hibank QRIS BackOffice function DisbursementPage({ merchants, primary, secondary, merchantView = false }) { const TEAL = primary || '#0056D2'; const ORANGE = secondary || '#cf5a27'; const GREEN = '#74b50c'; const RED = '#ed3151'; const AMBER = '#d9920b'; // State const [payouts, setPayouts] = React.useState([ { id: 'DISB-2606-0012', mid: 'MRC-00244', name: 'Batik Lestari Pekalongan', bank: 'Bank Mandiri', account: '1310022419', gross: 8500000, mdr: 59500, net: 8440500, time: '2026-06-24T11:00:00', mode: 'Batch', status: 'success', refNo: 'REF-MND-8812739' }, { id: 'DISB-2606-0013', mid: 'MRC-00245', name: 'Toko Bangunan Maju Jaya', bank: 'Bank BCA', account: '0127889123', gross: 12500000, mdr: 87500, net: 12412500, time: '2026-06-24T11:15:00', mode: 'Batch', status: 'success', refNo: 'REF-BCA-8812741' }, { id: 'DISB-2606-0014', mid: 'MRC-00246', name: 'Kopi Senja Nusantara', bank: 'Bank Hibank', account: '8820145566', gross: 3500000, mdr: 24500, net: 3475500, time: '2026-06-24T11:20:00', mode: 'Instant', status: 'failed', refNo: '—', failReason: 'API Gateway timeout from destination bank' }, { id: 'DISB-2606-0010', mid: 'MRC-00244', name: 'Batik Lestari Pekalongan', bank: 'Bank Mandiri', account: '1310022419', gross: 6200000, mdr: 43400, net: 6156600, time: '2026-06-23T11:00:00', mode: 'Batch', status: 'success', refNo: 'REF-MND-8811091' }, { id: 'DISB-2606-0011', mid: 'MRC-00245', name: 'Toko Bangunan Maju Jaya', bank: 'Bank BCA', account: '0127889123', gross: 10400000, mdr: 72800, net: 10327200, time: '2026-06-23T11:15:00', mode: 'Batch', status: 'success', refNo: 'REF-BCA-8811105' } ]); const [selectedPayout, setSelectedPayout] = React.useState(null); const [showFormModal, setShowFormModal] = React.useState(false); const [toast, setToast] = React.useState(null); // Form states const [formMerchantId, setFormMerchantId] = React.useState(''); const [formAmount, setFormAmount] = React.useState(''); const [formNotes, setFormNotes] = 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: Retry failed payout const handleRetryPayout = (payoutId) => { setPayouts(prev => prev.map(p => { if (p.id !== payoutId) return p; return { ...p, status: 'processing', failReason: null }; })); triggerToast(`Memproses ulang pencairan ${payoutId}...`, 'info'); // Simulate success after 1.5 seconds setTimeout(() => { setPayouts(prev => prev.map(p => { if (p.id !== payoutId) return p; const randRef = 'REF-' + Math.random().toString(36).substring(2, 9).toUpperCase(); return { ...p, status: 'success', refNo: randRef }; })); triggerToast(`Pencairan ${payoutId} sukses dikirim ke bank tujuan.`, 'success'); }, 1500); }; // Action: Submit manual instant payout const handleInstantPayoutSubmit = (e) => { e.preventDefault(); if (!formMerchantId || !formAmount.trim()) return; const m = activeMerchants.find(merchant => merchant.id === formMerchantId); if (!m) return; const gross = Number(formAmount); // MDR simulation: 0.7% standard const mdr = Math.round(gross * 0.007); const net = gross - mdr; const newId = 'DISB-2606-' + Math.floor(1000 + Math.random() * 9000); const newPayout = { id: newId, mid: m.id, name: m.merchantName, bank: m.bankName || 'Bank Hibank', account: m.accountNumber || '8820145566', gross: gross, mdr: mdr, net: net, time: new Date().toISOString(), mode: 'Instant', status: 'processing', refNo: '—' }; setPayouts(prev => [newPayout, ...prev]); setShowFormModal(false); triggerToast(`Mengirim pencairan dana instan untuk ${m.merchantName}...`, 'info'); // Reset Form setFormMerchantId(''); setFormAmount(''); setFormNotes(''); // Simulate success after 2 seconds setTimeout(() => { setPayouts(prev => prev.map(p => { if (p.id !== newId) return p; const randRef = 'REF-' + Math.random().toString(36).substring(2, 9).toUpperCase(); return { ...p, status: 'success', refNo: randRef }; })); triggerToast(`Dana instan ${newId} berhasil ditransfer.`, 'success'); }, 2000); }; // Totals calculations const visiblePayouts = merchantView ? payouts.filter(p => p.status === 'success') : payouts; const totalVolume = visiblePayouts.filter(p => p.status === 'success').reduce((acc, curr) => acc + curr.net, 0); const successCount = visiblePayouts.filter(p => p.status === 'success').length; const processingCount = visiblePayouts.filter(p => p.status === 'processing').length; const failedCount = visiblePayouts.filter(p => p.status === 'failed').length; return (
{merchantView ? 'Riwayat dana settlement QRIS Anda yang berhasil dicairkan ke rekening bank terdaftar.' : 'Kelola transfer hasil transaksi QRIS (settlement) ke rekening bank terdaftar merchant secara instan maupun terjadwal.'}
| ID Batch | Nama Merchant | Rekening Tujuan | Nominal Bersih (Net) | Waktu Disburse | Metode | Status | Aksi |
|---|---|---|---|---|---|---|---|
| {p.id} |
{p.name}
{p.mid}
|
{p.bank}
{p.account}
|
{fmtRp(p.net)}
Kotor: {fmtRp(p.gross)}
|
{fmtDate(p.time)} | {p.mode} | {p.status.toUpperCase()} |
{isFailed && (
)}
|