Fix referral list race condition causing entries to flash and disappear

Two concurrent loadAdminReferrals() calls shared the same DOM container,
so whichever fetch resolved last would overwrite the other's result.
Added a request ID counter (_refListReqId) so stale responses are
discarded rather than applied.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 10:05:24 +00:00
parent 8d27290831
commit f96c1b33c0
+6 -1
View File
@@ -2480,6 +2480,7 @@ async function submitPayout(){
// ─── REFERRAL MANAGEMENT ────────────────────────────────── // ─── REFERRAL MANAGEMENT ──────────────────────────────────
let _refTiers = []; let _refTiers = [];
let _refListReqId = 0;
function showRefSection(section) { function showRefSection(section) {
document.getElementById('ref-admin-list').style.display = section==='list'?'block':'none'; document.getElementById('ref-admin-list').style.display = section==='list'?'block':'none';
@@ -2490,6 +2491,7 @@ function showRefSection(section) {
} }
async function loadAdminReferrals(status, btn) { async function loadAdminReferrals(status, btn) {
const reqId = ++_refListReqId;
document.getElementById('ref-admin-list').style.display='block'; document.getElementById('ref-admin-list').style.display='block';
document.getElementById('ref-tiers-section').style.display='none'; document.getElementById('ref-tiers-section').style.display='none';
document.getElementById('ref-shares-section').style.display='none'; document.getElementById('ref-shares-section').style.display='none';
@@ -2497,6 +2499,7 @@ async function loadAdminReferrals(status, btn) {
const el = document.getElementById('ref-admin-list'); const el = document.getElementById('ref-admin-list');
el.innerHTML = '<div style="padding:16px;text-align:center;color:var(--text2)">Loading...</div>'; el.innerHTML = '<div style="padding:16px;text-align:center;color:var(--text2)">Loading...</div>';
const d = await fetch('/api/referrals.php?action=admin_list&status='+status).then(r=>r.json()); const d = await fetch('/api/referrals.php?action=admin_list&status='+status).then(r=>r.json());
if (reqId !== _refListReqId) return; // discard stale response
if (!d.success||!d.referrals.length) { el.innerHTML='<div class="empty" style="padding:24px;text-align:center">No '+status+' referrals.</div>'; return; } if (!d.success||!d.referrals.length) { el.innerHTML='<div class="empty" style="padding:24px;text-align:center">No '+status+' referrals.</div>'; return; }
el.innerHTML = d.referrals.map(r => buildRefCard(r, status)).join(''); el.innerHTML = d.referrals.map(r => buildRefCard(r, status)).join('');
} }
@@ -2525,7 +2528,9 @@ async function resolveReferral(id, status) {
}).then(r=>r.json()); }).then(r=>r.json());
if (d.success) { if (d.success) {
toast(status==='verified' ? 'Verified! '+d.tokens_awarded+' tokens awarded' : 'Denied', status==='verified'?'ok':'err'); toast(status==='verified' ? 'Verified! '+d.tokens_awarded+' tokens awarded' : 'Denied', status==='verified'?'ok':'err');
loadAdminReferrals('pending', document.querySelector('#section-referrals .ftab')); // Reload the pending tab (items resolved out of pending); active tab indicator stays correct
const pendingBtn = document.querySelector('#section-referrals .ftab');
loadAdminReferrals('pending', pendingBtn);
loadStats(); loadStats();
} else toast(d.error||'Error','err'); } else toast(d.error||'Error','err');
} }