import React, { useState, useEffect } from "react"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { Switch } from "@/components/ui/switch"; import { Separator } from "@/components/ui/separator"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { useTranslation } from "react-i18next"; import { UserCog, Trash2, Plus, AlertCircle, Shield, Key, Clock, } from "lucide-react"; import { toast } from "sonner"; import { useConfirmation } from "@/hooks/use-confirmation"; import { getUserRoles, getRoles, assignRoleToUser, removeRoleFromUser, makeUserAdmin, removeAdminStatus, initiatePasswordReset, revokeAllUserSessions, deleteUser, type UserRole, type Role, } from "@/ui/main-axios"; interface User { id: string; username: string; is_admin: boolean; is_oidc: boolean; password_hash?: string; } interface UserEditDialogProps { open: boolean; onOpenChange: (open: boolean) => void; user: User | null; currentUser: { id: string; username: string } | null; onSuccess: () => void; allowPasswordLogin: boolean; } export function UserEditDialog({ open, onOpenChange, user, currentUser, onSuccess, allowPasswordLogin, }: UserEditDialogProps) { const { t } = useTranslation(); const { confirmWithToast } = useConfirmation(); const [adminLoading, setAdminLoading] = useState(false); const [passwordResetLoading, setPasswordResetLoading] = useState(false); const [sessionLoading, setSessionLoading] = useState(false); const [deleteLoading, setDeleteLoading] = useState(false); const [rolesLoading, setRolesLoading] = useState(false); const [userRoles, setUserRoles] = useState([]); const [availableRoles, setAvailableRoles] = useState([]); const [isAdmin, setIsAdmin] = useState(false); const isCurrentUser = user?.id === currentUser?.id; useEffect(() => { if (open && user) { setIsAdmin(user.is_admin); loadRoles(); } }, [open, user]); const loadRoles = async () => { if (!user) return; setRolesLoading(true); try { const [rolesResponse, allRolesResponse] = await Promise.all([ getUserRoles(user.id), getRoles(), ]); setUserRoles(rolesResponse.roles || []); setAvailableRoles(allRolesResponse.roles || []); } catch (error) { console.error("Failed to load roles:", error); toast.error(t("rbac.failedToLoadRoles")); } finally { setRolesLoading(false); } }; const handleToggleAdmin = async (checked: boolean) => { if (!user) return; if (isCurrentUser) { toast.error(t("admin.cannotRemoveOwnAdmin")); return; } // Close dialog temporarily to show confirmation toast on top const userToUpdate = user; onOpenChange(false); const confirmed = await confirmWithToast({ title: checked ? t("admin.makeUserAdmin") : t("admin.removeAdmin"), description: checked ? t("admin.confirmMakeAdmin", { username: userToUpdate.username }) : t("admin.confirmRemoveAdmin", { username: userToUpdate.username }), confirmText: checked ? t("admin.makeAdmin") : t("admin.removeAdmin"), cancelText: t("common.cancel"), variant: checked ? "default" : "destructive", }); if (!confirmed) { onOpenChange(true); return; } setAdminLoading(true); try { if (checked) { await makeUserAdmin(userToUpdate.username); toast.success( t("admin.userIsNowAdmin", { username: userToUpdate.username }), ); } else { await removeAdminStatus(userToUpdate.username); toast.success( t("admin.adminStatusRemoved", { username: userToUpdate.username }), ); } setIsAdmin(checked); onSuccess(); onOpenChange(true); } catch (error) { console.error("Failed to toggle admin status:", error); toast.error( checked ? t("admin.failedToMakeUserAdmin") : t("admin.failedToRemoveAdminStatus"), ); onOpenChange(true); } finally { setAdminLoading(false); } }; const handlePasswordReset = async () => { if (!user) return; // Close dialog temporarily to show confirmation toast on top const userToReset = user; onOpenChange(false); const confirmed = await confirmWithToast({ title: t("admin.resetUserPassword"), description: `${t("admin.passwordResetWarning")} (${userToReset.username})`, confirmText: t("admin.resetUserPassword"), cancelText: t("common.cancel"), variant: "destructive", }); if (!confirmed) { onOpenChange(true); return; } setPasswordResetLoading(true); try { await initiatePasswordReset(userToReset.username); toast.success( t("admin.passwordResetInitiated", { username: userToReset.username }), ); onSuccess(); onOpenChange(true); } catch (error) { console.error("Failed to reset password:", error); toast.error(t("admin.failedToResetPassword")); onOpenChange(true); } finally { setPasswordResetLoading(false); } }; const handleAssignRole = async (roleId: number) => { if (!user) return; try { await assignRoleToUser(user.id, roleId); toast.success( t("rbac.roleAssignedSuccessfully", { username: user.username }), ); await loadRoles(); } catch (error) { console.error("Failed to assign role:", error); toast.error(t("rbac.failedToAssignRole")); } }; const handleRemoveRole = async (roleId: number) => { if (!user) return; // Close dialog temporarily to show confirmation toast on top const userToUpdate = user; onOpenChange(false); const confirmed = await confirmWithToast({ title: t("rbac.confirmRemoveRole"), description: t("rbac.confirmRemoveRoleDescription"), confirmText: t("common.remove"), cancelText: t("common.cancel"), variant: "destructive", }); if (!confirmed) { onOpenChange(true); return; } try { await removeRoleFromUser(userToUpdate.id, roleId); toast.success( t("rbac.roleRemovedSuccessfully", { username: userToUpdate.username }), ); await loadRoles(); onOpenChange(true); } catch (error) { console.error("Failed to remove role:", error); toast.error(t("rbac.failedToRemoveRole")); onOpenChange(true); } }; const handleRevokeAllSessions = async () => { if (!user) return; const isRevokingSelf = isCurrentUser; // Close dialog temporarily to show confirmation toast on top const userToUpdate = user; onOpenChange(false); const confirmed = await confirmWithToast({ title: t("admin.revokeAllSessions"), description: isRevokingSelf ? t("admin.confirmRevokeOwnSessions") : t("admin.confirmRevokeAllSessions"), confirmText: t("admin.revoke"), cancelText: t("common.cancel"), variant: "destructive", }); if (!confirmed) { onOpenChange(true); return; } setSessionLoading(true); try { const data = await revokeAllUserSessions(userToUpdate.id); toast.success(data.message || t("admin.sessionsRevokedSuccessfully")); if (isRevokingSelf) { setTimeout(() => { window.location.reload(); }, 1000); } else { onSuccess(); onOpenChange(true); } } catch (error) { console.error("Failed to revoke sessions:", error); toast.error(t("admin.failedToRevokeSessions")); onOpenChange(true); } finally { setSessionLoading(false); } }; const handleDeleteUser = async () => { if (!user) return; if (isCurrentUser) { toast.error(t("admin.cannotDeleteSelf")); return; } // Close dialog temporarily to show confirmation toast on top const userToDelete = user; onOpenChange(false); const confirmed = await confirmWithToast({ title: t("admin.deleteUserTitle"), description: t("admin.deleteUser", { username: userToDelete.username }), confirmText: t("common.delete"), cancelText: t("common.cancel"), variant: "destructive", }); if (!confirmed) { // Reopen dialog if user cancels onOpenChange(true); return; } setDeleteLoading(true); try { await deleteUser(userToDelete.username); toast.success( t("admin.userDeletedSuccessfully", { username: userToDelete.username }), ); onSuccess(); } catch (error) { console.error("Failed to delete user:", error); toast.error(t("admin.failedToDeleteUser")); onOpenChange(true); } finally { setDeleteLoading(false); } }; const getAuthTypeDisplay = (): string => { if (!user) return ""; if (user.is_oidc && user.password_hash) { return t("admin.dualAuth"); } else if (user.is_oidc) { return t("admin.externalOIDC"); } else { return t("admin.localPassword"); } }; if (!user) return null; const showPasswordReset = allowPasswordLogin && (user.password_hash || !user.is_oidc); return ( {t("admin.manageUser")}: {user.username} {t("admin.manageUserDescription")}
{/* READ-ONLY INFO SECTION */}

{user.username}

{getAuthTypeDisplay()}

{isAdmin ? ( {t("admin.adminBadge")} ) : ( t("admin.regularUser") )}

{user.id}

{/* ADMIN TOGGLE SECTION */}

{t("admin.administratorRole")}

{t("admin.administratorRoleDescription")}

{isCurrentUser && (

{t("admin.cannotModifyOwnAdminStatus")}

)}
{/* PASSWORD RESET SECTION */} {showPasswordReset && ( <>
{t("common.warning")} {t("admin.passwordResetWarning")}
)} {/* ROLE MANAGEMENT SECTION */}
{rolesLoading ? (
{t("common.loading")}
) : ( <> {/* Current Roles */}
{userRoles.length === 0 ? (

{t("rbac.noRolesAssigned")}

) : (
{userRoles.map((role) => (

{t(role.roleDisplayName)}

{role.roleName}

{role.isSystem && ( {t("rbac.systemRole")} )} {!role.isSystem && ( )}
))}
)}
{/* Assign New Role */}
{availableRoles .filter( (role) => !role.isSystem && !userRoles.some((ur) => ur.roleId === role.id), ) .map((role) => ( ))} {availableRoles.filter( (role) => !role.isSystem && !userRoles.some((ur) => ur.roleId === role.id), ).length === 0 && (

{t("rbac.noCustomRolesToAssign")}

)}
)}
{/* SESSION MANAGEMENT SECTION */}

{t("admin.revokeAllSessions")}

{t("admin.revokeAllSessionsDescription")}

{/* DANGER ZONE - DELETE USER */}
{t("admin.deleteUserTitle")} {t("admin.deleteUserWarning")} {isCurrentUser && (

{t("admin.cannotDeleteSelf")}

)}
); }