diff --git a/dashboard/src/lib/ActionMenu.svelte b/dashboard/src/lib/ActionMenu.svelte new file mode 100644 index 0000000..ea8058e --- /dev/null +++ b/dashboard/src/lib/ActionMenu.svelte @@ -0,0 +1,256 @@ + + + + + +
+ + + {#if open} + + {/if} +
+ + diff --git a/dashboard/src/routes/users/+page.svelte b/dashboard/src/routes/users/+page.svelte index 88fbbeb..e049dbb 100644 --- a/dashboard/src/routes/users/+page.svelte +++ b/dashboard/src/routes/users/+page.svelte @@ -16,6 +16,7 @@ import { currentUser } from '$lib/auth'; import RoleChip from '$lib/RoleChip.svelte'; import ConfirmModal from '$lib/ConfirmModal.svelte'; + import ActionMenu from '$lib/ActionMenu.svelte'; import { generatePassword } from '$lib/password-gen'; const me = $derived($currentUser); @@ -70,8 +71,7 @@ username: string; email: string; instance_role: InstanceRole; - is_active: boolean; - }>({ username: '', email: '', instance_role: 'admin', is_active: true }); + }>({ username: '', email: '', instance_role: 'admin' }); let editPending = $state(false); let editError = $state(null); @@ -161,8 +161,7 @@ editForm = { username: row.username, email: row.email ?? '', - instance_role: row.instance_role, - is_active: row.is_active + instance_role: row.instance_role }; editError = null; } @@ -176,7 +175,6 @@ username?: string; email?: string | null; instance_role?: InstanceRole; - is_active?: boolean; } = {}; if (editForm.username !== editTarget.username) patch.username = editForm.username; if ((editTarget.email ?? '') !== editForm.email.trim()) { @@ -185,7 +183,6 @@ if (editForm.instance_role !== editTarget.instance_role) { patch.instance_role = editForm.instance_role; } - if (editForm.is_active !== editTarget.is_active) patch.is_active = editForm.is_active; try { const updated = await api.admins.update(editTarget.id, patch); admins = admins @@ -350,19 +347,22 @@
{shortDate(row.created_at)}
{relative(row.last_login_at)}
- - - {#if canDelete(row)} - - {/if} + openEdit(row) }, + { + label: row.is_active ? 'Deactivate' : 'Reactivate', + onClick: () => toggleActive(row) + }, + { + label: 'Delete', + danger: true, + disabled: !canDelete(row), + onClick: () => openDelete(row) + } + ]} + />
{/each} @@ -514,11 +514,6 @@ {/if} - {#if editError}
{editError}
{/if} @@ -682,7 +677,7 @@ } .row { display: grid; - grid-template-columns: 1.3fr 0.7fr 1.5fr 0.9fr 0.8fr 0.9fr 1.6fr; + grid-template-columns: 1.3fr 0.7fr 1.5fr 0.9fr 0.8fr 0.9fr 2.5rem; align-items: center; gap: 0.75rem; padding: 0.7rem 1rem; @@ -737,29 +732,6 @@ .actions-col { display: flex; justify-content: flex-end; - gap: 0.25rem; - flex-wrap: wrap; - } - .row-action { - background: transparent; - color: #cbd5e1; - border: 1px solid #334155; - padding: 0.25rem 0.55rem; - border-radius: 0.25rem; - font-size: 0.75rem; - cursor: pointer; - } - .row-action:hover { - background: #1e293b; - color: #e2e8f0; - } - .danger-link { - color: #fca5a5; - border-color: #7f1d1d; - } - .danger-link:hover { - background: #450a0a; - color: #fecaca; } button.primary { @@ -923,21 +895,6 @@ color: #cbd5e1; } - .toggle { - display: flex; - flex-direction: column; - gap: 0.25rem; - font-size: 0.85rem; - color: #cbd5e1; - } - .toggle :global(input[type='checkbox']) { - margin-right: 0.4rem; - } - .toggle small { - color: #64748b; - font-size: 0.72rem; - margin-left: 1.3rem; - } .token-row { display: flex;