Add pagination/search/sort to the admin tables that can grow large

Uses simple-datatables (vanilla JS, no jQuery needed) on the Users,
Sessions, Email Log, and Feedback tables, since those grow with real
usage. Left Games (small, admin-curated) and Hints (row order is
meaningful - "checked in order" - a sortable column would mislead)
untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Frank
2026-08-28 20:39:14 +02:00
co-authored by Claude Sonnet 5
parent 4daaa8d102
commit 5dc01a358a
9 changed files with 87 additions and 6 deletions
+22
View File
@@ -0,0 +1,22 @@
import { DataTable } from 'simple-datatables';
import 'simple-datatables/dist/style.css';
// Paginate/search/sort any admin table opted in via class="admin-datatable".
// Kept off tables that are admin-curated and stay small (games) or where row
// order is meaningful and must not be disturbed by sorting (hints).
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('table.admin-datatable').forEach((table) => {
new DataTable(table, {
perPage: 25,
perPageSelect: [10, 25, 50, 100],
searchable: true,
sortable: true,
labels: {
placeholder: 'Search...',
perPage: '{select} entries per page',
noRows: 'No matching entries found',
info: 'Showing {start} to {end} of {rows} entries',
},
});
});
});
+1
View File
@@ -8,3 +8,4 @@ import 'bootstrap/js/dist/dropdown';
import './game-waiting';
import './game-lobby';
import './admin-session-tabs';
import './admin-datatables';
+24 -1
View File
@@ -11,7 +11,8 @@
"dependencies": {
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.8",
"bootstrap-icons": "^1.13.1"
"bootstrap-icons": "^1.13.1",
"simple-datatables": "^10.3.0"
},
"devDependencies": {
"@babel/core": "^7.25.0",
@@ -4013,6 +4014,12 @@
"dev": true,
"license": "CC0-1.0"
},
"node_modules/dayjs": {
"version": "1.11.23",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz",
"integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==",
"license": "MIT"
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -4112,6 +4119,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/diff-dom": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/diff-dom/-/diff-dom-5.2.1.tgz",
"integrity": "sha512-G6WquwIa/XOxiYDYRMxfJj+c+a6LXMv7zgXoAQ3eRjTYkc8Lc4Aq97krTu/B15r243FR7dyeSxDenzKtO6sbfA==",
"license": "LGPL-3.0"
},
"node_modules/dns-packet": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
@@ -7706,6 +7719,16 @@
"dev": true,
"license": "ISC"
},
"node_modules/simple-datatables": {
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/simple-datatables/-/simple-datatables-10.3.0.tgz",
"integrity": "sha512-/9XcZ/AzZWYNMTTwUcUqsNhZXD1KIHg4tWCUhRx5qPDOnRVG2gNdRDR1euNTCbQxfAypu78ucgWc/BOMFfuxAQ==",
"license": "LGPL-3.0",
"dependencies": {
"dayjs": "^1.11.21",
"diff-dom": "5.2.1"
}
},
"node_modules/sockjs": {
"version": "0.3.24",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
+2 -1
View File
@@ -27,6 +27,7 @@
"dependencies": {
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.8",
"bootstrap-icons": "^1.13.1"
"bootstrap-icons": "^1.13.1",
"simple-datatables": "^10.3.0"
}
}
+34
View File
@@ -48,6 +48,40 @@
overflow-x: auto;
}
/* simple-datatables theming to match the admin panel */
.datatable-top,
.datatable-bottom {
padding: 0.75rem 1rem;
font-size: 0.85rem;
color: #64748b;
}
.datatable-input {
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 0.35rem 0.6rem;
font-size: 0.85rem;
}
.datatable-selector {
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 0.25rem 0.4rem;
font-size: 0.85rem;
}
.datatable-pagination a,
.datatable-pagination button {
border-radius: 4px;
color: #475569;
}
.datatable-pagination a:hover,
.datatable-pagination button:hover {
background: #f1f5f9;
}
.datatable-pagination .datatable-active a,
.datatable-pagination .datatable-active button {
background: #3b82f6;
color: #fff;
}
@media (max-width: 767.98px) {
.admin-shell {
flex-direction: column;
@@ -9,7 +9,7 @@
</div>
<div style="background: #fff; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,.07); overflow: hidden;">
<table style="width: 100%; min-width: 620px; border-collapse: collapse; font-size: 0.9rem;">
<table class="admin-datatable" style="width: 100%; min-width: 620px; border-collapse: collapse; font-size: 0.9rem;">
<thead>
<tr style="background: #f1f5f9; border-bottom: 1px solid #e2e8f0;">
<th style="padding: 0.75rem 1rem; text-align: left; color: #475569; font-weight: 600;">ID</th>
@@ -31,7 +31,7 @@
</div>
<div style="background: #fff; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,.07); overflow: hidden;">
<table style="width: 100%; min-width: 760px; border-collapse: collapse; font-size: 0.9rem;">
<table class="admin-datatable" style="width: 100%; min-width: 760px; border-collapse: collapse; font-size: 0.9rem;">
<thead>
<tr style="background: #f1f5f9; border-bottom: 1px solid #e2e8f0;">
<th style="padding: 0.75rem 1rem; text-align: left; color: #475569; font-weight: 600;">Game</th>
@@ -9,7 +9,7 @@
</div>
<div style="background: #fff; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,.07); overflow: hidden;">
<table style="width: 100%; min-width: 700px; border-collapse: collapse; font-size: 0.9rem;">
<table class="admin-datatable" style="width: 100%; min-width: 700px; border-collapse: collapse; font-size: 0.9rem;">
<thead>
<tr style="background: #f1f5f9; border-bottom: 1px solid #e2e8f0;">
<th style="padding: 0.75rem 1rem; text-align: left; color: #475569; font-weight: 600;">ID</th>
+1 -1
View File
@@ -9,7 +9,7 @@
</div>
<div style="background: #fff; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,.07); overflow: hidden;">
<table style="width: 100%; min-width: 760px; border-collapse: collapse; font-size: 0.9rem;">
<table class="admin-datatable" style="width: 100%; min-width: 760px; border-collapse: collapse; font-size: 0.9rem;">
<thead>
<tr style="background: #f1f5f9; border-bottom: 1px solid #e2e8f0;">
<th style="padding: 0.75rem 1rem; text-align: left; color: #475569; font-weight: 600;">ID</th>