feat: delete users - not tested

This commit is contained in:
Eric Gullickson
2025-12-22 18:20:25 -06:00
parent 91b4534e76
commit 4897f0a52c
73 changed files with 4923 additions and 62 deletions

View File

@@ -15,6 +15,7 @@ import {
useReactivateUser,
useUpdateUserProfile,
usePromoteToAdmin,
useHardDeleteUser,
} from '../hooks/useUsers';
import {
ManagedUser,
@@ -103,6 +104,7 @@ export const AdminUsersMobileScreen: React.FC = () => {
const reactivateMutation = useReactivateUser();
const updateProfileMutation = useUpdateUserProfile();
const promoteToAdminMutation = usePromoteToAdmin();
const hardDeleteMutation = useHardDeleteUser();
// Selected user for actions
const [selectedUser, setSelectedUser] = useState<ManagedUser | null>(null);
@@ -115,6 +117,9 @@ export const AdminUsersMobileScreen: React.FC = () => {
const [editDisplayName, setEditDisplayName] = useState('');
const [showPromoteModal, setShowPromoteModal] = useState(false);
const [promoteRole, setPromoteRole] = useState<'admin' | 'super_admin'>('admin');
const [showHardDeleteModal, setShowHardDeleteModal] = useState(false);
const [hardDeleteReason, setHardDeleteReason] = useState('');
const [hardDeleteConfirmText, setHardDeleteConfirmText] = useState('');
// Handlers
const handleSearch = useCallback(() => {
@@ -256,6 +261,34 @@ export const AdminUsersMobileScreen: React.FC = () => {
setSelectedUser(null);
}, []);
const handleHardDeleteClick = useCallback(() => {
setShowUserActions(false);
setShowHardDeleteModal(true);
}, []);
const handleHardDeleteConfirm = useCallback(() => {
if (selectedUser && hardDeleteConfirmText === 'DELETE') {
hardDeleteMutation.mutate(
{ auth0Sub: selectedUser.auth0Sub, reason: hardDeleteReason || undefined },
{
onSuccess: () => {
setShowHardDeleteModal(false);
setHardDeleteReason('');
setHardDeleteConfirmText('');
setSelectedUser(null);
},
}
);
}
}, [selectedUser, hardDeleteReason, hardDeleteConfirmText, hardDeleteMutation]);
const handleHardDeleteCancel = useCallback(() => {
setShowHardDeleteModal(false);
setHardDeleteReason('');
setHardDeleteConfirmText('');
setSelectedUser(null);
}, []);
const handleLoadMore = useCallback(() => {
setParams(prev => ({ ...prev, page: (prev.page || 1) + 1 }));
}, []);
@@ -527,6 +560,15 @@ export const AdminUsersMobileScreen: React.FC = () => {
Deactivate User
</button>
)}
{!selectedUser.isAdmin && (
<button
onClick={handleHardDeleteClick}
className="w-full py-3 text-left text-red-600 font-medium min-h-[44px]"
>
Delete Permanently
</button>
)}
</div>
</div>
)}
@@ -716,6 +758,82 @@ export const AdminUsersMobileScreen: React.FC = () => {
</p>
</div>
</Modal>
{/* Hard Delete Confirmation Modal */}
<Modal
isOpen={showHardDeleteModal}
onClose={() => !hardDeleteMutation.isPending && handleHardDeleteCancel()}
title="Permanently Delete User"
actions={
<>
<button
onClick={handleHardDeleteCancel}
disabled={hardDeleteMutation.isPending}
className="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg font-medium min-h-[44px]"
>
Cancel
</button>
<button
onClick={handleHardDeleteConfirm}
disabled={hardDeleteMutation.isPending || hardDeleteConfirmText !== 'DELETE'}
className="px-4 py-2 bg-red-600 text-white rounded-lg font-medium min-h-[44px] disabled:opacity-50"
>
{hardDeleteMutation.isPending ? 'Deleting...' : 'Delete'}
</button>
</>
}
>
<div className="space-y-4">
<div className="bg-red-50 border border-red-200 rounded-lg p-3">
<p className="text-sm font-semibold text-red-800">
Warning: This action cannot be undone!
</p>
<p className="text-sm text-red-700 mt-1">
All user data will be permanently deleted, including vehicles, fuel logs,
maintenance records, and documents.
</p>
</div>
<p className="text-slate-600">
Are you sure you want to permanently delete{' '}
<strong>{selectedUser?.email}</strong>?
</p>
<div>
<label className="text-sm font-medium text-slate-600 block mb-1">
Reason for deletion
</label>
<textarea
value={hardDeleteReason}
onChange={(e) => setHardDeleteReason(e.target.value)}
placeholder="GDPR request, user request, etc..."
className="w-full px-3 py-2 rounded-lg border border-slate-200 resize-none min-h-[60px]"
rows={2}
/>
</div>
<div>
<label className="text-sm font-medium text-slate-600 block mb-1">
Type <strong>DELETE</strong> to confirm
</label>
<input
type="text"
value={hardDeleteConfirmText}
onChange={(e) => setHardDeleteConfirmText(e.target.value.toUpperCase())}
placeholder="Type DELETE"
className={`w-full px-3 py-2 rounded-lg border min-h-[44px] ${
hardDeleteConfirmText && hardDeleteConfirmText !== 'DELETE'
? 'border-red-500'
: 'border-slate-200'
}`}
style={{ fontSize: '16px' }}
/>
{hardDeleteConfirmText && hardDeleteConfirmText !== 'DELETE' && (
<p className="text-sm text-red-500 mt-1">Please type DELETE exactly</p>
)}
</div>
</div>
</Modal>
</MobileContainer>
);
};