-[ template "/base/base.html" ]-
-[ block body ]-
-[ insert "/base/path.html" ]-
<div class="container-func">
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3">
<h2 class="mb-0">Manage Admins</h2>
<a class="btn btn-outline-secondary btn-sm" href="/admin/panel">Back to users</a>
</div>
<form id="addAdminForm" method="POST" action="/admin/admins" class="mb-4">
<div class="mb-3">
<label for="adminUid" class="form-label">UID or uid@server</label>
<input id="adminUid" type="text" name="uid" class="form-control" placeholder="2 or 2@local" required />
</div>
<button type="submit" class="btn btn-pink">Add Admin</button>
<span id="addAdminStatus" class="ms-2"></span>
</form>
<table class="table">
<thead>
<tr>
<th>Admin entry</th>
<th>Action</th>
</tr>
</thead>
<tbody id="adminsTableBody"></tbody>
</table>
<script>
const esc = (s) => String(s ?? '').replace(/[&<>"']/g, c => ({
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
}[c]));
async function loadAdmins() {
const tbody = document.getElementById('adminsTableBody');
try {
const res = await fetch('/admin/admins/json');
const data = await res.json();
const admins = Array.isArray(data.admins) ? data.admins : [];
tbody.innerHTML = admins.map(admin =>
'<tr><td>' + esc(admin) + '</td>' +
'<td><button class="btn btn-sm btn-danger remove-admin" data-entry="' + esc(admin) + '">Remove</button></td></tr>'
).join('');
} catch (e) {
tbody.innerHTML = '<tr><td colspan="2">Unable to load admins</td></tr>';
}
}
document.addEventListener('DOMContentLoaded', () => {
loadAdmins();
document.getElementById('addAdminForm').addEventListener('submit', async (event) => {
event.preventDefault();
const status = document.getElementById('addAdminStatus');
status.textContent = 'Saving...';
const params = new URLSearchParams();
for (const [k, v] of new FormData(event.currentTarget)) {
params.append(k, v);
}
try {
const res = await fetch('/admin/admins', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params.toString(),
});
const data = await res.json();
if (!res.ok || !data.success) {
status.textContent = data.message || 'Add failed';
return;
}
status.textContent = 'Added';
event.currentTarget.reset();
loadAdmins();
} catch (e) {
status.textContent = 'Add failed';
}
});
document.getElementById('adminsTableBody').addEventListener('click', async (event) => {
if (!event.target.classList.contains('remove-admin')) {
return;
}
const entry = event.target.getAttribute('data-entry');
try {
const res = await fetch('/admin/admins/' + encodeURIComponent(entry) + '/delete', {
method: 'POST'
});
const data = await res.json();
if (!res.ok || !data.success) {
alert(data.message || 'Remove failed');
return;
}
loadAdmins();
} catch (e) {
alert('Remove failed');
}
});
});
</script>
</div>
-[ endblock ]-