580 lines
29 KiB
PHP
580 lines
29 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../../includes/auth.php';
|
|
require_once __DIR__ . '/../../includes/config.php';
|
|
require_once __DIR__ . '/../../includes/header.php';
|
|
require_once __DIR__ . '/../../includes/sidebar.php';
|
|
?>
|
|
|
|
<div class="admin-content">
|
|
<style>
|
|
.admin-page-title{font-size:24px;font-weight:600;color:#23282d;margin-bottom:20px;padding-bottom:10px;border-bottom:2px solid #0073aa}
|
|
.admin-content-box{background:#fff;border:1px solid #ddd;border-radius:4px;padding:24px;margin-top:20px;box-shadow:0 1px 3px rgba(0,0,0,.1)}
|
|
.section-wrap{margin-bottom:32px}
|
|
.section-head{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-radius:4px;cursor:pointer;user-select:none;font-weight:700;font-size:14px}
|
|
.section-head .toggle-arrow{font-size:12px;color:#888;transition:transform .2s}
|
|
.section-head.collapsed .toggle-arrow{transform:rotate(-90deg)}
|
|
.sh-live{background:#fff2f2;border:1px solid #f5c6cb;color:#721c24}
|
|
.sh-planned{background:#f0f6ff;border:1px solid #b8d4f5;color:#0c4a8a}
|
|
.sh-ended{background:#f0fff4;border:1px solid #b2dfdb;color:#155724}
|
|
.sh-results{background:#fafafa;border:1px solid #ddd;color:#333}
|
|
.section-body{padding:14px 0}
|
|
.section-badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:700;margin-left:8px}
|
|
.sb-live{background:#dc3545;color:#fff}
|
|
.sb-planned{background:#0073aa;color:#fff}
|
|
.sb-ended{background:#28a745;color:#fff}
|
|
.filters-bar{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px;align-items:center}
|
|
.filters-bar input,.filters-bar select{padding:6px 10px;border:1px solid #ddd;border-radius:4px;font-size:12px}
|
|
.filters-bar button{padding:6px 12px;background:#6c757d;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px}
|
|
.filters-bar button:hover{background:#5a6268}
|
|
.matches-table{width:100%;border-collapse:collapse}
|
|
.matches-table thead{background:#f5f5f5;border-bottom:2px solid #ddd}
|
|
.matches-table th{padding:9px 11px;text-align:left;font-weight:600;color:#333;font-size:12px;cursor:pointer;user-select:none}
|
|
.matches-table th:hover{background:#e8e8e8}
|
|
.matches-table td{padding:8px 11px;border-bottom:1px solid #eee;font-size:12px}
|
|
.matches-table tbody tr:hover{background:#f9f9f9}
|
|
.badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;text-transform:uppercase}
|
|
.badge-live{background:#dc3545;color:#fff}
|
|
.badge-end{background:#28a745;color:#fff}
|
|
.badge-planned{background:#0073aa;color:#fff}
|
|
.badge-normal{background:#28a745;color:#fff}
|
|
.badge-disconnect{background:#dc3545;color:#fff}
|
|
.badge-both-disconnect{background:#6f42c1;color:#fff}
|
|
.badge-forfeit{background:#e83e8c;color:#fff}
|
|
.badge-timeout{background:#fd7e14;color:#fff}
|
|
.badge-other{background:#6c757d;color:#fff}
|
|
.no-data{text-align:center;padding:20px;color:#aaa;font-size:12px}
|
|
.loading-row td{text-align:center;padding:20px;color:#666;font-size:12px}
|
|
.total-info{font-size:11px;color:#888;margin-bottom:8px}
|
|
.pagination{display:flex;justify-content:center;gap:5px;margin-top:14px;flex-wrap:wrap}
|
|
.pagination button,.pagination span{padding:5px 10px;border:1px solid #ddd;border-radius:4px;cursor:pointer;background:#fff;font-size:12px}
|
|
.pagination button:hover{background:#0073aa;color:#fff;border-color:#0073aa}
|
|
.pagination .active{background:#0073aa;color:#fff;border-color:#0073aa}
|
|
.error-box{background:#f8d7da;color:#721c24;padding:10px 12px;border-radius:4px;border:1px solid #f5c6cb;font-size:12px;margin-bottom:10px}
|
|
.btn-del{background:#dc3545;color:#fff;border:none;border-radius:3px;padding:3px 9px;font-size:11px;cursor:pointer;line-height:1.4}
|
|
.btn-del:hover{background:#b02a37}
|
|
@media(max-width:768px){
|
|
.matches-table{font-size:11px}
|
|
.matches-table th,.matches-table td{padding:5px 6px}
|
|
.filters-bar{flex-direction:column}
|
|
.filters-bar input,.filters-bar select,.filters-bar button{width:100%}
|
|
}
|
|
</style>
|
|
|
|
<h1 class="admin-page-title">Mecze online</h1>
|
|
|
|
<div class="admin-content-box">
|
|
|
|
<!-- ======================================================= -->
|
|
<!-- SEKCJA 1: AKTYWNE (live) -->
|
|
<!-- ======================================================= -->
|
|
<div class="section-wrap" id="wrap-live">
|
|
<div class="section-head sh-live" onclick="toggleSection('live')">
|
|
<span>Aktywne mecze <span class="section-badge sb-live" id="badge-live">0</span></span>
|
|
<span class="toggle-arrow" id="arrow-live">▼</span>
|
|
</div>
|
|
<div class="section-body" id="body-live">
|
|
<div class="filters-bar">
|
|
<input type="text" id="live-search" placeholder="Szukaj gracza / ID..." oninput="filterSection('live')" style="min-width:180px">
|
|
<select id="live-disc" onchange="filterSection('live')">
|
|
<option value="">Wszystkie dyscypliny</option>
|
|
<option value="ping-pong">Ping-Pong</option>
|
|
<option value="table-football">Pilkarzyki</option>
|
|
<option value="rock-paper-scissors">Kamien, papier, nozyce</option>
|
|
</select>
|
|
<button onclick="clearFilters('live')">Resetuj</button>
|
|
</div>
|
|
<div id="live-error"></div>
|
|
<div id="live-empty" class="no-data" style="display:none">Brak aktywnych meczow</div>
|
|
<table class="matches-table" id="live-table" style="display:none">
|
|
<thead><tr>
|
|
<th>#</th><th>Dyscyplina</th><th>Gracz 1</th><th>Gracz 2</th><th>Wynik</th><th>Rozpoczeto</th>
|
|
</tr></thead>
|
|
<tbody id="live-tbody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ======================================================= -->
|
|
<!-- SEKCJA 2: ZAPLANOWANE (planned) -->
|
|
<!-- ======================================================= -->
|
|
<div class="section-wrap" id="wrap-planned">
|
|
<div class="section-head sh-planned" onclick="toggleSection('planned')">
|
|
<span>Zaplanowane mecze <span class="section-badge sb-planned" id="badge-planned">0</span></span>
|
|
<span class="toggle-arrow" id="arrow-planned">▼</span>
|
|
</div>
|
|
<div class="section-body" id="body-planned">
|
|
<div class="filters-bar">
|
|
<input type="text" id="planned-search" placeholder="Szukaj gracza / ID..." oninput="filterSection('planned')" style="min-width:180px">
|
|
<select id="planned-disc" onchange="filterSection('planned')">
|
|
<option value="">Wszystkie dyscypliny</option>
|
|
<option value="ping-pong">Ping-Pong</option>
|
|
<option value="table-football">Pilkarzyki</option>
|
|
<option value="rock-paper-scissors">Kamien, papier, nozyce</option>
|
|
</select>
|
|
<button onclick="clearFilters('planned')">Resetuj</button>
|
|
</div>
|
|
<div id="planned-error"></div>
|
|
<div id="planned-empty" class="no-data" style="display:none">Brak zaplanowanych meczow</div>
|
|
<table class="matches-table" id="planned-table" style="display:none">
|
|
<thead><tr>
|
|
<th>#</th><th>Dyscyplina</th><th>Gracz 1</th><th>Gracz 2</th><th>Wynik</th><th>Rozpoczeto</th>
|
|
</tr></thead>
|
|
<tbody id="planned-tbody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ======================================================= -->
|
|
<!-- SEKCJA 3: ZAKONCZONE SESJE (end, z tabeli matches) -->
|
|
<!-- ======================================================= -->
|
|
<div class="section-wrap" id="wrap-ended">
|
|
<div class="section-head sh-ended" onclick="toggleSection('ended')">
|
|
<span>Zakonczone sesje <span class="section-badge sb-ended" id="badge-ended">0</span></span>
|
|
<span class="toggle-arrow" id="arrow-ended">▼</span>
|
|
</div>
|
|
<div class="section-body" id="body-ended">
|
|
<div class="filters-bar">
|
|
<input type="text" id="ended-search" placeholder="Szukaj gracza / ID..." oninput="filterSection('ended')" style="min-width:180px">
|
|
<select id="ended-disc" onchange="filterSection('ended')">
|
|
<option value="">Wszystkie dyscypliny</option>
|
|
<option value="ping-pong">Ping-Pong</option>
|
|
<option value="table-football">Pilkarzyki</option>
|
|
<option value="rock-paper-scissors">Kamien, papier, nozyce</option>
|
|
</select>
|
|
<input type="date" id="ended-date-from" onchange="filterSection('ended')" title="Data od">
|
|
<input type="date" id="ended-date-to" onchange="filterSection('ended')" title="Data do">
|
|
<button onclick="clearFilters('ended')">Resetuj</button>
|
|
</div>
|
|
<div id="ended-error"></div>
|
|
<div id="ended-empty" class="no-data" style="display:none">Brak zakonczonch sesji</div>
|
|
<table class="matches-table" id="ended-table" style="display:none">
|
|
<thead><tr>
|
|
<th>#</th><th>Dyscyplina</th><th>Gracz 1</th><th>Gracz 2</th><th>Wynik</th><th>Rozpoczeto</th><th>Zakonczone</th><th></th>
|
|
</tr></thead>
|
|
<tbody id="ended-tbody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ======================================================= -->
|
|
<!-- SEKCJA 4: WYNIKI MECZOW (match_results, paginowane) -->
|
|
<!-- ======================================================= -->
|
|
<div class="section-wrap" id="wrap-results">
|
|
<div class="section-head sh-results" onclick="toggleSection('results')">
|
|
<span>Wyniki meczow <span class="section-badge" id="badge-results" style="background:#555;color:#fff">0</span></span>
|
|
<span class="toggle-arrow" id="arrow-results">▼</span>
|
|
</div>
|
|
<div class="section-body" id="body-results">
|
|
<div class="filters-bar">
|
|
<input type="text" id="res-user" placeholder="Szukaj gracza..." oninput="debounceResults()" style="min-width:160px">
|
|
<select id="res-disc" onchange="loadResults(1)">
|
|
<option value="">Wszystkie dyscypliny</option>
|
|
<option value="ping-pong">Ping-Pong</option>
|
|
<option value="table-football">Pilkarzyki</option>
|
|
<option value="rock-paper-scissors">Kamien, papier, nozyce</option>
|
|
</select>
|
|
<select id="res-mode" onchange="loadResults(1)">
|
|
<option value="">Wszystkie tryby</option>
|
|
<option value="1v1">1v1</option>
|
|
</select>
|
|
<select id="res-reason" onchange="loadResults(1)">
|
|
<option value="">Wszystkie zakonczenia</option>
|
|
<option value="normal">Normalny</option>
|
|
<option value="disconnect">Rozlaczenie</option>
|
|
<option value="timeout">Timeout</option>
|
|
</select>
|
|
<input type="date" id="res-date-from" onchange="loadResults(1)" title="Data od">
|
|
<input type="date" id="res-date-to" onchange="loadResults(1)" title="Data do">
|
|
<button onclick="clearFilters('results')">Resetuj</button>
|
|
</div>
|
|
<div id="res-total" class="total-info" style="display:none"></div>
|
|
<div id="res-error"></div>
|
|
<div id="res-empty" class="no-data" style="display:none">Brak wynikow meczow</div>
|
|
<table class="matches-table" id="res-table" style="display:none">
|
|
<thead><tr>
|
|
<th onclick="doSort('id')">#</th>
|
|
<th onclick="doSort('discipline')">Dyscyplina</th>
|
|
<th onclick="doSort('winner_username')">Zwyciezca</th>
|
|
<th onclick="doSort('loser_username')">Przegrany</th>
|
|
<th>Wynik</th>
|
|
<th onclick="doSort('reason')">Zakonczenie</th>
|
|
<th onclick="doSort('ended_at')">Zakonczone</th>
|
|
<th></th>
|
|
</tr></thead>
|
|
<tbody id="res-tbody"></tbody>
|
|
</table>
|
|
<div class="pagination" id="res-pages" style="display:none"></div>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /admin-content-box -->
|
|
</div><!-- /admin-content -->
|
|
|
|
<script>
|
|
// ─── STATE ──────────────────────────────────────────────────────────────────
|
|
var rawData = { live: [], planned: [], ended: [], results: [] };
|
|
var resSort = 'ended_at';
|
|
var resSortOrder = 'DESC';
|
|
var resPage = 1;
|
|
var resDebounce = null;
|
|
var collapsed = { live: false, planned: false, ended: false, results: false };
|
|
|
|
// ─── UTILS ──────────────────────────────────────────────────────────────────
|
|
function esc(s) {
|
|
return String(s == null ? '' : s)
|
|
.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
}
|
|
|
|
function fmt(dt) {
|
|
if (!dt) return '-';
|
|
var d = new Date(dt);
|
|
return d.toLocaleDateString('pl-PL') + ' ' + d.toLocaleTimeString('pl-PL',{hour:'2-digit',minute:'2-digit'});
|
|
}
|
|
|
|
function badgeClass(reason) {
|
|
if (!reason) return 'badge-other';
|
|
switch (reason.toLowerCase()) {
|
|
case 'normal': return 'badge-normal';
|
|
case 'sets': return 'badge-normal';
|
|
case 'disconnect': return 'badge-disconnect';
|
|
case 'both_disconnect': return 'badge-both-disconnect';
|
|
case 'timeout': return 'badge-timeout';
|
|
default:
|
|
if (reason.startsWith('forfeit_')) return 'badge-forfeit';
|
|
return 'badge-other';
|
|
}
|
|
}
|
|
|
|
function val(id) { return document.getElementById(id).value; }
|
|
function show(id) { document.getElementById(id).style.display = ''; }
|
|
function hide(id) { document.getElementById(id).style.display = 'none'; }
|
|
function setText(id, t) { document.getElementById(id).textContent = t; }
|
|
function setHTML(id, h) { document.getElementById(id).innerHTML = h; }
|
|
|
|
// ─── TOGGLE SECTIONS ────────────────────────────────────────────────────────
|
|
function toggleSection(key) {
|
|
collapsed[key] = !collapsed[key];
|
|
var body = document.getElementById('body-' + key);
|
|
var arrow = document.getElementById('arrow-' + key);
|
|
var head = document.querySelector('.sh-' + (key === 'results' ? 'results' : key));
|
|
body.style.display = collapsed[key] ? 'none' : '';
|
|
if (arrow) arrow.style.transform = collapsed[key] ? 'rotate(-90deg)' : '';
|
|
}
|
|
|
|
// ─── CLIENT-SIDE FILTER (live / planned / ended) ────────────────────────────
|
|
function filterSection(key) {
|
|
var search = val(key + '-search').toLowerCase();
|
|
var disc = val(key + '-disc');
|
|
var dfrom = key === 'ended' ? val('ended-date-from') : '';
|
|
var dto = key === 'ended' ? val('ended-date-to') : '';
|
|
|
|
var source = key === 'ended' ? rawData.ended : (key === 'live' ? rawData.live : rawData.planned);
|
|
|
|
var filtered = source.filter(function(m) {
|
|
var u1 = (m.user1_username || '').toLowerCase();
|
|
var u2 = (m.user2_username || '').toLowerCase();
|
|
var id = String(m.id);
|
|
if (search && u1.indexOf(search) < 0 && u2.indexOf(search) < 0 && id.indexOf(search) < 0) return false;
|
|
if (disc && m.discipline !== disc) return false;
|
|
if (dfrom && m.started_at && m.started_at.slice(0,10) < dfrom) return false;
|
|
if (dto && m.started_at && m.started_at.slice(0,10) > dto) return false;
|
|
return true;
|
|
});
|
|
|
|
renderSessionTable(key, filtered);
|
|
}
|
|
|
|
function clearFilters(key) {
|
|
if (key === 'live' || key === 'planned') { document.getElementById(key+'-search').value=''; document.getElementById(key+'-disc').value=''; }
|
|
if (key === 'ended') { ['ended-search','ended-disc','ended-date-from','ended-date-to'].forEach(function(id){document.getElementById(id).value='';}); }
|
|
if (key === 'results'){ ['res-user','res-disc','res-mode','res-reason','res-date-from','res-date-to'].forEach(function(id){document.getElementById(id).value='';}); loadResults(1); return; }
|
|
filterSection(key);
|
|
}
|
|
|
|
// ─── RENDER SESSION TABLE (live / planned / ended) ──────────────────────────
|
|
function renderSessionTable(key, rows) {
|
|
var tbody = document.getElementById(key + '-tbody');
|
|
var table = document.getElementById(key + '-table');
|
|
var empty = document.getElementById(key + '-empty');
|
|
tbody.innerHTML = '';
|
|
if (rows.length === 0) {
|
|
table.style.display = 'none';
|
|
empty.style.display = '';
|
|
return;
|
|
}
|
|
empty.style.display = 'none';
|
|
rows.forEach(function(m) {
|
|
var u1 = esc(m.user1_username || '');
|
|
var u2 = esc(m.user2_username || '');
|
|
var tr = document.createElement('tr');
|
|
var cells =
|
|
'<td><strong>' + esc(m.id) + '</strong></td>' +
|
|
'<td>' + esc(m.discipline) + '</td>' +
|
|
'<td>' + (u1 ? u1 + ' ' : '') + '<small style="color:#999">#' + esc(m.user1_id) + '</small></td>' +
|
|
'<td>' + (u2 ? u2 + ' ' : '') + '<small style="color:#999">#' + esc(m.user2_id) + '</small></td>' +
|
|
'<td><strong>' + esc(m.score || '0:0') + '</strong></td>' +
|
|
'<td style="white-space:nowrap">' + fmt(m.started_at) + '</td>';
|
|
if (key === 'ended') {
|
|
cells += '<td style="white-space:nowrap">' + (m.ended_at ? fmt(m.ended_at) : '-') + '</td>';
|
|
cells += '<td><button class="btn-del" onclick="deleteMatch(' + esc(m.id) + ', this)">Usun</button></td>';
|
|
}
|
|
tr.innerHTML = cells;
|
|
tbody.appendChild(tr);
|
|
});
|
|
table.style.display = 'table';
|
|
}
|
|
|
|
// ─── LOAD ALL (initial + live refresh) ───────────────────────────────────────
|
|
function loadAll() {
|
|
console.log('[matches] loadAll() start');
|
|
// Show loading state in results section
|
|
document.getElementById('res-empty').style.display = 'none';
|
|
document.getElementById('res-table').style.display = 'none';
|
|
document.getElementById('res-error').innerHTML = '';
|
|
document.getElementById('res-total').innerHTML = '<em style="color:#999;font-size:11px">Ladowanie...</em>';
|
|
document.getElementById('res-total').style.display = '';
|
|
|
|
var url = '/api/admin_match_results.php?page=' + resPage + '&limit=25&sortBy=' + resSort + '&sortOrder=' + resSortOrder + buildResParams();
|
|
console.log('[matches] fetch URL:', url);
|
|
|
|
fetch(url)
|
|
.then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); })
|
|
.then(function(data) {
|
|
console.log('[matches] response:', data);
|
|
document.getElementById('res-total').innerHTML = '';
|
|
document.getElementById('res-total').style.display = 'none';
|
|
|
|
if (!data.success) {
|
|
showError('global', data.error || 'Blad');
|
|
showError('res', data.error || 'Blad');
|
|
return;
|
|
}
|
|
|
|
// Store raw data
|
|
rawData.live = data.live || [];
|
|
rawData.planned = data.planned || [];
|
|
rawData.ended = data.ended_sessions || [];
|
|
|
|
// Update badges
|
|
setText('badge-live', rawData.live.length);
|
|
setText('badge-planned', rawData.planned.length);
|
|
setText('badge-ended', rawData.ended.length);
|
|
setText('badge-results', data.pagination ? data.pagination.totalRecords : 0);
|
|
|
|
// Render sessions
|
|
filterSection('live');
|
|
filterSection('planned');
|
|
filterSection('ended');
|
|
|
|
// Render results
|
|
console.log('[matches] results count:', data.results ? data.results.length : 'null', 'total:', data.pagination ? data.pagination.totalRecords : 'no pag');
|
|
renderResults(data.results || [], data.pagination);
|
|
})
|
|
.catch(function(err) {
|
|
console.error('[matches] fetch error:', err);
|
|
document.getElementById('res-total').style.display = 'none';
|
|
showError('global', 'Blad ladowania: ' + err.message);
|
|
showError('res', 'Blad ladowania: ' + err.message);
|
|
});
|
|
}
|
|
|
|
// ─── LOAD RESULTS (paginated, server-side) ───────────────────────────────────
|
|
function buildResParams() {
|
|
var p = '';
|
|
if (val('res-user')) p += '&user=' + encodeURIComponent(val('res-user'));
|
|
if (val('res-disc')) p += '&discipline=' + encodeURIComponent(val('res-disc'));
|
|
if (val('res-mode')) p += '&mode=' + encodeURIComponent(val('res-mode'));
|
|
if (val('res-reason')) p += '&reason=' + encodeURIComponent(val('res-reason'));
|
|
if (val('res-date-from')) p += '&date_from=' + encodeURIComponent(val('res-date-from'));
|
|
if (val('res-date-to')) p += '&date_to=' + encodeURIComponent(val('res-date-to'));
|
|
return p;
|
|
}
|
|
|
|
function loadResults(page) {
|
|
resPage = page || 1;
|
|
var url = '/api/admin_match_results.php?page=' + resPage + '&limit=25&sortBy=' + resSort + '&sortOrder=' + resSortOrder + buildResParams();
|
|
fetch(url)
|
|
.then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); })
|
|
.then(function(data) {
|
|
if (!data.success) { showError('res', data.error || 'Blad'); return; }
|
|
rawData.live = data.live || [];
|
|
rawData.planned = data.planned || [];
|
|
rawData.ended = data.ended_sessions || [];
|
|
setText('badge-live', rawData.live.length);
|
|
setText('badge-planned', rawData.planned.length);
|
|
setText('badge-ended', rawData.ended.length);
|
|
setText('badge-results', data.pagination ? data.pagination.totalRecords : 0);
|
|
filterSection('live');
|
|
filterSection('planned');
|
|
filterSection('ended');
|
|
renderResults(data.results || [], data.pagination);
|
|
})
|
|
.catch(function(err) { showError('res', 'Blad: ' + err.message); });
|
|
}
|
|
|
|
function debounceResults() {
|
|
clearTimeout(resDebounce);
|
|
resDebounce = setTimeout(function() { loadResults(1); }, 400);
|
|
}
|
|
|
|
function doSort(col) {
|
|
if (resSort === col) {
|
|
resSortOrder = resSortOrder === 'DESC' ? 'ASC' : 'DESC';
|
|
} else {
|
|
resSort = col;
|
|
resSortOrder = 'DESC';
|
|
}
|
|
loadResults(1);
|
|
}
|
|
|
|
// ─── RENDER RESULTS TABLE ────────────────────────────────────────────────────
|
|
function renderResults(rows, pag) {
|
|
var tbody = document.getElementById('res-tbody');
|
|
var table = document.getElementById('res-table');
|
|
var empty = document.getElementById('res-empty');
|
|
var total = document.getElementById('res-total');
|
|
var pages = document.getElementById('res-pages');
|
|
|
|
tbody.innerHTML = '';
|
|
pages.innerHTML = '';
|
|
pages.style.display = 'none';
|
|
empty.style.display = 'none';
|
|
table.style.display = 'none';
|
|
total.style.display = 'none';
|
|
|
|
if (pag) {
|
|
total.textContent = 'Lacznie: ' + pag.totalRecords + ' wynikow';
|
|
total.style.display = '';
|
|
setText('badge-results', pag.totalRecords);
|
|
}
|
|
|
|
if (!rows || rows.length === 0) {
|
|
empty.style.display = '';
|
|
return;
|
|
}
|
|
|
|
rows.forEach(function(m) {
|
|
var sets = (m.sets_winner != null && m.sets_loser != null)
|
|
? ' <small>(' + esc(m.sets_winner) + ':' + esc(m.sets_loser) + ' set.)</small>' : '';
|
|
var tr = document.createElement('tr');
|
|
tr.innerHTML =
|
|
'<td><strong>' + esc(m.id) + '</strong></td>' +
|
|
'<td>' + esc(m.discipline) + ' <small style="color:#999">' + esc(m.mode) + '</small></td>' +
|
|
'<td style="color:#155724;font-weight:600">W: ' + esc(m.winner_username) + ' <small style="color:#999">#' + esc(m.winner_user_id) + '</small></td>' +
|
|
'<td style="color:#721c24">L: ' + esc(m.loser_username) + ' <small style="color:#999">#' + esc(m.loser_user_id) + '</small></td>' +
|
|
'<td>' + esc(m.score) + sets + '</td>' +
|
|
'<td><span class="badge ' + badgeClass(m.reason) + '">' + esc(m.reason || '?') + '</span></td>' +
|
|
'<td style="white-space:nowrap">' + fmt(m.ended_at) + '</td>' +
|
|
'<td><button class="btn-del" onclick="deleteResult(' + esc(m.id) + ', this)">Usun</button></td>';
|
|
tbody.appendChild(tr);
|
|
});
|
|
table.style.display = 'table';
|
|
|
|
if (pag && pag.totalPages > 1) {
|
|
renderPagination(pag);
|
|
pages.style.display = 'flex';
|
|
}
|
|
}
|
|
|
|
function renderPagination(p) {
|
|
var c = document.getElementById('res-pages');
|
|
c.innerHTML = '';
|
|
if (p.hasPreviousPage) {
|
|
var b = document.createElement('button');
|
|
b.textContent = '< Poprzednia';
|
|
b.onclick = (function(pg) { return function() { loadResults(pg); }; })(p.currentPage - 1);
|
|
c.appendChild(b);
|
|
}
|
|
var start = Math.max(1, p.currentPage - 2);
|
|
var end = Math.min(p.totalPages, p.currentPage + 2);
|
|
for (var i = start; i <= end; i++) {
|
|
var b2 = document.createElement('button');
|
|
b2.textContent = i;
|
|
if (i === p.currentPage) b2.classList.add('active');
|
|
b2.onclick = (function(_i) { return function() { loadResults(_i); }; })(i);
|
|
c.appendChild(b2);
|
|
}
|
|
if (p.hasNextPage) {
|
|
var b3 = document.createElement('button');
|
|
b3.textContent = 'Nastepna >';
|
|
b3.onclick = (function(pg) { return function() { loadResults(pg); }; })(p.currentPage + 1);
|
|
c.appendChild(b3);
|
|
}
|
|
}
|
|
|
|
// ─── DELETE ──────────────────────────────────────────────────────────────────
|
|
function deleteMatch(id, btn) {
|
|
if (!confirm('Usunac mecz #' + id + ' z bazy?')) return;
|
|
btn.disabled = true;
|
|
btn.textContent = '...';
|
|
var fd = new FormData();
|
|
fd.append('type', 'match');
|
|
fd.append('id', id);
|
|
fetch('/api/admin_delete_match.php', { method: 'POST', body: fd })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(d) {
|
|
if (!d.success) { alert('Blad: ' + (d.error || '?')); btn.disabled = false; btn.textContent = 'Usun'; return; }
|
|
var tr = btn.closest('tr');
|
|
if (tr) tr.remove();
|
|
rawData.ended = rawData.ended.filter(function(m) { return String(m.id) !== String(id); });
|
|
setText('badge-ended', rawData.ended.length);
|
|
})
|
|
.catch(function(e) { alert('Blad: ' + e.message); btn.disabled = false; btn.textContent = 'Usun'; });
|
|
}
|
|
|
|
function deleteResult(id, btn) {
|
|
if (!confirm('Usunac wynik #' + id + ' z bazy?')) return;
|
|
btn.disabled = true;
|
|
btn.textContent = '...';
|
|
var fd = new FormData();
|
|
fd.append('type', 'result');
|
|
fd.append('id', id);
|
|
fetch('/api/admin_delete_match.php', { method: 'POST', body: fd })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(d) {
|
|
if (!d.success) { alert('Blad: ' + (d.error || '?')); btn.disabled = false; btn.textContent = 'Usun'; return; }
|
|
var tr = btn.closest('tr');
|
|
if (tr) tr.remove();
|
|
loadResults(resPage);
|
|
})
|
|
.catch(function(e) { alert('Blad: ' + e.message); btn.disabled = false; btn.textContent = 'Usun'; });
|
|
}
|
|
|
|
// ─── ERRORS ──────────────────────────────────────────────────────────────────
|
|
function showError(key, msg) {
|
|
var id = key === 'global' ? 'live-error' : 'res-error';
|
|
document.getElementById(id).innerHTML = '<div class="error-box">' + esc(msg) + '</div>';
|
|
}
|
|
|
|
// ─── INIT ────────────────────────────────────────────────────────────────────
|
|
console.log('[matches] script zaladowany');
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
console.log('[matches] DOMContentLoaded');
|
|
loadAll();
|
|
// Auto-refresh live section every 10s
|
|
setInterval(function() {
|
|
fetch('/api/admin_match_results.php?page=1&limit=25')
|
|
.then(function(r) { return r.ok ? r.json() : null; })
|
|
.then(function(data) {
|
|
if (!data || !data.success) return;
|
|
rawData.live = data.live || [];
|
|
rawData.planned = data.planned || [];
|
|
rawData.ended = data.ended_sessions || [];
|
|
setText('badge-live', rawData.live.length);
|
|
setText('badge-planned', rawData.planned.length);
|
|
setText('badge-ended', rawData.ended.length);
|
|
filterSection('live');
|
|
filterSection('planned');
|
|
filterSection('ended');
|
|
// odswiezaj tez wyniki jesli jestesmy na stronie 1
|
|
if (resPage === 1) renderResults(data.results || [], data.pagination || {});
|
|
})
|
|
.catch(function() {});
|
|
}, 2000);
|
|
});
|
|
</script>
|
|
|
|
<?php
|
|
require_once __DIR__ . '/../../includes/footer.php';
|
|
?>
|