From bc87f76d11c00c8eef97bb637134626c048eb09c Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Mon, 1 Sep 2025 17:34:45 -0600 Subject: [PATCH] Crude dialog rather than prompt() for input fields. (needs cleanup) --- frontend/src/admin/AdminApp.vue | 340 +++++++++++++++++--------------- 1 file changed, 181 insertions(+), 159 deletions(-) diff --git a/frontend/src/admin/AdminApp.vue b/frontend/src/admin/AdminApp.vue index f479c0a..c5fb0a5 100644 --- a/frontend/src/admin/AdminApp.vue +++ b/frontend/src/admin/AdminApp.vue @@ -23,6 +23,7 @@ const newPermId = ref('') const newPermName = ref('') const editingPermId = ref(null) const renameIdValue = ref('') +const dialog = ref({ type: null, data: null, busy: false, error: '' }) const safeIdRegex = /[^A-Za-z0-9:._~-]/g function sanitizeNewId() { if (newPermId.value) newPermId.value = newPermId.value.replace(safeIdRegex, '') } @@ -80,28 +81,9 @@ function availableOrgsForPermission(pid) { return orgs.value.filter(o => !o.permissions.includes(pid)) } -async function renamePermissionDisplay(p) { - const newName = prompt('New display name', p.display_name) - if (!newName || newName === p.display_name) return - try { - const body = { id: p.id, display_name: newName } - const res = await fetch(`/auth/admin/permission?permission_id=${encodeURIComponent(p.id)}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body) - }) - const data = await res.json() - if (data.detail) throw new Error(data.detail) - await refreshPermissionsContext() - } catch (e) { - alert(e.message || 'Failed to rename display name') - } -} +function renamePermissionDisplay(p) { openDialog('perm-display', { permission: p }) } -function startRenamePermissionId(p) { - editingPermId.value = p.id - renameIdValue.value = p.id -} +function startRenamePermissionId(p) { editingPermId.value = p.id; renameIdValue.value = p.id } function cancelRenameId() { editingPermId.value = null; renameIdValue.value = '' } async function submitRenamePermissionId(p) { const newId = renameIdValue.value.trim() @@ -112,9 +94,7 @@ async function submitRenamePermissionId(p) { let data; try { data = await res.json() } catch(_) { data = {} } if (!res.ok || data.detail) throw new Error(data.detail || data.error || `Failed (${res.status})`) await refreshPermissionsContext(); cancelRenameId() - } catch (e) { - alert(e?.message || 'Failed to rename permission id') - } + } catch (e) { authStore.showMessage(e?.message || 'Rename failed') } } async function refreshPermissionsContext() { @@ -131,21 +111,22 @@ async function attachPermissionToOrg(pid, orgUuid) { if (data.detail) throw new Error(data.detail) await loadOrgs() } catch (e) { - alert(e.message || 'Failed to add permission to org') + authStore.showMessage(e.message || 'Failed to add permission to org') } } async function detachPermissionFromOrg(pid, orgUuid) { - if (!confirm('Remove permission from this org?')) return - try { - const params = new URLSearchParams({ permission_id: pid }) - const res = await fetch(`/auth/admin/orgs/${orgUuid}/permission?${params.toString()}`, { method: 'DELETE' }) - const data = await res.json() - if (data.detail) throw new Error(data.detail) - await loadOrgs() - } catch (e) { - alert(e.message || 'Failed to remove permission from org') - } + openDialog('confirm', { message: 'Remove permission from this org?', action: async () => { + try { + const params = new URLSearchParams({ permission_id: pid }) + const res = await fetch(`/auth/admin/orgs/${orgUuid}/permission?${params.toString()}`, { method: 'DELETE' }) + const data = await res.json() + if (data.detail) throw new Error(data.detail) + await loadOrgs() + } catch (e) { + authStore.showMessage(e.message || 'Failed to remove permission from org') + } + } }) } function parseHash() { @@ -209,56 +190,20 @@ async function load() { } // Org actions -async function createOrg() { - const name = prompt('New organization display name:') - if (!name) return - const res = await fetch('/auth/admin/orgs', { - method: 'POST', - headers: { 'content-type': 'application/json' }, - body: JSON.stringify({ display_name: name, permissions: [] }) - }) - const data = await res.json() - if (data.detail) return alert(data.detail) - await Promise.all([loadOrgs(), loadPermissions()]) +function createOrg() { openDialog('org-create', {}) } + +function updateOrg(org) { openDialog('org-update', { org }) } + +function deleteOrg(org) { + if (!info.value?.is_global_admin) { authStore.showMessage('Global admin only'); return } + openDialog('confirm', { message: `Delete organization ${org.display_name}?`, action: async () => { + const res = await fetch(`/auth/admin/orgs/${org.uuid}`, { method: 'DELETE' }) + const data = await res.json(); if (data.detail) throw new Error(data.detail) + await loadOrgs() + } }) } -async function updateOrg(org) { - const name = prompt('Organization display name:', org.display_name) - if (!name) return - const res = await fetch(`/auth/admin/orgs/${org.uuid}`, { - method: 'PUT', - headers: { 'content-type': 'application/json' }, - body: JSON.stringify({ display_name: name, permissions: org.permissions }) - }) - const data = await res.json() - if (data.detail) return alert(data.detail) - await loadOrgs() -} - -async function deleteOrg(org) { - if (!info.value?.is_global_admin) { - alert('Only global admins may delete organizations.') - return - } - if (!confirm(`Delete organization ${org.display_name}?`)) return - const res = await fetch(`/auth/admin/orgs/${org.uuid}`, { method: 'DELETE' }) - const data = await res.json() - if (data.detail) return alert(data.detail) - await loadOrgs() -} - -async function createUserInRole(org, role) { - const displayName = prompt(`New member display name for role "${role.display_name}":`) - if (!displayName) return - const res = await fetch(`/auth/admin/orgs/${org.uuid}/users`, { - method: 'POST', - headers: { 'content-type': 'application/json' }, - body: JSON.stringify({ display_name: displayName, role: role.display_name }) - }) - const data = await res.json() - if (data.detail) return alert(data.detail) - await loadOrgs() -} +function createUserInRole(org, role) { openDialog('user-create', { org, role }) } async function moveUserToRole(org, user, targetRoleDisplayName) { if (user.role === targetRoleDisplayName) return @@ -268,7 +213,7 @@ async function moveUserToRole(org, user, targetRoleDisplayName) { body: JSON.stringify({ role: targetRoleDisplayName }) }) const data = await res.json() - if (data.detail) return alert(data.detail) + if (data.detail) { authStore.showMessage(data.detail); return } await loadOrgs() } @@ -292,57 +237,22 @@ function onRoleDrop(e, org, role) { } catch (_) { /* ignore */ } } -async function addOrgPermission(org) { - const id = prompt('Permission ID to add:', permissions.value[0]?.id || '') - if (!id) return - const res = await fetch(`/auth/admin/orgs/${org.uuid}/permissions/${encodeURIComponent(id)}`, { method: 'POST' }) - const data = await res.json() - if (data.detail) return alert(data.detail) - await loadOrgs() -} +// (legacy function retained but unused in UI) +async function addOrgPermission() { /* obsolete */ } -async function removeOrgPermission(org, permId) { - const res = await fetch(`/auth/admin/orgs/${org.uuid}/permissions/${encodeURIComponent(permId)}`, { method: 'DELETE' }) - const data = await res.json() - if (data.detail) return alert(data.detail) - await loadOrgs() -} +async function removeOrgPermission() { /* obsolete */ } // Role actions -async function createRole(org) { - const name = prompt('New role display name:') - if (!name) return - const res = await fetch(`/auth/admin/orgs/${org.uuid}/roles`, { - method: 'POST', - headers: { 'content-type': 'application/json' }, - body: JSON.stringify({ display_name: name, permissions: [] }) - }) - const data = await res.json() - if (data.detail) return alert(data.detail) - await loadOrgs() -} +function createRole(org) { openDialog('role-create', { org }) } -async function updateRole(role) { - const name = prompt('Role display name:', role.display_name) - if (!name) return - const csv = prompt('Permission IDs (comma-separated):', role.permissions.join(', ')) || '' - const perms = csv.split(',').map(s => s.trim()).filter(Boolean) - const res = await fetch(`/auth/admin/roles/${role.uuid}`, { - method: 'PUT', - headers: { 'content-type': 'application/json' }, - body: JSON.stringify({ display_name: name, permissions: perms }) - }) - const data = await res.json() - if (data.detail) return alert(data.detail) - await loadOrgs() -} +function updateRole(role) { openDialog('role-update', { role }) } -async function deleteRole(role) { - if (!confirm(`Delete role ${role.display_name}?`)) return - const res = await fetch(`/auth/admin/roles/${role.uuid}`, { method: 'DELETE' }) - const data = await res.json() - if (data.detail) return alert(data.detail) - await loadOrgs() +function deleteRole(role) { + openDialog('confirm', { message: `Delete role ${role.display_name}?`, action: async () => { + const res = await fetch(`/auth/admin/roles/${role.uuid}`, { method: 'DELETE' }) + const data = await res.json(); if (data.detail) throw new Error(data.detail) + await loadOrgs() + } }) } // Permission actions @@ -351,28 +261,20 @@ async function submitCreatePermission() { const name = newPermName.value.trim() if (!id || !name) return const res = await fetch('/auth/admin/permissions', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id, display_name: name }) }) - const data = await res.json(); if (data.detail) { alert(data.detail); return } + const data = await res.json(); if (data.detail) { authStore.showMessage(data.detail); return } await loadPermissions(); newPermId.value=''; newPermName.value=''; showCreatePermission.value=false } function cancelCreatePermission() { newPermId.value=''; newPermName.value=''; showCreatePermission.value=false } -async function updatePermission(p) { - const name = prompt('Permission display name:', p.display_name) - if (!name) return - const params = new URLSearchParams({ permission_id: p.id, display_name: name }) - const res = await fetch(`/auth/admin/permission?${params.toString()}`, { method: 'PUT' }) - const data = await res.json() - if (data.detail) return alert(data.detail) - await loadPermissions() -} +function updatePermission(p) { openDialog('perm-display', { permission: p }) } -async function deletePermission(p) { - if (!confirm(`Delete permission ${p.id}?`)) return - const params = new URLSearchParams({ permission_id: p.id }) - const res = await fetch(`/auth/admin/permission?${params.toString()}`, { method: 'DELETE' }) - const data = await res.json() - if (data.detail) return alert(data.detail) - await loadPermissions() +function deletePermission(p) { + openDialog('confirm', { message: `Delete permission ${p.id}?`, action: async () => { + const params = new URLSearchParams({ permission_id: p.id }) + const res = await fetch(`/auth/admin/permission?${params.toString()}`, { method: 'DELETE' }) + const data = await res.json(); if (data.detail) throw new Error(data.detail) + await loadPermissions() + } }) } onMounted(() => { @@ -454,10 +356,54 @@ async function toggleRolePermission(role, permId, checked) { const data = await res.json() if (data.detail) throw new Error(data.detail) } catch (e) { - alert(e.message || 'Failed to update role permission') + authStore.showMessage(e.message || 'Failed to update role permission') role.permissions = prev // revert } } + +function openDialog(type, data) { dialog.value = { type, data, busy: false, error: '' } } +function closeDialog() { dialog.value = { type: null, data: null, busy: false, error: '' } } + +async function submitDialog() { + if (!dialog.value.type || dialog.value.busy) return + dialog.value.busy = true; dialog.value.error = '' + try { + const t = dialog.value.type + if (t === 'org-create') { + const name = dialog.value.data.name?.trim(); if (!name) throw new Error('Name required') + const res = await fetch('/auth/admin/orgs', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ display_name: name, permissions: [] }) }) + const d = await res.json(); if (d.detail) throw new Error(d.detail); await Promise.all([loadOrgs(), loadPermissions()]) + } else if (t === 'org-update') { + const { org } = dialog.value.data; const name = dialog.value.data.name?.trim(); if (!name) throw new Error('Name required') + const res = await fetch(`/auth/admin/orgs/${org.uuid}`, { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ display_name: name, permissions: org.permissions }) }) + const d = await res.json(); if (d.detail) throw new Error(d.detail); await loadOrgs() + } else if (t === 'role-create') { + const { org } = dialog.value.data; const name = dialog.value.data.name?.trim(); if (!name) throw new Error('Name required') + const res = await fetch(`/auth/admin/orgs/${org.uuid}/roles`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ display_name: name, permissions: [] }) }) + const d = await res.json(); if (d.detail) throw new Error(d.detail); await loadOrgs() + } else if (t === 'role-update') { + const { role } = dialog.value.data; const name = dialog.value.data.name?.trim(); if (!name) throw new Error('Name required') + const permsCsv = dialog.value.data.perms || '' + const perms = permsCsv.split(',').map(s=>s.trim()).filter(Boolean) + const res = await fetch(`/auth/admin/roles/${role.uuid}`, { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ display_name: name, permissions: perms }) }) + const d = await res.json(); if (d.detail) throw new Error(d.detail); await loadOrgs() + } else if (t === 'user-create') { + const { org, role } = dialog.value.data; const name = dialog.value.data.name?.trim(); if (!name) throw new Error('Name required') + const res = await fetch(`/auth/admin/orgs/${org.uuid}/users`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ display_name: name, role: role.display_name }) }) + const d = await res.json(); if (d.detail) throw new Error(d.detail); await loadOrgs() + } else if (t === 'perm-display') { + const { permission } = dialog.value.data; const display = dialog.value.data.display_name?.trim(); if (!display) throw new Error('Display name required') + const params = new URLSearchParams({ permission_id: permission.id, display_name: display }) + const res = await fetch(`/auth/admin/permission?${params.toString()}`, { method: 'PUT' }) + const d = await res.json(); if (d.detail) throw new Error(d.detail); await loadPermissions() + } else if (t === 'confirm') { + const action = dialog.value.data.action; if (action) await action() + } + closeDialog() + } catch (e) { + dialog.value.error = e.message || 'Error' + } finally { dialog.value.busy = false } +} @@ -701,6 +649,59 @@ async function toggleRolePermission(role, permId, checked) { +