import { useState, useRef, useEffect } from 'react'; import { toast } from 'sonner'; import { MoreHorizontal, ShieldCheck, ShieldOff, KeyRound, UserX, UserCheck, LogOut, Smartphone, SmartphoneOff, ChevronRight, Loader2, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { useConfirmAction } from '@/hooks/useConfirmAction'; import { useUpdateRole, useResetPassword, useDisableMfa, useEnforceMfa, useRemoveMfaEnforcement, useToggleUserActive, useRevokeSessions, getErrorMessage, } from '@/hooks/useAdmin'; import type { AdminUserDetail, UserRole } from '@/types'; import { cn } from '@/lib/utils'; interface UserActionsMenuProps { user: AdminUserDetail; } const ROLES: { value: UserRole; label: string }[] = [ { value: 'admin', label: 'Admin' }, { value: 'standard', label: 'Standard' }, { value: 'public_event_manager', label: 'Public Event Manager' }, ]; export default function UserActionsMenu({ user }: UserActionsMenuProps) { const [open, setOpen] = useState(false); const [roleSubmenuOpen, setRoleSubmenuOpen] = useState(false); const [showResetPassword, setShowResetPassword] = useState(false); const [newPassword, setNewPassword] = useState(''); const menuRef = useRef(null); const updateRole = useUpdateRole(); const resetPassword = useResetPassword(); const disableMfa = useDisableMfa(); const enforceMfa = useEnforceMfa(); const removeMfaEnforcement = useRemoveMfaEnforcement(); const toggleActive = useToggleUserActive(); const revokeSessions = useRevokeSessions(); // Close on outside click useEffect(() => { const handleOutside = (e: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(e.target as Node)) { setOpen(false); setRoleSubmenuOpen(false); } }; if (open) document.addEventListener('mousedown', handleOutside); return () => document.removeEventListener('mousedown', handleOutside); }, [open]); const handleAction = async (fn: () => Promise, successMsg: string) => { try { await fn(); toast.success(successMsg); setOpen(false); } catch (err) { toast.error(getErrorMessage(err, 'Action failed')); } }; // Two-click confirms const disableMfaConfirm = useConfirmAction(() => { handleAction(() => disableMfa.mutateAsync(user.id), 'MFA disabled'); }); const toggleActiveConfirm = useConfirmAction(() => { handleAction( () => toggleActive.mutateAsync({ userId: user.id, active: !user.is_active }), user.is_active ? 'Account disabled' : 'Account enabled' ); }); const revokeSessionsConfirm = useConfirmAction(() => { handleAction(() => revokeSessions.mutateAsync(user.id), 'Sessions revoked'); }); const isLoading = updateRole.isPending || resetPassword.isPending || disableMfa.isPending || enforceMfa.isPending || removeMfaEnforcement.isPending || toggleActive.isPending || revokeSessions.isPending; return (
{open && (
{/* Edit Role */}
{roleSubmenuOpen && (
setRoleSubmenuOpen(true)} onMouseLeave={() => setRoleSubmenuOpen(false)} > {ROLES.map(({ value, label }) => ( ))}
)}
{/* Reset Password */} {!showResetPassword ? ( ) : (
setNewPassword(e.target.value)} autoFocus />
)}
{/* MFA actions */} {user.mfa_enforce_pending ? ( ) : ( )} {user.totp_enabled && ( )}
{/* Disable / Enable Account */} {/* Revoke Sessions */}
)}
); }