fix users deleted and actived desactived
This commit is contained in:
parent
591213aaf7
commit
cf029b1be4
@ -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}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user