Let admins delete tables from the lobby

Admins see a "削除" button on every table in the lobby. It calls the new
admin-only DELETE /api/tables/:id (404 for non-admins, same-origin only),
which closes the table the same way as when every human leaves. Players
and spectators still at the table are told "管理者がテーブルを削除しました。"

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
agomizi1cmandClaude Opus 5.5 committed 2026-10-19 23:03:24 +09:00
1 parent 3f2515a9fa
commit 0318ee2b5e
5 files changed
+53 -13

No files matched your search

+34 -7
View File
@@ -22,11 +22,17 @@
return false;
}
// 管理者だけにハンドレンジ学習・CPU戦のレビューへのリンクを出す(ページ自体もサーバ側で管理者以外は 404)
let admin = false;
let tables = null;
// 管理者だけにハンドレンジ学習・CPU戦のレビューへのリンクとテーブルの削除ボタンを出す
// (ページ・API 自体もサーバ側で管理者以外は 404)
App.api('GET', '/api/me')
.then((me) => {
document.getElementById('range-link').hidden = !me.admin;
document.getElementById('review-link').hidden = !me.admin;
admin = Boolean(me.admin);
document.getElementById('range-link').hidden = !admin;
document.getElementById('review-link').hidden = !admin;
if (tables) render(tables);
})
.catch(() => {});
@@ -35,10 +41,27 @@
});
listEl.addEventListener('click', (e) => {
const del = e.target.closest('[data-delete]');
if (del) return deleteTable(del.dataset.delete, del);
const btn = e.target.closest('[data-join]');
if (btn && requireName()) location.href = '/table/' + encodeURIComponent(btn.dataset.join);
});
function deleteTable(id, btn) {
const t = tables && tables.find((x) => x.id === id);
if (!t) return;
const players = t.playerCount ? `
着席中の ${t.playerCount} 人はテーブルから退出させられます。` : '';
if (!confirm(`「${t.name}」を削除しますか?${players}`)) return;
btn.disabled = true;
App.api('DELETE', '/api/tables/' + encodeURIComponent(id))
.then(() => App.toast('テーブルを削除しました'))
.catch((err) => {
btn.disabled = false;
App.toast(err.message);
});
}
const MODE_LABEL = { chips: '💰 キャッシュオンリー', friends: '🃏 友人戦', cpu: '🤖 CPU戦', review: '📝 レビュー付きCPU戦' };
function phaseBadge(t) {
@@ -47,7 +70,8 @@
return '<span class="badge live">PLAYING</span>';
}
function render(tables) {
function render(list) {
tables = list;
countEl.textContent = tables.length ? `${tables.length} テーブル` : '';
if (!tables.length) {
listEl.innerHTML = '<div class="empty">開催中のテーブルはありません。<br>テーブルを作成して友達を招待しよう。</div>';
@@ -69,9 +93,12 @@
<span>${MODE_LABEL[t.mode] || ''}</span>
</div>
</div>
<button class="btn btn-blue btn-sm" data-join="${esc(t.id)}" ${full ? 'title="満席(観戦のみ)"' : ''}>
${full ? '観戦' : '参加'}
</button>
<div class="buttons">
${admin ? `<button class="btn btn-red btn-sm" data-delete="${esc(t.id)}" title="テーブルを削除">削除</button>` : ''}
<button class="btn btn-blue btn-sm" data-join="${esc(t.id)}" ${full ? 'title="満席(観戦のみ)"' : ''}>
${full ? '観戦' : '参加'}
</button>
</div>
</div>`;
})
.join('');