/** * Admin Panel - Payments Queue Logic */ let allTransactions = []; let currentPaymentTab = 'pending'; let filteredTransactions = []; let currentRejectionTxId = null; async function loadTransactions() { const tableBody = document.getElementById('tx-table-body'); if (!tableBody) return; try { const res = await fetch('/api/admin/transactions'); allTransactions = await res.json(); if (allTransactions.length === 0) { tableBody.innerHTML = 'No transactions found.'; updatePaymentCounts(); return; } filterAndDisplayTransactions(); } catch (err) { console.error('Failed to load transactions:', err); tableBody.innerHTML = 'Failed to fetch transactions.'; } } function refreshPayments() { loadTransactions(); if(typeof showToast === 'function') { showToast('success', 'Payments Refreshed', 'Payment queue has been updated'); } } function switchPaymentTab(tabName) { currentPaymentTab = tabName; // Update tab buttons document.querySelectorAll('.payment-tab-btn').forEach(btn => btn.classList.remove('active')); document.getElementById(`tab-${tabName}`).classList.add('active'); // Filter and display transactions filterAndDisplayTransactions(); } function filterAndDisplayTransactions() { // Apply tab filter filteredTransactions = allTransactions.filter(tx => { if (currentPaymentTab === 'pending') return tx.status === 'pending'; if (currentPaymentTab === 'approved') return tx.status === 'approved'; if (currentPaymentTab === 'rejected') return tx.status === 'rejected'; return true; }); // Apply search filter const searchInput = document.getElementById('payment-search'); const searchTerm = searchInput ? searchInput.value.toLowerCase() : ''; if (searchTerm) { filteredTransactions = filteredTransactions.filter(tx => (tx.user || '').toLowerCase().includes(searchTerm) || (tx.phone || '').toLowerCase().includes(searchTerm) || (tx.utr || '').toLowerCase().includes(searchTerm) ); } // Apply amount filter const filterSelect = document.getElementById('payment-filter'); const amountFilter = filterSelect ? filterSelect.value : 'all'; if (amountFilter !== 'all') { filteredTransactions = filteredTransactions.filter(tx => { const amount = parseFloat(tx.amount) || 0; switch (amountFilter) { case '0-500': return amount <= 500; case '500-1000': return amount > 500 && amount <= 1000; case '1000-5000': return amount > 1000 && amount <= 5000; case '5000+': return amount > 5000; default: return true; } }); } // Apply sorting const sortSelect = document.getElementById('payment-sort'); const sortBy = sortSelect ? sortSelect.value : 'recent'; filteredTransactions.sort((a, b) => { switch (sortBy) { case 'newest': case 'recent': return new Date(b.date) - new Date(a.date); case 'oldest': return new Date(a.date) - new Date(b.date); case 'amount-high': return parseFloat(b.amount) - parseFloat(a.amount); case 'amount-low': return parseFloat(a.amount) - parseFloat(b.amount); default: return 0; } }); // Update counts updatePaymentCounts(); // Display transactions renderTransactions(filteredTransactions); } function updatePaymentCounts() { const pendingCount = allTransactions.filter(t => t.status === 'pending').length; const approvedCount = allTransactions.filter(t => t.status === 'approved').length; const rejectedCount = allTransactions.filter(t => t.status === 'rejected').length; const pendingEl = document.getElementById('pending-count'); const approvedEl = document.getElementById('approved-count'); const rejectedEl = document.getElementById('rejected-count'); if (pendingEl) { pendingEl.textContent = pendingCount; pendingEl.style.display = pendingCount > 0 ? 'inline-block' : 'none'; } if (approvedEl) { approvedEl.textContent = approvedCount; approvedEl.style.display = approvedCount > 0 ? 'inline-block' : 'none'; } if (rejectedEl) { rejectedEl.textContent = rejectedCount; rejectedEl.style.display = rejectedCount > 0 ? 'inline-block' : 'none'; } } function renderTransactions(transactions) { const tableBody = document.getElementById('tx-table-body'); if (!tableBody) return; if (transactions.length === 0) { tableBody.innerHTML = `

No ${currentPaymentTab} transactions found

`; return; } tableBody.innerHTML = transactions.map(t => { const statusClass = `status-${t.status.toLowerCase()}`; const actions = t.status === 'pending' ? `
` : `
Complete
`; return ` ${t.date}
${t.user} ${t.phone}
₹${t.amount} ${t.utr} ${t.screenshot ? ` View Proof ` : 'N/A'} ${t.status.toUpperCase()} ${actions} `; }).join(''); } async function showPaymentDetails(txId) { const transaction = allTransactions.find(t => t.tx_id === txId); if (!transaction) return; const modal = document.getElementById('payment-details-modal'); const modalTitle = document.getElementById('modal-title'); const modalBody = document.getElementById('modal-body'); const modalActions = document.getElementById('modal-actions'); if(!modal || !modalTitle || !modalBody || !modalActions) return; modalTitle.textContent = `Payment Details - ${transaction.utr}`; let rejectionCommentHtml = ''; if (transaction.status === 'rejected' && transaction.rejection_comment) { rejectionCommentHtml = `
Rejection Reason:

${transaction.rejection_comment}

`; } modalBody.innerHTML = `
User:
${transaction.user}
${transaction.phone}
Amount:
₹${transaction.amount}
Status:
${transaction.status.toUpperCase()}
Date:
${transaction.date}
${rejectionCommentHtml}
UTR Number:
${transaction.utr}
Screenshot Proof:
${transaction.screenshot ? ` View Payment Proof ` : 'No screenshot provided'}
`; if (transaction.status === 'pending') { modalActions.innerHTML = ` `; } else { modalActions.innerHTML = ''; } modal.style.display = 'flex'; } function closePaymentModal() { const modal = document.getElementById('payment-details-modal'); if (modal) modal.style.display = 'none'; } function showConfirmModal(action) { return new Promise((resolve) => { const modal = document.getElementById('custom-confirm-modal'); const title = document.getElementById('modal-title'); const message = document.getElementById('modal-message'); const confirmBtn = document.getElementById('modal-confirm-btn'); const cancelBtn = document.getElementById('modal-cancel-btn'); const icon = document.getElementById('modal-icon'); const iconContainer = document.getElementById('modal-icon-container'); if (!modal) { resolve(confirm(`Are you sure you want to ${action} this transaction?`)); return; } if (action === 'approve') { title.innerText = 'Approve Payment?'; message.innerHTML = 'This will securely add the
requested funds to the user profile.'; icon.className = 'fa-solid fa-check-circle'; iconContainer.style.color = '#00b894'; confirmBtn.style.background = '#00b894'; confirmBtn.style.boxShadow = '0 10px 25px rgba(0, 184, 148, 0.3)'; confirmBtn.innerText = 'Yes, Approve'; } else { title.innerText = 'Reject Payment?'; message.innerHTML = 'This request will be permanently
declined and user will not get funds.'; icon.className = 'fa-solid fa-triangle-exclamation'; iconContainer.style.color = '#ff7675'; confirmBtn.style.background = '#ff7675'; confirmBtn.style.boxShadow = '0 10px 25px rgba(255, 118, 117, 0.3)'; confirmBtn.innerText = 'Yes, Reject'; } modal.style.display = 'flex'; const cleanup = () => { confirmBtn.onclick = null; cancelBtn.onclick = null; modal.style.display = 'none'; }; confirmBtn.onclick = () => { cleanup(); resolve(true); }; cancelBtn.onclick = () => { cleanup(); resolve(false); }; }); } function showRejectionCommentModal(txId) { currentRejectionTxId = txId; const modal = document.getElementById('rejection-comment-modal'); const commentInput = document.getElementById('rejection-comment'); if(modal) modal.style.display = 'flex'; if(commentInput) { commentInput.value = ''; commentInput.focus(); } } function closeRejectionModal() { const modal = document.getElementById('rejection-comment-modal'); if (modal) modal.style.display = 'none'; currentRejectionTxId = null; } async function confirmRejectionWithComment() { const commentInput = document.getElementById('rejection-comment'); const errorElement = document.getElementById('rejection-error'); if(!commentInput || !errorElement) return; const comment = commentInput.value.trim(); if (!comment) { errorElement.style.display = 'block'; commentInput.style.borderColor = '#ff7675'; commentInput.style.boxShadow = '0 0 0 3px rgba(255, 118, 117, 0.1)'; commentInput.classList.add('shake-animation'); setTimeout(() => commentInput.classList.remove('shake-animation'), 500); commentInput.focus(); return; } if (!currentRejectionTxId) { alert('No transaction selected for rejection.'); return; } errorElement.style.display = 'none'; commentInput.style.borderColor = 'var(--border-color)'; commentInput.style.boxShadow = 'none'; try { const res = await fetch('/api/admin/verify-payment', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tx_id: currentRejectionTxId, action: 'reject', comment: comment }) }); const data = await res.json(); if (data.success) { closeRejectionModal(); loadTransactions(); if(typeof showToast === 'function') { showToast('success', 'Payment Rejected', 'Payment has been rejected with your comment.'); } } else { alert(data.error || 'Rejection failed'); } } catch (err) { console.error('Rejection error:', err); alert('A network error occurred.'); } } async function verifyTransaction(tx_id, action) { if (action === 'reject') { showRejectionCommentModal(tx_id); return; } const isConfirmed = await showConfirmModal(action); if (!isConfirmed) return; try { const res = await fetch('/api/admin/verify-payment', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tx_id, action }) }); const data = await res.json(); if (data.success) { loadTransactions(); } else { alert(data.error || 'Verification failed'); } } catch (err) { console.error('Verification error:', err); alert('A network error occurred.'); } } function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } document.addEventListener('DOMContentLoaded', function() { const searchInput = document.getElementById('payment-search'); const filterSelect = document.getElementById('payment-filter'); const sortSelect = document.getElementById('payment-sort'); if (searchInput) searchInput.addEventListener('input', debounce(filterAndDisplayTransactions, 300)); if (filterSelect) filterSelect.addEventListener('change', filterAndDisplayTransactions); if (sortSelect) sortSelect.addEventListener('change', filterAndDisplayTransactions); });