-
Order: ${g.sort_order}
+
Order: ${g.sort_order}
+
💳 —
@@ -2798,6 +2872,16 @@ async function loadGames() {
`).join('');
// Store for edit
window._gamesData = games;
+ // Load credit totals for each game
+ games.forEach(g => {
+ fetch('/api/platforms.php?action=credits_list&platform_id=' + g.id).then(r=>r.json()).then(d=>{
+ const el = document.getElementById('credit-total-' + g.id);
+ if (el && d.success) {
+ const t = d.total||0;
+ el.textContent = '💳 ' + (t%1===0 ? t.toLocaleString() : parseFloat(t).toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2}));
+ }
+ }).catch(()=>{});
+ });
}
function editGame(id) {
@@ -2822,6 +2906,14 @@ function editGame(id) {
document.getElementById('gf-sort').value = g.sort_order;
document.getElementById('gf-active').value = g.is_active;
document.getElementById('game-form-title').textContent = '✏️ Editing: ' + g.name;
+ document.getElementById('gf-credit-btn').disabled = false;
+ // Load credit total for this platform
+ fetch('/api/platforms.php?action=credits_list&platform_id=' + g.id).then(r=>r.json()).then(d=>{
+ if (d.success) {
+ const t = d.total||0;
+ document.getElementById('gf-credit-total').textContent = t%1===0 ? t.toLocaleString() : parseFloat(t).toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2});
+ }
+ });
document.getElementById('game-form-card').scrollIntoView({behavior:'smooth'});
}
@@ -2844,6 +2936,8 @@ function resetGameForm() {
document.getElementById('gf-color-hex').value = '#f0c040';
document.getElementById('gf-sort').value = '99';
document.getElementById('gf-active').value = '1';
+ document.getElementById('gf-credit-total').textContent = '—';
+ document.getElementById('gf-credit-btn').disabled = true;
document.getElementById('game-form-title').textContent = '➕ Add New Game';
document.getElementById('game-form-alert').className = 'alert';
}
@@ -2894,6 +2988,114 @@ async function deleteGame(id, name) {
else toast(d.error||'Error','err');
}
+// ── CREDIT ACCOUNTING ─────────────────────────────────────────────────────
+let _creditPlatformId = null;
+
+async function openCreditModal() {
+ const id = document.getElementById('gf-id').value;
+ if (!id) return;
+ _creditPlatformId = parseInt(id);
+ const g = (window._gamesData||[]).find(x=>x.id==id);
+ document.getElementById('cm-platform-name').textContent = g ? g.name : ('Platform #' + id);
+ document.getElementById('credit-modal').style.display = 'flex';
+ document.getElementById('cm-date').value = new Date().toISOString().slice(0,10);
+ resetCreditForm();
+ await loadCreditEntries();
+}
+
+function closeCreditModal() {
+ document.getElementById('credit-modal').style.display = 'none';
+ _creditPlatformId = null;
+}
+
+async function loadCreditEntries() {
+ if (!_creditPlatformId) return;
+ const d = await fetch('/api/platforms.php?action=credits_list&platform_id=' + _creditPlatformId).then(r=>r.json());
+ if (!d.success) { document.getElementById('cm-list').innerHTML='
Failed to load.
'; return; }
+ const total = d.total || 0;
+ document.getElementById('cm-total').textContent = total % 1 === 0 ? total.toLocaleString() : parseFloat(total).toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2});
+ document.getElementById('gf-credit-total').textContent = total % 1 === 0 ? total.toLocaleString() : parseFloat(total).toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2});
+ const list = document.getElementById('cm-list');
+ if (!d.credits.length) { list.innerHTML='
No credit entries yet.
'; return; }
+ list.innerHTML = `
+
+ | DATE |
+ CREDITS |
+ METHOD |
+ NOTES |
+ |
+
+
+ ${d.credits.map(c=>`
+ | ${escHtmlA(c.credit_date)} |
+ ${parseFloat(c.credits_purchased).toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2})} |
+ ${escHtmlA(c.payment_method||'—')} |
+ ${escHtmlA(c.notes||'')} |
+
+
+
+ |
+
`).join('')}
+
`;
+}
+
+function editCreditEntry(id) {
+ const rows = document.querySelectorAll('#cm-list tbody tr');
+ // Re-fetch from last API response stored on rows via data or refetch
+ fetch('/api/platforms.php?action=credits_list&platform_id=' + _creditPlatformId).then(r=>r.json()).then(d=>{
+ if (!d.success) return;
+ const c = d.credits.find(x=>x.id==id);
+ if (!c) return;
+ document.getElementById('cm-entry-id').value = c.id;
+ document.getElementById('cm-credits').value = c.credits_purchased;
+ document.getElementById('cm-date').value = c.credit_date;
+ document.getElementById('cm-method').value = c.payment_method || '';
+ document.getElementById('cm-notes').value = c.notes || '';
+ document.getElementById('cm-form-title').textContent = '✏️ Editing Entry #' + c.id;
+ document.getElementById('cm-credits').focus();
+ });
+}
+
+function resetCreditForm() {
+ document.getElementById('cm-entry-id').value = '';
+ document.getElementById('cm-credits').value = '';
+ document.getElementById('cm-date').value = new Date().toISOString().slice(0,10);
+ document.getElementById('cm-method').value = '';
+ document.getElementById('cm-notes').value = '';
+ document.getElementById('cm-form-title').textContent = '➕ Add Credit Entry';
+ document.getElementById('cm-form-alert').className = 'alert';
+}
+
+async function saveCreditEntry() {
+ const al = document.getElementById('cm-form-alert');
+ const entryId = document.getElementById('cm-entry-id').value;
+ const credits = parseFloat(document.getElementById('cm-credits').value);
+ const date = document.getElementById('cm-date').value;
+ const method = document.getElementById('cm-method').value.trim();
+ const notes = document.getElementById('cm-notes').value.trim();
+ if (!credits || credits <= 0) { showAdminAlert(al,'Credits Purchased must be greater than 0.','error'); return; }
+ if (!date) { showAdminAlert(al,'Date is required.','error'); return; }
+ const action = entryId ? 'credits_update' : 'credits_create';
+ const payload = entryId
+ ? {id:parseInt(entryId),credits_purchased:credits,credit_date:date,payment_method:method,notes}
+ : {platform_id:_creditPlatformId,credits_purchased:credits,credit_date:date,payment_method:method,notes};
+ const d = await apiFetch(action,'POST',payload);
+ if (d.success) {
+ showAdminAlert(al, entryId ? 'Entry updated!' : 'Entry added!', 'success');
+ resetCreditForm();
+ await loadCreditEntries();
+ } else {
+ showAdminAlert(al, d.error||'Error saving entry.','error');
+ }
+}
+
+async function deleteCreditEntry(id) {
+ if (!confirm('Delete this credit entry? This cannot be undone.')) return;
+ const d = await apiFetch('credits_delete','POST',{id});
+ if (d.success) { toast('Entry deleted','ok'); await loadCreditEntries(); }
+ else toast(d.error||'Error','err');
+}
+
// Sync hex input with color picker
document.addEventListener('DOMContentLoaded', function() {
const picker = document.getElementById('gf-color');
diff --git a/api/platforms.php b/api/platforms.php
index 8414d9b..ae19366 100644
--- a/api/platforms.php
+++ b/api/platforms.php
@@ -104,6 +104,72 @@ switch ($action) {
echo json_encode(['success'=>true]);
break;
+ // ── Admin: list credits for a platform ───────────────
+ case 'credits_list':
+ if (!$isAdmin) { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
+ $pid = (int)($_GET['platform_id'] ?? 0);
+ if (!$pid) { echo json_encode(['success'=>false,'error'=>'platform_id required']); exit; }
+ $rows = db()->prepare("SELECT * FROM platform_credits WHERE platform_id=? ORDER BY credit_date DESC, id DESC");
+ $rows->execute([$pid]);
+ $credits = $rows->fetchAll();
+ $total = db()->prepare("SELECT COALESCE(SUM(credits_purchased),0) FROM platform_credits WHERE platform_id=?");
+ $total->execute([$pid]);
+ echo json_encode(['success'=>true,'credits'=>$credits,'total'=>(float)$total->fetchColumn()]);
+ break;
+
+ // ── Admin: add credit entry ───────────────────────────
+ case 'credits_create':
+ if (!$isAdmin || $_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
+ $d = json_decode(file_get_contents('php://input'), true);
+ $pid = (int)($d['platform_id'] ?? 0);
+ $credits = (float)($d['credits_purchased'] ?? 0);
+ $date = $d['credit_date'] ?? date('Y-m-d');
+ $method = substr(trim($d['payment_method'] ?? ''), 0, 100);
+ $notes = trim($d['notes'] ?? '');
+ if (!$pid || $credits <= 0 || !$date) { echo json_encode(['success'=>false,'error'=>'platform_id, credits_purchased, and credit_date are required']); exit; }
+ $stmt = db()->prepare("INSERT INTO platform_credits (platform_id,credits_purchased,credit_date,payment_method,notes) VALUES (?,?,?,?,?)");
+ $stmt->execute([$pid,$credits,$date,$method,$notes]);
+ $newId = db()->lastInsertId();
+ $total = db()->prepare("SELECT COALESCE(SUM(credits_purchased),0) FROM platform_credits WHERE platform_id=?");
+ $total->execute([$pid]);
+ echo json_encode(['success'=>true,'id'=>$newId,'total'=>(float)$total->fetchColumn()]);
+ break;
+
+ // ── Admin: update credit entry ────────────────────────
+ case 'credits_update':
+ if (!$isAdmin || $_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
+ $d = json_decode(file_get_contents('php://input'), true);
+ $id = (int)($d['id'] ?? 0);
+ $credits = (float)($d['credits_purchased'] ?? 0);
+ $date = $d['credit_date'] ?? date('Y-m-d');
+ $method = substr(trim($d['payment_method'] ?? ''), 0, 100);
+ $notes = trim($d['notes'] ?? '');
+ if (!$id || $credits <= 0 || !$date) { echo json_encode(['success'=>false,'error'=>'id, credits_purchased, and credit_date are required']); exit; }
+ db()->prepare("UPDATE platform_credits SET credits_purchased=?,credit_date=?,payment_method=?,notes=? WHERE id=?")
+ ->execute([$credits,$date,$method,$notes,$id]);
+ $row = db()->prepare("SELECT platform_id FROM platform_credits WHERE id=?");
+ $row->execute([$id]);
+ $pid = (int)($row->fetchColumn() ?: 0);
+ $total = db()->prepare("SELECT COALESCE(SUM(credits_purchased),0) FROM platform_credits WHERE platform_id=?");
+ $total->execute([$pid]);
+ echo json_encode(['success'=>true,'total'=>(float)$total->fetchColumn()]);
+ break;
+
+ // ── Admin: delete credit entry ────────────────────────
+ case 'credits_delete':
+ if (!$isAdmin || $_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
+ $d = json_decode(file_get_contents('php://input'), true);
+ $id = (int)($d['id'] ?? 0);
+ if (!$id) { echo json_encode(['success'=>false,'error'=>'ID required']); exit; }
+ $row = db()->prepare("SELECT platform_id FROM platform_credits WHERE id=?");
+ $row->execute([$id]);
+ $pid = (int)($row->fetchColumn() ?: 0);
+ db()->prepare("DELETE FROM platform_credits WHERE id=?")->execute([$id]);
+ $total = db()->prepare("SELECT COALESCE(SUM(credits_purchased),0) FROM platform_credits WHERE platform_id=?");
+ $total->execute([$pid]);
+ echo json_encode(['success'=>true,'total'=>(float)$total->fetchColumn()]);
+ break;
+
default:
echo json_encode(['success'=>false,'error'=>'Unknown action']);
}