Add Users settings tab

This commit is contained in:
sjg
2026-08-11 00:37:59 +02:00
parent 3c3fc69542
commit e4cce9a004
4 changed files with 41 additions and 9 deletions
@@ -6051,9 +6051,18 @@ function initSettingsUI() {
} }
async function refreshUserManagement() { async function refreshUserManagement() {
const section = document.getElementById("user-management"); const section = document.getElementById("user-management");
if (!section) return; const tab = document.getElementById("settings-users-tab");
section.style.display = authEnabled && authRole === "admin" ? "block" : "none"; if (!section || !tab) return;
if (section.style.display === "none") return; const canManageUsers = authEnabled && authRole === "admin";
tab.style.display = canManageUsers ? "" : "none";
if (!canManageUsers) {
const panel = document.getElementById("subtab-settings-users");
if (panel) panel.style.display = "none";
if (tab.classList.contains("active")) {
document.querySelector('[data-subtab="settings-scheduler"]')?.click();
}
return;
}
const list = requiredElement("user-list"); const list = requiredElement("user-list");
try { try {
const users = await listUsers(); const users = await listUsers();
@@ -1461,6 +1461,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
<button class="sub-tab" data-subtab="settings-background-decode">Background Decode</button> <button class="sub-tab" data-subtab="settings-background-decode">Background Decode</button>
<button class="sub-tab" data-subtab="settings-bandplan">Bandplan</button> <button class="sub-tab" data-subtab="settings-bandplan">Bandplan</button>
<button class="sub-tab" data-subtab="settings-history">History</button> <button class="sub-tab" data-subtab="settings-history">History</button>
<button id="settings-users-tab" class="sub-tab" data-subtab="settings-users" style="display:none;">Users</button>
</div> </div>
<div id="subtab-settings-scheduler" class="sub-tab-panel"> <div id="subtab-settings-scheduler" class="sub-tab-panel">
<div id="scheduler-panel" class="sch-panel"> <div id="scheduler-panel" class="sch-panel">
@@ -1750,8 +1751,8 @@ SPDX-License-Identifier: GPL-2.0-or-later
</div> </div>
</div> </div>
</div> </div>
<section id="user-management" style="display:none; margin-top:1rem;"> <div id="subtab-settings-users" class="sub-tab-panel" style="display:none;">
<h2 class="section-heading">User management</h2> <div id="user-management">
<div class="settings-card"> <div class="settings-card">
<form id="user-create-form" class="sch-row" style="flex-wrap:wrap; gap:.5rem;"> <form id="user-create-form" class="sch-row" style="flex-wrap:wrap; gap:.5rem;">
<input id="user-create-username" class="auth-input" placeholder="Username" autocomplete="off" required /> <input id="user-create-username" class="auth-input" placeholder="Username" autocomplete="off" required />
@@ -1762,7 +1763,8 @@ SPDX-License-Identifier: GPL-2.0-or-later
<div id="user-management-error" class="auth-error" style="display:none;"></div> <div id="user-management-error" class="auth-error" style="display:none;"></div>
<div id="user-list" style="margin-top:.75rem;"></div> <div id="user-list" style="margin-top:.75rem;"></div>
</div> </div>
</section> </div>
</div>
</div> </div>
<div id="tab-about" class="tab-panel" style="display:none;"> <div id="tab-about" class="tab-panel" style="display:none;">
<h2 class="section-heading">About</h2> <h2 class="section-heading">About</h2>
@@ -5055,9 +5055,18 @@ function initSettingsUI() {
async function refreshUserManagement() { async function refreshUserManagement() {
const section = document.getElementById("user-management"); const section = document.getElementById("user-management");
if (!section) return; const tab = document.getElementById("settings-users-tab");
section.style.display = authEnabled && authRole === "admin" ? "block" : "none"; if (!section || !tab) return;
if (section.style.display === "none") return; const canManageUsers = authEnabled && authRole === "admin";
tab.style.display = canManageUsers ? "" : "none";
if (!canManageUsers) {
const panel = document.getElementById("subtab-settings-users");
if (panel) panel.style.display = "none";
if (tab.classList.contains("active")) {
document.querySelector<HTMLButtonElement>('[data-subtab="settings-scheduler"]')?.click();
}
return;
}
const list = requiredElement("user-list"); const list = requiredElement("user-list");
try { try {
const users = await listUsers(); const users = await listUsers();
@@ -9,6 +9,7 @@ import { readFile } from "node:fs/promises";
const indexPath = new URL("../../assets/web/index.html", import.meta.url); const indexPath = new URL("../../assets/web/index.html", import.meta.url);
const pluginLoaderPath = new URL("../src/plugin-loader.ts", import.meta.url); const pluginLoaderPath = new URL("../src/plugin-loader.ts", import.meta.url);
const mapCorePath = new URL("../src/map-core.ts", import.meta.url); const mapCorePath = new URL("../src/map-core.ts", import.meta.url);
const appPath = new URL("../src/app.ts", import.meta.url);
test("index loads one first-party application bootstrap", async () => { test("index loads one first-party application bootstrap", async () => {
const html = await readFile(indexPath, "utf8"); const html = await readFile(indexPath, "utf8");
@@ -30,6 +31,17 @@ test("startup has no remote script or stylesheet dependencies", async () => {
); );
}); });
test("administrator user management is a dedicated Settings sub-tab", async () => {
const [html, app] = await Promise.all([
readFile(indexPath, "utf8"),
readFile(appPath, "utf8"),
]);
assert.match(html, /data-subtab="settings-users"/);
assert.match(html, /id="subtab-settings-users" class="sub-tab-panel"/);
assert.match(app, /authEnabled && authRole === "admin"/);
assert.match(app, /settings-users-tab/);
});
test("lazy frontend features use modules and local map symbols", async () => { test("lazy frontend features use modules and local map symbols", async () => {
const [loader, map] = await Promise.all([ const [loader, map] = await Promise.all([
readFile(pluginLoaderPath, "utf8"), readFile(pluginLoaderPath, "utf8"),