From 48e15fa6774f5b5ee16286c0f35736a5432f75a1 Mon Sep 17 00:00:00 2001 From: Kyle Pope Date: Fri, 27 Feb 2026 19:30:43 +0800 Subject: [PATCH] UX polish for delete-user: username toast, hide self-delete S-03: Delete toast now shows the deleted username from the API response S-04: Delete button hidden for the current admin's own row (backend still guards with 403, but no reason to show a dead button) Adds username to auth status response so the frontend can identify the current user. Co-Authored-By: Claude Opus 4.6 --- backend/app/routers/auth.py | 1 + frontend/src/components/admin/IAMPage.tsx | 4 +++- .../src/components/admin/UserActionsMenu.tsx | 19 +++++++++++++++---- frontend/src/types/index.ts | 1 + 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/backend/app/routers/auth.py b/backend/app/routers/auth.py index 16e8ed5..d9cd550 100644 --- a/backend/app/routers/auth.py +++ b/backend/app/routers/auth.py @@ -531,6 +531,7 @@ async def auth_status( "authenticated": authenticated, "setup_required": setup_required, "role": role, + "username": u.username if authenticated and u else None, "registration_open": registration_open, } diff --git a/frontend/src/components/admin/IAMPage.tsx b/frontend/src/components/admin/IAMPage.tsx index aed1a85..3800b4c 100644 --- a/frontend/src/components/admin/IAMPage.tsx +++ b/frontend/src/components/admin/IAMPage.tsx @@ -20,6 +20,7 @@ import { useUpdateConfig, getErrorMessage, } from '@/hooks/useAdmin'; +import { useAuth } from '@/hooks/useAuth'; import { getRelativeTime } from '@/lib/date-utils'; import type { AdminUserDetail, UserRole } from '@/types'; import { cn } from '@/lib/utils'; @@ -55,6 +56,7 @@ function RoleBadge({ role }: { role: UserRole }) { export default function IAMPage() { const [createOpen, setCreateOpen] = useState(false); + const { authStatus } = useAuth(); const { data: users, isLoading: usersLoading } = useAdminUsers(); const { data: dashboard } = useAdminDashboard(); @@ -205,7 +207,7 @@ export default function IAMPage() { {getRelativeTime(user.created_at)} - + ))} diff --git a/frontend/src/components/admin/UserActionsMenu.tsx b/frontend/src/components/admin/UserActionsMenu.tsx index c2d8c43..a2318e0 100644 --- a/frontend/src/components/admin/UserActionsMenu.tsx +++ b/frontend/src/components/admin/UserActionsMenu.tsx @@ -30,6 +30,7 @@ import { cn } from '@/lib/utils'; interface UserActionsMenuProps { user: AdminUserDetail; + currentUsername: string | null; } const ROLES: { value: UserRole; label: string }[] = [ @@ -38,7 +39,7 @@ const ROLES: { value: UserRole; label: string }[] = [ { value: 'public_event_manager', label: 'Public Event Manager' }, ]; -export default function UserActionsMenu({ user }: UserActionsMenuProps) { +export default function UserActionsMenu({ user, currentUsername }: UserActionsMenuProps) { const [open, setOpen] = useState(false); const [roleSubmenuOpen, setRoleSubmenuOpen] = useState(false); const [tempPassword, setTempPassword] = useState(null); @@ -91,8 +92,14 @@ export default function UserActionsMenu({ user }: UserActionsMenuProps) { handleAction(() => revokeSessions.mutateAsync(user.id), 'Sessions revoked'); }); - const deleteUserConfirm = useConfirmAction(() => { - handleAction(() => deleteUser.mutateAsync(user.id), 'User permanently deleted'); + const deleteUserConfirm = useConfirmAction(async () => { + try { + const result = await deleteUser.mutateAsync(user.id); + toast.success(`User '${(result as { deleted_username: string }).deleted_username}' permanently deleted`); + setOpen(false); + } catch (err) { + toast.error(getErrorMessage(err, 'Delete failed')); + } }); const isLoading = @@ -283,9 +290,11 @@ export default function UserActionsMenu({ user }: UserActionsMenuProps) { {revokeSessionsConfirm.confirming ? 'Sure? Click to confirm' : 'Revoke All Sessions'} + {/* Delete User — hidden for own account */} + {currentUsername !== user.username && ( + <>
- {/* Delete User — destructive, red two-click confirm */} + + )}
)} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 542e3d4..ba85daf 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -194,6 +194,7 @@ export interface AuthStatus { authenticated: boolean; setup_required: boolean; role: UserRole | null; + username: string | null; registration_open: boolean; }