diff --git a/frontend/auth/admin/AdminApp.vue b/frontend/auth/admin/AdminApp.vue
index f80babf..4c1c20f 100644
--- a/frontend/auth/admin/AdminApp.vue
+++ b/frontend/auth/admin/AdminApp.vue
@@ -501,16 +501,20 @@ async function toggleOrgPermission(org, permId, checked) {
function openDialog(type, data) { dialog.value = { type, data, busy: false, error: '' } }
function closeDialog() { dialog.value = { type: null, data: null, busy: false, error: '' } }
-async function onUserNameSaved() {
+async function refreshUserDetail() {
await loadOrgs()
if (selectedUser.value) {
try {
- const r = await fetch(`/auth/api/admin/orgs/${selectedUser.value.org_uuid}/users/${selectedUser.value.uuid}`)
+ const r = await fetch(`/auth/api/admin/orgs/${selectedUser.value.org_uuid}/users/${selectedUser.value.uuid}`)
const jd = await r.json()
if (!r.ok || jd.detail) throw new Error(jd.detail || 'Reload failed')
userDetail.value = jd
} catch (e) { authStore.showMessage(e.message || 'Failed to reload user', 'error') }
}
+}
+
+async function onUserNameSaved() {
+ await refreshUserDetail()
authStore.showMessage('User renamed', 'success', 1500)
}
@@ -625,6 +629,7 @@ async function submitDialog() {
@go-overview="goOverview"
@open-org="openOrg"
@on-user-name-saved="onUserNameSaved"
+ @refresh-user-detail="refreshUserDetail"
@edit-user-name="editUserName"
@close-reg-modal="showRegModal = false"
/>
diff --git a/frontend/src/admin/AdminUserDetail.vue b/frontend/src/admin/AdminUserDetail.vue
index a1a86bf..1c748f9 100644
--- a/frontend/src/admin/AdminUserDetail.vue
+++ b/frontend/src/admin/AdminUserDetail.vue
@@ -14,9 +14,10 @@ const props = defineProps({
showRegModal: Boolean
})
-const emit = defineEmits(['generateUserRegistrationLink', 'goOverview', 'openOrg', 'onUserNameSaved', 'closeRegModal', 'editUserName'])
+const emit = defineEmits(['generateUserRegistrationLink', 'goOverview', 'openOrg', 'onUserNameSaved', 'closeRegModal', 'editUserName', 'refreshUserDetail'])
const authStore = useAuthStore()
+const terminatingSessions = ref({})
function onLinkCopied() {
authStore.showMessage('Link copied to clipboard!')
@@ -39,6 +40,34 @@ function handleDelete(credential) {
.catch(err => console.error('Delete credential error', err))
}
+async function handleTerminateSession(session) {
+ const sessionId = session?.id
+ if (!sessionId) return
+ terminatingSessions.value = { ...terminatingSessions.value, [sessionId]: true }
+ try {
+ const res = await fetch(`/auth/api/admin/orgs/${props.selectedUser.org_uuid}/users/${props.selectedUser.uuid}/sessions/${sessionId}`, { method: 'DELETE' })
+ const data = await res.json()
+ if (data.status === 'ok') {
+ if (data.current_session_terminated) {
+ sessionStorage.clear()
+ location.reload()
+ return
+ }
+ emit('refreshUserDetail') // Refresh without showing rename message
+ authStore.showMessage('Session terminated', 'success', 2500)
+ } else {
+ authStore.showMessage(data.detail || 'Failed to terminate session', 'error')
+ }
+ } catch (err) {
+ console.error('Terminate session error', err)
+ authStore.showMessage('Failed to terminate session', 'error')
+ } finally {
+ const next = { ...terminatingSessions.value }
+ delete next[sessionId]
+ terminatingSessions.value = next
+ }
+}
+
@@ -84,9 +113,10 @@ function handleDelete(credential) {