Bo Nix better win the damn super bowl dude he gives me anxiety every time I watch him

This commit is contained in:
Ben Mosley
2025-11-30 22:36:19 -06:00
parent 1398684dec
commit 716e0c1fac
16 changed files with 1802 additions and 0 deletions

100
templates/admin_users.html Normal file
View File

@@ -0,0 +1,100 @@
<section class="grid gap-6">
<!-- Create User -->
<div class="rounded-2xl border border-slate-800 bg-slate-900/60 p-5">
<h1 class="text-xl font-bold">Users</h1>
<p class="text-slate-400 text-sm mt-1">Temp password will be generated and shown as a flash message.</p>
<form method="post" action="{{ url_for('admin_users_create') }}"
class="mt-4 grid gap-3 sm:grid-cols-[1fr,200px,140px]">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input name="username" placeholder="Username (e.g. email)"
required
class="px-3 py-2 rounded-lg bg-slate-950 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-brand-600" />
<select name="role"
class="px-3 py-2 rounded-lg bg-slate-950 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-brand-600">
<option value="member">Member</option>
<option value="admin">Admin</option>
</select>
<button class="px-4 py-2 rounded-lg bg-brand-600 hover:bg-brand-700 font-semibold" type="submit">
Create
</button>
</form>
</div>
<!-- Users table -->
<div class="rounded-2xl border border-slate-800 bg-slate-900/60 p-5 overflow-x-auto">
<div class="flex items-center justify-between gap-3">
<h2 class="text-lg font-semibold">All Users</h2>
<input id="userFilter" placeholder="Filter by username…" class="px-3 py-2 rounded-lg bg-slate-950 border border-slate-700" oninput="filterUsers(this.value)" />
</div>
<table class="min-w-full text-sm mt-3">
<thead class="text-slate-300">
<tr>
<th class="py-2 pr-3 text-left font-medium">ID</th>
<th class="py-2 pr-3 text-left font-medium">Username</th>
<th class="py-2 pr-3 text-left font-medium">Role</th>
<th class="py-2 pr-3 text-left font-medium">Active</th>
<th class="py-2 pr-3 text-left font-medium">Must Change PW</th>
<th class="py-2 pr-3 text-left font-medium">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-800">
{% for u in users %}
<tr id="u{{ u.id }}">
<td class="py-2 pr-3 text-slate-300">{{ u.id }}</td>
<td class="py-2 pr-3 font-medium">{{ u.username }}</td>
<!-- Role updater -->
<td class="py-2 pr-3">
<form method="post" action="{{ url_for('admin_users_role', user_id=u.id) }}" class="flex items-center gap-2">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<select name="role"
class="px-2 py-1 rounded bg-slate-950 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-brand-600">
<option value="member" {{ 'selected' if u.role=='member' else '' }}>member</option>
<option value="admin" {{ 'selected' if u.role=='admin' else '' }}>admin</option>
</select>
<button class="px-3 py-1.5 rounded-lg border border-slate-700 hover:border-slate-500" type="submit">
Update
</button>
</form>
</td>
<!-- Active toggle -->
<td class="py-2 pr-3">
<form method="post" action="{{ url_for('admin_users_toggle', user_id=u.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button
class="px-3 py-1.5 rounded-lg {{ 'bg-emerald-600 hover:bg-emerald-700' if u.is_active else 'bg-slate-700 hover:bg-slate-600' }}"
type="submit">
{{ 'Active' if u.is_active else 'Inactive' }}
</button>
</form>
</td>
<td class="py-2 pr-3">{{ 'yes' if u.must_change_password else 'no' }}</td>
<!-- Reset -->
<td class="py-2 pr-3">
<form method="post" action="{{ url_for('admin_users_reset', user_id=u.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="px-3 py-1.5 rounded-lg bg-brand-600 hover:bg-brand-700 font-semibold" type="submit">
Reset Password
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<script>
function filterUsers(q) {
q = (q || '').toLowerCase();
document.querySelectorAll('tbody tr').forEach(tr => {
const name = tr.querySelector('td:nth-child(2)')?.textContent.toLowerCase() || '';
tr.style.display = name.includes(q) ? '' : 'none';
});
}
</script>
</div>
</section>