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 + } +} +
diff --git a/frontend/src/components/SessionList.vue b/frontend/src/components/SessionList.vue index 2172cb5..7fd0daa 100644 --- a/frontend/src/components/SessionList.vue +++ b/frontend/src/components/SessionList.vue @@ -24,7 +24,6 @@ Current Same IP