fix users deleted and actived desactived

This commit is contained in:
David 2025-11-30 17:36:34 +01:00
parent 591213aaf7
commit cf029b1be4
2 changed files with 97 additions and 18 deletions

View File

@ -307,18 +307,17 @@ export class UsersController {
@Param('id', ParseUUIDPipe) id: string, @Param('id', ParseUUIDPipe) id: string,
@CurrentUser() currentUser: UserPayload @CurrentUser() currentUser: UserPayload
): Promise<void> { ): Promise<void> {
this.logger.log(`[Admin: ${currentUser.email}] Deactivating user: ${id}`); this.logger.log(`[Admin: ${currentUser.email}] Deleting user: ${id}`);
const user = await this.userRepository.findById(id); const user = await this.userRepository.findById(id);
if (!user) { if (!user) {
throw new NotFoundException(`User ${id} not found`); throw new NotFoundException(`User ${id} not found`);
} }
// Deactivate user // Permanently delete user from database
user.deactivate(); await this.userRepository.deleteById(id);
await this.userRepository.save(user);
this.logger.log(`User deactivated successfully: ${id}`); this.logger.log(`User deleted successfully: ${id}`);
} }
/** /**

View File

@ -18,6 +18,8 @@ export default function UsersManagementPage() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { user: currentUser } = useAuth(); const { user: currentUser } = useAuth();
const [showInviteModal, setShowInviteModal] = useState(false); const [showInviteModal, setShowInviteModal] = useState(false);
const [openMenuId, setOpenMenuId] = useState<string | null>(null);
const [menuPosition, setMenuPosition] = useState<{ top: number; left: number } | null>(null);
const [inviteForm, setInviteForm] = useState({ const [inviteForm, setInviteForm] = useState({
email: '', email: '',
firstName: '', firstName: '',
@ -185,14 +187,14 @@ export default function UsersManagementPage() {
)} )}
{/* Users Table */} {/* Users Table */}
<div className="bg-white rounded-lg shadow overflow-hidden"> <div className="bg-white rounded-lg shadow">
{isLoading ? ( {isLoading ? (
<div className="px-6 py-12 text-center text-gray-500"> <div className="px-6 py-12 text-center text-gray-500">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div> <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
Loading users... Loading users...
</div> </div>
) : users?.users && users.users.length > 0 ? ( ) : users?.users && users.users.length > 0 ? (
<div className="overflow-x-auto"> <div className="overflow-x-auto overflow-y-visible">
<table className="min-w-full divide-y divide-gray-200"> <table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50"> <thead className="bg-gray-50">
<tr> <tr>
@ -254,28 +256,39 @@ export default function UsersManagementPage() {
</select> </select>
</td> </td>
<td className="px-6 py-4 whitespace-nowrap"> <td className="px-6 py-4 whitespace-nowrap">
<button <span
onClick={() => handleToggleActive(user.id, user.isActive)}
disabled={toggleActiveMutation.isPending}
className={`px-3 py-1 inline-flex text-xs leading-5 font-semibold rounded-full ${ className={`px-3 py-1 inline-flex text-xs leading-5 font-semibold rounded-full ${
user.isActive user.isActive
? 'bg-green-100 text-green-800 hover:bg-green-200' ? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800 hover:bg-red-200' : 'bg-red-100 text-red-800'
} disabled:opacity-50 disabled:cursor-not-allowed`} }`}
> >
{user.isActive ? 'Active' : 'Inactive'} {user.isActive ? 'Active' : 'Inactive'}
</button> </span>
</td> </td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{new Date(user.createdAt).toLocaleDateString()} {new Date(user.createdAt).toLocaleDateString()}
</td> </td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> <td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button <button
onClick={() => handleDelete(user.id)} onClick={(e) => {
disabled={deleteMutation.isPending} if (openMenuId === user.id) {
className="text-red-600 hover:text-red-900 ml-4 disabled:opacity-50 disabled:cursor-not-allowed" setOpenMenuId(null);
setMenuPosition(null);
} else {
const rect = e.currentTarget.getBoundingClientRect();
setMenuPosition({
top: rect.bottom + 5,
left: rect.left - 180
});
setOpenMenuId(user.id);
}
}}
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
> >
Delete <svg className="w-5 h-5 text-gray-600" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" />
</svg>
</button> </button>
</td> </td>
</tr> </tr>
@ -313,6 +326,73 @@ export default function UsersManagementPage() {
)} )}
</div> </div>
{/* Actions Menu Modal */}
{openMenuId && menuPosition && (
<>
<div
className="fixed inset-0 z-[998]"
onClick={() => {
setOpenMenuId(null);
setMenuPosition(null);
}}
/>
<div
className="fixed w-56 bg-white border-2 border-gray-300 rounded-lg shadow-2xl z-[999]"
style={{
top: `${menuPosition.top}px`,
left: `${menuPosition.left}px`,
}}
>
<div className="py-2">
<button
onClick={() => {
const user = users?.users.find(u => u.id === openMenuId);
if (user) {
handleToggleActive(user.id, user.isActive);
}
setOpenMenuId(null);
setMenuPosition(null);
}}
disabled={toggleActiveMutation.isPending}
className="w-full px-4 py-3 text-left hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed flex items-center space-x-3 border-b border-gray-200"
>
{users?.users.find(u => u.id === openMenuId)?.isActive ? (
<>
<svg className="w-5 h-5 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636" />
</svg>
<span className="text-sm font-medium text-gray-700">Désactiver</span>
</>
) : (
<>
<svg className="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="text-sm font-medium text-gray-700">Activer</span>
</>
)}
</button>
<button
onClick={() => {
if (openMenuId) {
handleDelete(openMenuId);
}
setOpenMenuId(null);
setMenuPosition(null);
}}
disabled={deleteMutation.isPending}
className="w-full px-4 py-3 text-left hover:bg-red-50 disabled:opacity-50 disabled:cursor-not-allowed flex items-center space-x-3"
>
<svg className="w-5 h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
<span className="text-sm font-medium text-red-600">Supprimer</span>
</button>
</div>
</div>
</>
)}
{/* Invite Modal */} {/* Invite Modal */}
{showInviteModal && ( {showInviteModal && (
<div className="fixed inset-0 z-50 overflow-y-auto"> <div className="fixed inset-0 z-50 overflow-y-auto">