From d8202427aea2b561386084ceeb1d336ca893a115 Mon Sep 17 00:00:00 2001 From: Myron Blair Date: Fri, 5 Jun 2026 22:03:38 +0000 Subject: [PATCH] Add platform credit overview to dashboard New section below pending purchases/cashouts: one square card per active platform showing net credit balance, completed purchase count, and sent cashout count. Loads on page load alongside other dashboard data. Credits turn yellow below 100 and red at/below 0 with a warning. Clicking a card jumps to Game Management and opens that platform's credit modal. Co-Authored-By: Claude Sonnet 4.6 --- admin/index.php | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ api/admin.php | 17 ++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/admin/index.php b/admin/index.php index aa15eea..7dd7c04 100644 --- a/admin/index.php +++ b/admin/index.php @@ -349,6 +349,15 @@ tr:hover td{background:rgba(255,255,255,.015)}
⚡ Pending Cashout Requests
+ + +
+
+ đŸ•šī¸ Platform Credit Overview + +
+
+
@@ -1200,6 +1209,58 @@ async function loadStats() { loadDashPurchases(); loadDashCashouts(); loadPendingSignups(); + loadPlatformStats(); +} + +async function loadPlatformStats() { + const grid = document.getElementById('dash-platform-grid'); + if (!grid) return; + grid.innerHTML = '
Loading...
'; + const d = await apiFetch('platform_stats'); + if (!d.success || !d.platforms.length) { + grid.innerHTML = '
No platforms found.
'; + return; + } + grid.innerHTML = d.platforms.map(p => { + const bal = parseFloat(p.credits_balance); + const balFmt = bal % 1 === 0 ? bal.toLocaleString() : bal.toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2}); + const color = p.color || '#00e5ff'; + const balColor = bal <= 0 ? 'var(--red)' : bal < 100 ? 'var(--yellow)' : 'var(--cyan)'; + const lowBadge = bal <= 0 + ? '
⚠ LOW
' + : (bal < 100 ? '
⚠ LOW
' : ''); + return `
+
+
${escHtmlA(p.name)}
+
Credits
+
${balFmt}
+ ${lowBadge} +
+
+
${p.purchases}
+
PURCH
+
+
+
${p.cashouts}
+
CASH
+
+
+
`; + }).join(''); +} + +function openGameCredits(slug) { + // Switch to Games section and open the credit modal for this platform + showSec('games'); + // Wait for games to load then find and click the matching game's edit button + const tryOpen = () => { + const games = window._gamesData || []; + const g = games.find(x => x.slug === slug); + if (g) { editGame(g.id); setTimeout(() => openCreditModal(), 150); } + }; + if ((window._gamesData||[]).length) { tryOpen(); } + else { loadGames().then(() => tryOpen()); } } // ─── SECTION NAV ─────────────────────────────────────────── diff --git a/api/admin.php b/api/admin.php index 33369df..ff126c4 100644 --- a/api/admin.php +++ b/api/admin.php @@ -68,6 +68,23 @@ switch ($action) { } break; + // ─── PLATFORM STATS ────────────────────────────────────── + case 'platform_stats': + if (!$isAdmin) { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; } + $rows = db()->query(" + SELECT p.id, p.name, p.slug, p.color, + COALESCE(SUM(CASE WHEN pc.type='debit' THEN -pc.credits_purchased ELSE pc.credits_purchased END),0) AS credits_balance, + (SELECT COUNT(*) FROM token_purchases tp WHERE tp.platform_id=p.slug AND tp.status='completed') AS purchases, + (SELECT COUNT(*) FROM cashout_requests cr WHERE cr.platform_id=p.slug AND cr.status IN ('sent','approved')) AS cashouts + FROM platforms p + LEFT JOIN platform_credits pc ON pc.platform_id=p.id + WHERE p.is_deleted=0 AND p.is_active=1 + GROUP BY p.id, p.name, p.slug, p.color + ORDER BY p.sort_order, p.id + ")->fetchAll(); + echo json_encode(['success'=>true,'platforms'=>$rows]); + break; + // ─── PURCHASES ──────────────────────────────────────────── case 'purchases': $status = $_GET['status'] ?? 'pending';