ui: move activate/inactivate/delete to user list table actions column
- New actions column (right of state) with inline buttons per row - Manage form keeps save user + clear only (no duplicate action buttons) - Themed to match admin dialog: JetBrains Mono, uppercase, square borders - Delete uses danger styling
This commit is contained in:
+63
-5
@@ -831,6 +831,38 @@ body {
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--ink-dim);
|
||||
}
|
||||
.user-list-table .user-actions-cell {
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
gap: 0.3rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.user-list-table .user-actions-cell button {
|
||||
font-family: var(--font);
|
||||
font-size: 0.55rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
padding: 0.2rem 0.4rem;
|
||||
min-height: 1.7rem;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--border-light);
|
||||
background: var(--input-bg);
|
||||
color: var(--ink);
|
||||
border-radius: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.user-list-table .user-actions-cell button:hover {
|
||||
background: var(--hover);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
}
|
||||
.user-list-table .user-actions-cell button.danger {
|
||||
color: var(--danger);
|
||||
}
|
||||
.user-list-table .user-actions-cell button.danger:hover {
|
||||
border-color: var(--danger);
|
||||
color: var(--danger);
|
||||
}
|
||||
.user-state-active { color: var(--accent3); }
|
||||
.user-state-inactive { color: var(--danger); }
|
||||
/* OAuth client list — compact, matches user table density */
|
||||
@@ -1384,9 +1416,6 @@ body {
|
||||
<input type="password" id="admin-password" placeholder="required for new users; leave blank to keep unchanged" autocomplete="new-password">
|
||||
<div class="manage-user-actions">
|
||||
<button class="primary" type="button" onclick="saveManagedUser()">save user</button>
|
||||
<button type="button" onclick="setManagedUserActive(false)">inactivate</button>
|
||||
<button type="button" onclick="setManagedUserActive(true)">activate</button>
|
||||
<button type="button" onclick="deleteManagedUser()">delete</button>
|
||||
<button type="button" onclick="clearManageUserForm()">clear</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1714,14 +1743,20 @@ function renderUserList() {
|
||||
el.innerHTML = '<div class="empty-state">no users</div>';
|
||||
return;
|
||||
}
|
||||
el.innerHTML = `<table class="user-list-table"><thead><tr><th>user id</th><th>name</th><th>role</th><th>state</th></tr></thead><tbody>${
|
||||
el.innerHTML = `<table class="user-list-table"><thead><tr><th>user id</th><th>name</th><th>role</th><th>state</th><th style="text-align:right">actions</th></tr></thead><tbody>${
|
||||
state.users.map(u => {
|
||||
const active = u.active !== false;
|
||||
const uid = escapeHtml(u.user_id || '');
|
||||
return `<tr>
|
||||
<td class="user-id">${escapeHtml(u.user_id || '')}</td>
|
||||
<td class="user-id">${uid}</td>
|
||||
<td>${escapeHtml(u.display_name || '')}</td>
|
||||
<td>${escapeHtml(u.role || '')}</td>
|
||||
<td class="${active ? 'user-state-active' : 'user-state-inactive'}">${active ? 'active' : 'inactive'}</td>
|
||||
<td class="user-actions-cell">
|
||||
<button type="button" onclick="userListAction('${uid}', 'activate')">activate</button>
|
||||
<button type="button" onclick="userListAction('${uid}', 'inactivate')">inactivate</button>
|
||||
<button type="button" class="danger" onclick="userListAction('${uid}', 'delete')">delete</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}).join('')
|
||||
}</tbody></table>`;
|
||||
@@ -1785,6 +1820,29 @@ async function setManagedUserActive(active) {
|
||||
}
|
||||
}
|
||||
|
||||
async function userListAction(userId, action) {
|
||||
if (!userId) return;
|
||||
if (action === 'delete') {
|
||||
if (!confirm(`Delete user "${userId}"? If delete fails, inactivate instead.`)) return;
|
||||
try {
|
||||
await api('DELETE', `/users/${encodeURIComponent(userId)}`);
|
||||
showToast(`user deleted · ${userId}`, 'success');
|
||||
await loadUsers();
|
||||
} catch (e) {
|
||||
if (e.message !== 'unauthorized') showToast('delete failed: ' + e.message, 'error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
const active = action === 'activate';
|
||||
try {
|
||||
await api('PATCH', `/users/${encodeURIComponent(userId)}`, { active });
|
||||
showToast(active ? `activated · ${userId}` : `inactivated · ${userId}`, 'success');
|
||||
await loadUsers();
|
||||
} catch (e) {
|
||||
if (e.message !== 'unauthorized') showToast('update failed: ' + e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteManagedUser() {
|
||||
const userId = document.getElementById('admin-manage-select').value || document.getElementById('admin-user-id').value.trim();
|
||||
if (!userId) {
|
||||
|
||||
Reference in New Issue
Block a user