Files
justice/www/html/gamelog/index.php
T
2026-07-02 04:25:42 +00:00

135 lines
7.8 KiB
PHP

<?php /* gamelog viewer — log รวม 7 มินิเกม + LobbyA/B (อ่านจาก log.php) */ ?>
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Game Log · Justice</title>
<style>
:root { --bg:#0a0e1a; --panel:#121a2e; --line:#23304d; --txt:#cfe0ff; --dim:#7f93bd; --acc:#37e0ff; }
* { box-sizing:border-box; }
body { margin:0; background:var(--bg); color:var(--txt); font:14px/1.5 "Kanit",system-ui,sans-serif; }
header { position:sticky; top:0; z-index:5; background:linear-gradient(180deg,#0d1426,#0a0e1a); border-bottom:1px solid var(--line); padding:10px 14px; }
h1 { margin:0 0 8px; font-size:18px; letter-spacing:.5px; color:#fff; }
h1 small { color:var(--dim); font-size:12px; font-weight:400; }
.tabs { display:flex; flex-wrap:wrap; gap:6px; }
.tab { cursor:pointer; padding:5px 11px; border-radius:8px; border:1px solid var(--line); background:#0f1730; color:var(--dim); font-size:13px; user-select:none; }
.tab:hover { border-color:var(--acc); color:var(--txt); }
.tab.active { background:var(--acc); color:#05101f; border-color:var(--acc); font-weight:700; }
.tab .n { opacity:.7; font-size:11px; margin-left:4px; }
.bar { display:flex; align-items:center; gap:12px; margin-top:8px; font-size:12px; color:var(--dim); flex-wrap:wrap; }
.bar label { cursor:pointer; }
.bar input[type=text]{ background:#0f1730; border:1px solid var(--line); color:var(--txt); border-radius:7px; padding:5px 9px; min-width:160px; }
.btn { cursor:pointer; padding:5px 11px; border-radius:8px; border:1px solid var(--line); background:#0f1730; color:var(--txt); font-size:12px; }
.btn:hover { border-color:var(--acc); }
.btn.danger:hover { border-color:#ff5b6e; color:#ff8a97; }
#wrap { padding:6px 8px 60px; }
table { width:100%; border-collapse:collapse; }
th,td { text-align:left; padding:5px 9px; border-bottom:1px solid #182238; vertical-align:top; }
th { position:sticky; top:0; color:var(--dim); font-weight:600; font-size:12px; background:var(--bg); }
td.t { color:var(--dim); white-space:nowrap; font-variant-numeric:tabular-nums; font-size:12px; }
td.cat span { display:inline-block; padding:1px 8px; border-radius:6px; font-size:12px; font-weight:700; }
td.room { color:var(--dim); font-size:12px; max-width:130px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
td.who { color:#bcd; font-size:13px; }
td.ev { font-weight:600; }
td.detail { color:var(--dim); font-size:12px; word-break:break-word; }
tr.src-client td.ev::after { content:" ·c"; color:#6a7da6; font-size:10px; font-weight:400; }
.empty { text-align:center; color:var(--dim); padding:40px; }
/* cat colors */
.c-LobbyA{background:#1d3a5f;color:#9fd4ff} .c-LobbyB{background:#2a2f63;color:#c4c2ff}
.c-MG1{background:#7a2540;color:#ffc0d4} .c-MG2{background:#2a5f3a;color:#bfffd0}
.c-MG3{background:#5f4a1d;color:#ffe6a0} .c-MG4{background:#1d5f5a;color:#a0fff2}
.c-MG5{background:#3a2a5f;color:#d6c0ff} .c-MG6{background:#5f1d3a;color:#ffb0c8}
.c-MG7{background:#5f3a1d;color:#ffd0a0} .cother{background:#2a3550;color:#9fb0d0}
.ev-leave td.ev, .ev-disconnect td.ev{color:#ff8a97} .ev-join td.ev{color:#7ee787}
.ev-bot-takeover td.ev,.ev-ghost-prune td.ev{color:#ffd166} .ev-error td.ev{color:#ff5b6e}
</style>
</head>
<body>
<header>
<h1>🎮 Game Log <small>— 7 มินิเกม + LobbyA / LobbyB · srv1361159</small></h1>
<div style="margin:0 0 8px;font-size:12px;color:#9fb0d0">
🔎 ค้นหา <b>TC154 / TC156 / TC157 / TC144 / TC158</b> (หรือ ev: preplay-lock · story-gate · vote-gate · lobbyb-gate · mg4-warp)
→ ดูว่าฟีเจอร์ทำงานไหม แล้วอัปเดตผลที่ <a href="/testcase/" style="color:#37e0ff" target="_blank">/testcase/</a>
· เสียง (TC159-164) = ฟังเอง ไม่มีใน log
</div>
<div class="tabs" id="tabs"></div>
<div class="bar">
<label><input type="checkbox" id="auto" checked> auto-refresh 3s</label>
<input type="text" id="search" placeholder="ค้นหา (ชื่อ/ห้อง/event/detail)">
<span id="status"></span>
<span style="flex:1"></span>
<button class="btn" id="reload">รีโหลด</button>
<button class="btn danger" id="clear">ล้าง log</button>
</div>
</header>
<div id="wrap"><div class="empty">กำลังโหลด...</div></div>
<script>
var CATS = ['ALL','LobbyA','LobbyB','MG1','MG2','MG3','MG4','MG5','MG6','MG7','อื่นๆ'];
var curCat = 'ALL';
var rows = [];
var counts = {};
function catClass(c){ var m={'LobbyA':'c-LobbyA','LobbyB':'c-LobbyB','MG1':'c-MG1','MG2':'c-MG2','MG3':'c-MG3','MG4':'c-MG4','MG5':'c-MG5','MG6':'c-MG6','MG7':'c-MG7'}; return m[c]||'cother'; }
function fmtTime(ms){ var d=new Date(ms); function p(n){return (n<10?'0':'')+n;} return p(d.getHours())+':'+p(d.getMinutes())+':'+p(d.getSeconds()); }
function esc(s){ return String(s==null?'':s).replace(/[&<>]/g,function(c){return{'&':'&amp;','<':'&lt;','>':'&gt;'}[c];}); }
function renderTabs(){
var t=document.getElementById('tabs'); t.innerHTML='';
CATS.forEach(function(c){
var d=document.createElement('div'); d.className='tab'+(c===curCat?' active':'');
var n=counts[c]!=null?('<span class="n">'+counts[c]+'</span>'):'';
d.innerHTML=esc(c)+n;
d.onclick=function(){ curCat=c; renderTabs(); render(); load(); };
t.appendChild(d);
});
}
function render(){
var wrap=document.getElementById('wrap');
var q=(document.getElementById('search').value||'').toLowerCase().trim();
var list=rows.filter(function(r){
if(!q) return true;
return ((r.who||'')+' '+(r.room||'')+' '+(r.ev||'')+' '+(r.detail||'')).toLowerCase().indexOf(q)>=0;
});
if(!list.length){ wrap.innerHTML='<div class="empty">ยังไม่มี log'+(curCat!=='ALL'?(' ใน '+esc(curCat)):'')+'</div>'; return; }
var h='<table><thead><tr><th>เวลา</th><th>หมวด</th><th>ห้อง</th><th>ใคร</th><th>event</th><th>รายละเอียด</th></tr></thead><tbody>';
list.forEach(function(r){
h+='<tr class="src-'+esc(r.src||'')+' ev-'+esc((r.ev||'').replace(/[^a-z-]/gi,''))+'">'+
'<td class="t">'+fmtTime(r.t)+'</td>'+
'<td class="cat"><span class="'+catClass(r.cat)+'">'+esc(r.cat)+'</span></td>'+
'<td class="room" title="'+esc(r.room)+'">'+esc(r.room)+'</td>'+
'<td class="who">'+esc(r.who)+'</td>'+
'<td class="ev">'+esc(r.ev)+'</td>'+
'<td class="detail">'+esc(r.detail)+'</td></tr>';
});
h+='</tbody></table>';
wrap.innerHTML=h;
}
function load(){
fetch('log.php?cat='+encodeURIComponent(curCat)+'&limit=800&_='+Date.now())
.then(function(r){return r.json();})
.then(function(j){
rows = (j&&j.logs)||[];
document.getElementById('status').textContent = (rows.length)+' รายการ · '+new Date().toLocaleTimeString('th-TH');
render();
}).catch(function(){ document.getElementById('status').textContent='โหลดไม่ได้'; });
// counts ทุกหมวด (จาก ALL)
fetch('log.php?cat=ALL&limit=2000&_c='+Date.now()).then(function(r){return r.json();}).then(function(j){
counts={ALL:0}; CATS.forEach(function(c){counts[c]=0;});
((j&&j.logs)||[]).forEach(function(r){ counts.ALL++; if(counts[r.cat]!=null) counts[r.cat]++; else counts['อื่นๆ']++; });
renderTabs();
}).catch(function(){});
}
document.getElementById('reload').onclick=load;
document.getElementById('search').oninput=render;
document.getElementById('clear').onclick=function(){
if(!confirm('ล้าง log ทั้งหมด?')) return;
fetch('log.php?action=clear').then(function(){ load(); });
};
renderTabs(); load();
setInterval(function(){ if(document.getElementById('auto').checked) load(); }, 3000);
</script>
</body>
</html>