Cleanup on Admin app, better delete confirmations.
This commit is contained in:
@@ -26,8 +26,6 @@ const permissions = ref([])
|
|||||||
const currentOrgId = ref(null) // UUID of selected org for detail view
|
const currentOrgId = ref(null) // UUID of selected org for detail view
|
||||||
const currentUserId = ref(null) // UUID for user detail view
|
const currentUserId = ref(null) // UUID for user detail view
|
||||||
const userDetail = ref(null) // cached user detail object
|
const userDetail = ref(null) // cached user detail object
|
||||||
const userLink = ref(null) // latest generated registration link
|
|
||||||
const userLinkExpires = ref(null)
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const addingOrgForPermission = ref(null)
|
const addingOrgForPermission = ref(null)
|
||||||
const PERMISSION_ID_PATTERN = '^[A-Za-z0-9:._~-]+$'
|
const PERMISSION_ID_PATTERN = '^[A-Za-z0-9:._~-]+$'
|
||||||
@@ -112,33 +110,6 @@ const permissionSummary = computed(() => {
|
|||||||
|
|
||||||
function renamePermissionDisplay(p) { openDialog('perm-display', { permission: p, id: p.id, display_name: p.display_name }) }
|
function renamePermissionDisplay(p) { openDialog('perm-display', { permission: p, id: p.id, display_name: p.display_name }) }
|
||||||
|
|
||||||
async function refreshPermissionsContext() {
|
|
||||||
// Reload both lists so All Permissions table shows new associations promptly.
|
|
||||||
await Promise.all([loadPermissions(), loadOrgs()])
|
|
||||||
}
|
|
||||||
|
|
||||||
async function attachPermissionToOrg(pid, orgUuid) {
|
|
||||||
if (!orgUuid) return
|
|
||||||
try {
|
|
||||||
const params = new URLSearchParams({ permission_id: pid })
|
|
||||||
await apiJson(`/auth/api/admin/orgs/${orgUuid}/permission?${params.toString()}`, { method: 'POST' })
|
|
||||||
await loadOrgs()
|
|
||||||
} catch (e) {
|
|
||||||
authStore.showMessage(e.message || 'Failed to add permission to org')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function detachPermissionFromOrg(pid, orgUuid) {
|
|
||||||
openDialog('confirm', { message: 'Remove permission from this org?', action: async () => {
|
|
||||||
try {
|
|
||||||
const params = new URLSearchParams({ permission_id: pid })
|
|
||||||
await apiJson(`/auth/api/admin/orgs/${orgUuid}/permission?${params.toString()}`, { method: 'DELETE' })
|
|
||||||
await loadOrgs()
|
|
||||||
} catch (e) {
|
|
||||||
authStore.showMessage(e.message || 'Failed to remove permission from org')
|
|
||||||
}
|
|
||||||
} })
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseHash() {
|
function parseHash() {
|
||||||
const h = window.location.hash || ''
|
const h = window.location.hash || ''
|
||||||
@@ -209,11 +180,37 @@ function updateOrg(org) { openDialog('org-update', { org, name: org.display_name
|
|||||||
|
|
||||||
function editUserName(user) { openDialog('user-update-name', { user, name: user.display_name }) }
|
function editUserName(user) { openDialog('user-update-name', { user, name: user.display_name }) }
|
||||||
|
|
||||||
|
async function performOrgDeletion(orgUuid) {
|
||||||
|
await apiJson(`/auth/api/admin/orgs/${orgUuid}`, { method: 'DELETE' })
|
||||||
|
await Promise.all([loadOrgs(), loadPermissions()])
|
||||||
|
}
|
||||||
|
|
||||||
function deleteOrg(org) {
|
function deleteOrg(org) {
|
||||||
if (!info.value?.is_global_admin) { authStore.showMessage('Global admin only'); return }
|
if (!info.value?.is_global_admin) { authStore.showMessage('Global admin only'); return }
|
||||||
openDialog('confirm', { message: `Delete organization ${org.display_name}?`, action: async () => {
|
|
||||||
await apiJson(`/auth/api/admin/orgs/${org.uuid}`, { method: 'DELETE' })
|
const userCount = org.roles.reduce((acc, r) => acc + r.users.length, 0)
|
||||||
await Promise.all([loadOrgs(), loadPermissions()])
|
|
||||||
|
if (userCount === 0) {
|
||||||
|
// No users in the organization, safe to delete directly
|
||||||
|
performOrgDeletion(org.uuid)
|
||||||
|
.then(() => {
|
||||||
|
authStore.showMessage(`Organization "${org.display_name}" deleted.`, 'success', 2500)
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
authStore.showMessage(e.message || 'Failed to delete organization', 'error')
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build detailed breakdown of users by role
|
||||||
|
const roleParts = org.roles
|
||||||
|
.filter(r => r.users.length > 0)
|
||||||
|
.map(r => `${r.users.length} ${r.display_name}`)
|
||||||
|
|
||||||
|
const affects = roleParts.join(', ')
|
||||||
|
|
||||||
|
openDialog('confirm', { message: `Delete organization "${org.display_name}", including accounts of ${affects})?`, action: async () => {
|
||||||
|
await performOrgDeletion(org.uuid)
|
||||||
} })
|
} })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,21 +249,21 @@ function onRoleDrop(e, org, role) {
|
|||||||
} catch (_) { /* ignore */ }
|
} catch (_) { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// (legacy function retained but unused in UI)
|
|
||||||
async function addOrgPermission() { /* obsolete */ }
|
|
||||||
|
|
||||||
async function removeOrgPermission() { /* obsolete */ }
|
|
||||||
|
|
||||||
// Role actions
|
// Role actions
|
||||||
function createRole(org) { openDialog('role-create', { org }) }
|
function createRole(org) { openDialog('role-create', { org }) }
|
||||||
|
|
||||||
function updateRole(role) { openDialog('role-update', { role, name: role.display_name }) }
|
function updateRole(role) { openDialog('role-update', { role, name: role.display_name }) }
|
||||||
|
|
||||||
function deleteRole(role) {
|
function deleteRole(role) {
|
||||||
openDialog('confirm', { message: `Delete role ${role.display_name}?`, action: async () => {
|
// UI only allows deleting empty roles, so no confirmation needed
|
||||||
await apiJson(`/auth/api/admin/orgs/${role.org_uuid}/roles/${role.uuid}`, { method: 'DELETE' })
|
apiJson(`/auth/api/admin/orgs/${role.org_uuid}/roles/${role.uuid}`, { method: 'DELETE' })
|
||||||
await loadOrgs()
|
.then(() => {
|
||||||
} })
|
authStore.showMessage(`Role "${role.display_name}" deleted.`, 'success', 2500)
|
||||||
|
loadOrgs()
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
authStore.showMessage(e.message || 'Failed to delete role', 'error')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleRolePermission(role, pid, checked) {
|
async function toggleRolePermission(role, pid, checked) {
|
||||||
@@ -292,13 +289,44 @@ async function toggleRolePermission(role, pid, checked) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Permission actions
|
// Permission actions
|
||||||
function updatePermission(p) { openDialog('perm-display', { permission: p }) }
|
async function performPermissionDeletion(permissionId) {
|
||||||
|
const params = new URLSearchParams({ permission_id: permissionId })
|
||||||
|
await apiJson(`/auth/api/admin/permission?${params.toString()}`, { method: 'DELETE' })
|
||||||
|
await loadPermissions()
|
||||||
|
}
|
||||||
|
|
||||||
function deletePermission(p) {
|
function deletePermission(p) {
|
||||||
openDialog('confirm', { message: `Delete permission ${p.id}?`, action: async () => {
|
const userCount = permissionSummary.value[p.id]?.userCount || 0
|
||||||
const params = new URLSearchParams({ permission_id: p.id })
|
|
||||||
await apiJson(`/auth/api/admin/permission?${params.toString()}`, { method: 'DELETE' })
|
// Count roles that have this permission
|
||||||
await loadPermissions()
|
let roleCount = 0
|
||||||
|
for (const org of orgs.value) {
|
||||||
|
for (const role of org.roles) {
|
||||||
|
if (role.permissions.includes(p.id)) {
|
||||||
|
roleCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roleCount === 0) {
|
||||||
|
// No roles have this permission, safe to delete directly
|
||||||
|
performPermissionDeletion(p.id)
|
||||||
|
.then(() => {
|
||||||
|
authStore.showMessage(`Permission "${p.display_name}" deleted.`, 'success', 2500)
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
authStore.showMessage(e.message || 'Failed to delete permission', 'error')
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = []
|
||||||
|
if (roleCount > 0) parts.push(`${roleCount} role${roleCount !== 1 ? 's' : ''}`)
|
||||||
|
if (userCount > 0) parts.push(`${userCount} user${userCount !== 1 ? 's' : ''}`)
|
||||||
|
const affects = parts.join(', ')
|
||||||
|
|
||||||
|
openDialog('confirm', { message: `Delete permission "${p.display_name}" (${affects})?`, action: async () => {
|
||||||
|
await performPermissionDeletion(p.id)
|
||||||
} })
|
} })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user