feat(admin): visible Test/Test-seed badges + filter toggle

Users table shows [Test] next to soak-harness accounts, invite codes
list shows [Test-seed] next to codes that flag new accounts as test,
and a new "Include test accounts" checkbox lets admins hide bot
traffic from the user list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-04-11 01:20:09 -04:00
parent 917ef2a239
commit 983518e93d
2 changed files with 20 additions and 4 deletions

View File

@@ -114,6 +114,10 @@
<input type="checkbox" id="include-banned" checked>
Include banned
</label>
<label class="checkbox-label">
<input type="checkbox" id="include-test" checked>
Include test accounts
</label>
</div>
<table id="users-table" class="data-table">
<thead>

View File

@@ -67,12 +67,13 @@ async function getStats() {
return apiRequest('/api/admin/stats');
}
async function getUsers(query = '', offset = 0, includeBanned = true) {
async function getUsers(query = '', offset = 0, includeBanned = true, includeTest = true) {
const params = new URLSearchParams({
query,
offset,
limit: PAGE_SIZE,
include_banned: includeBanned,
include_test: includeTest,
});
return apiRequest(`/api/admin/users?${params}`);
}
@@ -306,15 +307,19 @@ async function loadUsers() {
try {
const query = document.getElementById('user-search').value;
const includeBanned = document.getElementById('include-banned').checked;
const data = await getUsers(query, usersPage * PAGE_SIZE, includeBanned);
const includeTest = document.getElementById('include-test').checked;
const data = await getUsers(query, usersPage * PAGE_SIZE, includeBanned, includeTest);
const tbody = document.querySelector('#users-table tbody');
tbody.innerHTML = '';
data.users.forEach(user => {
const testBadge = user.is_test_account
? ' <span class="badge badge-info" title="Soak harness test account">Test</span>'
: '';
tbody.innerHTML += `
<tr>
<td>${escapeHtml(user.username)}</td>
<td>${escapeHtml(user.username)}${testBadge}</td>
<td>${escapeHtml(user.email || '-')}</td>
<td><span class="badge badge-${user.role === 'admin' ? 'info' : 'muted'}">${user.role}</span></td>
<td>${getStatusBadge(user)}</td>
@@ -447,10 +452,13 @@ async function loadInvites() {
: invite.remaining_uses <= 0
? '<span class="badge badge-warning">Used Up</span>'
: '<span class="badge badge-success">Active</span>';
const testSeedBadge = invite.marks_as_test
? ' <span class="badge badge-info" title="Creates test accounts">Test-seed</span>'
: '';
tbody.innerHTML += `
<tr>
<td><code>${escapeHtml(invite.code)}</code></td>
<td><code>${escapeHtml(invite.code)}</code>${testSeedBadge}</td>
<td>${invite.use_count} / ${invite.max_uses}</td>
<td>${invite.remaining_uses}</td>
<td>${escapeHtml(invite.created_by_username)}</td>
@@ -827,6 +835,10 @@ document.addEventListener('DOMContentLoaded', () => {
usersPage = 0;
loadUsers();
});
document.getElementById('include-test').addEventListener('change', () => {
usersPage = 0;
loadUsers();
});
document.getElementById('users-prev').addEventListener('click', () => {
if (usersPage > 0) {
usersPage--;