mirror of
https://github.com/myronblair/tomtomgames
synced 2026-06-30 17:51:08 -05:00
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 <noreply@anthropic.com>
This commit is contained in:
@@ -349,6 +349,15 @@ tr:hover td{background:rgba(255,255,255,.015)}
|
||||
<div class="card-title">⚡ Pending Cashout Requests</div>
|
||||
<div id="dash-cashouts"></div>
|
||||
</div>
|
||||
|
||||
<!-- PLATFORM CREDIT OVERVIEW -->
|
||||
<div style="margin-top:8px">
|
||||
<div style="font-family:'Exo 2',sans-serif;font-weight:700;font-size:16px;color:var(--text);margin-bottom:14px;display:flex;align-items:center;gap:8px">
|
||||
🕹️ Platform Credit Overview
|
||||
<button onclick="loadPlatformStats()" style="background:none;border:none;color:var(--text2);font-size:13px;cursor:pointer;font-family:'Exo 2',sans-serif;font-weight:700;padding:0;margin-left:4px">↻</button>
|
||||
</div>
|
||||
<div id="dash-platform-grid" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PURCHASES -->
|
||||
@@ -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 = '<div style="color:var(--text2);font-size:14px;padding:8px 0">Loading...</div>';
|
||||
const d = await apiFetch('platform_stats');
|
||||
if (!d.success || !d.platforms.length) {
|
||||
grid.innerHTML = '<div style="color:var(--text2);font-size:14px;padding:8px 0">No platforms found.</div>';
|
||||
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
|
||||
? '<div style="font-size:11px;font-weight:700;color:var(--red);letter-spacing:.5px;margin-top:4px">⚠ LOW</div>'
|
||||
: (bal < 100 ? '<div style="font-size:11px;font-weight:700;color:var(--yellow);letter-spacing:.5px;margin-top:4px">⚠ LOW</div>' : '');
|
||||
return `<div onclick="openGameCredits('${escHtmlA(p.slug)}')" style="background:var(--card);border:1px solid var(--border);border-radius:12px;padding:16px 14px;cursor:pointer;transition:all .18s;position:relative;overflow:hidden"
|
||||
onmouseover="this.style.borderColor='${color}44';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='';this.style.transform=''">
|
||||
<div style="position:absolute;top:0;left:0;right:0;height:3px;background:${color};opacity:.7;border-radius:12px 12px 0 0"></div>
|
||||
<div style="font-family:'Exo 2',sans-serif;font-weight:700;font-size:13px;color:var(--text);margin-bottom:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${escHtmlA(p.name)}</div>
|
||||
<div style="font-size:11px;font-weight:700;color:var(--text2);letter-spacing:1px;text-transform:uppercase;margin-bottom:3px">Credits</div>
|
||||
<div style="font-family:'Exo 2',sans-serif;font-weight:900;font-size:22px;color:${balColor};line-height:1">${balFmt}</div>
|
||||
${lowBadge}
|
||||
<div style="display:flex;gap:10px;margin-top:12px;padding-top:10px;border-top:1px solid var(--border)">
|
||||
<div style="flex:1;text-align:center">
|
||||
<div style="font-family:'Exo 2',sans-serif;font-weight:700;font-size:17px;color:var(--gold)">${p.purchases}</div>
|
||||
<div style="font-size:11px;color:var(--text2);font-weight:700;letter-spacing:.5px">PURCH</div>
|
||||
</div>
|
||||
<div style="flex:1;text-align:center;border-left:1px solid var(--border)">
|
||||
<div style="font-family:'Exo 2',sans-serif;font-weight:700;font-size:17px;color:var(--green)">${p.cashouts}</div>
|
||||
<div style="font-size:11px;color:var(--text2);font-weight:700;letter-spacing:.5px">CASH</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}).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 ───────────────────────────────────────────
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user