"use client"; import { useState, useEffect } from "react"; import { Search, Plus, MoreHorizontal, Trash2, Edit2, Users, UserPlus, X, Check, Loader2 } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { useToast } from "@/components/ui/use-toast"; // Types interface User { id: string; username: string; firstName: string; lastName: string; email: string; roles: string[]; enabled: boolean; } interface Group { id: string; name: string; path: string; membersCount: number; } type ActiveTab = "users" | "groups"; type EditMode = null | { type: "user" | "group"; id: string; action: "edit" | "roles" | "members" }; export default function EquipePage() { const { toast } = useToast(); const [activeTab, setActiveTab] = useState("users"); const [searchTerm, setSearchTerm] = useState(""); const [loading, setLoading] = useState(true); const [actionLoading, setActionLoading] = useState(null); // Data const [users, setUsers] = useState([]); const [groups, setGroups] = useState([]); const [roles, setRoles] = useState<{ id: string; name: string }[]>([]); // Inline edit states const [editMode, setEditMode] = useState(null); const [editData, setEditData] = useState({}); // Group members state const [groupMembers, setGroupMembers] = useState([]); const [availableUsers, setAvailableUsers] = useState([]); // Fetch data on mount useEffect(() => { fetchData(); }, []); const fetchData = async () => { setLoading(true); try { const [usersRes, groupsRes, rolesRes] = await Promise.all([ fetch("/api/users"), fetch("/api/groups"), fetch("/api/roles") ]); if (usersRes.ok) { const usersData = await usersRes.json(); setUsers(Array.isArray(usersData) ? usersData : []); } if (groupsRes.ok) { const groupsData = await groupsRes.json(); setGroups(Array.isArray(groupsData) ? groupsData : []); } if (rolesRes.ok) { const rolesData = await rolesRes.json(); setRoles(Array.isArray(rolesData) ? rolesData : []); } } catch (error) { console.error("Error fetching data:", error); toast({ title: "Erreur", description: "Impossible de charger les données", variant: "destructive" }); } finally { setLoading(false); } }; // Filter functions const filteredUsers = users.filter(user => user.username?.toLowerCase().includes(searchTerm.toLowerCase()) || user.email?.toLowerCase().includes(searchTerm.toLowerCase()) || user.firstName?.toLowerCase().includes(searchTerm.toLowerCase()) || user.lastName?.toLowerCase().includes(searchTerm.toLowerCase()) ); const filteredGroups = groups.filter(group => group.name?.toLowerCase().includes(searchTerm.toLowerCase()) ); // User actions const handleEditUser = (user: User) => { setEditMode({ type: "user", id: user.id, action: "edit" }); setEditData({ firstName: user.firstName || "", lastName: user.lastName || "", email: user.email || "" }); }; const handleEditRoles = (user: User) => { setEditMode({ type: "user", id: user.id, action: "roles" }); setEditData({ roles: user.roles || [] }); }; const saveUserEdit = async (userId: string) => { setActionLoading(userId); try { const response = await fetch(`/api/users/${userId}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(editData) }); if (!response.ok) throw new Error("Failed to update user"); setUsers(prev => prev.map(u => u.id === userId ? { ...u, ...editData } : u )); toast({ title: "Succès", description: "Utilisateur modifié" }); setEditMode(null); } catch (error) { toast({ title: "Erreur", description: "Échec de la modification", variant: "destructive" }); } finally { setActionLoading(null); } }; const saveUserRoles = async (userId: string) => { setActionLoading(userId); try { const response = await fetch(`/api/users/${userId}/roles`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ roles: editData.roles }) }); if (!response.ok) throw new Error("Failed to update roles"); setUsers(prev => prev.map(u => u.id === userId ? { ...u, roles: editData.roles } : u )); toast({ title: "Succès", description: "Rôles mis à jour" }); setEditMode(null); } catch (error) { toast({ title: "Erreur", description: "Échec de la mise à jour des rôles", variant: "destructive" }); } finally { setActionLoading(null); } }; const deleteUser = async (userId: string, email: string) => { if (!confirm("Êtes-vous sûr de vouloir supprimer cet utilisateur ?")) return; setActionLoading(userId); try { const response = await fetch(`/api/users?id=${userId}&email=${encodeURIComponent(email)}`, { method: "DELETE" }); if (!response.ok) throw new Error("Failed to delete user"); setUsers(prev => prev.filter(u => u.id !== userId)); toast({ title: "Succès", description: "Utilisateur supprimé" }); } catch (error) { toast({ title: "Erreur", description: "Échec de la suppression", variant: "destructive" }); } finally { setActionLoading(null); } }; // Group actions const handleEditGroup = (group: Group) => { setEditMode({ type: "group", id: group.id, action: "edit" }); setEditData({ name: group.name }); }; const handleManageMembers = async (group: Group) => { setActionLoading(group.id); try { const membersRes = await fetch(`/api/groups/${group.id}/members`); if (membersRes.ok) { const members = await membersRes.json(); setGroupMembers(Array.isArray(members) ? members : []); setAvailableUsers(users.filter(u => !members.some((m: User) => m.id === u.id))); } setEditMode({ type: "group", id: group.id, action: "members" }); } catch (error) { toast({ title: "Erreur", description: "Impossible de charger les membres", variant: "destructive" }); } finally { setActionLoading(null); } }; const saveGroupEdit = async (groupId: string) => { setActionLoading(groupId); try { const response = await fetch(`/api/groups/${groupId}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: editData.name }) }); if (!response.ok) throw new Error("Failed to update group"); setGroups(prev => prev.map(g => g.id === groupId ? { ...g, name: editData.name } : g )); toast({ title: "Succès", description: "Groupe modifié" }); setEditMode(null); } catch (error) { toast({ title: "Erreur", description: "Échec de la modification", variant: "destructive" }); } finally { setActionLoading(null); } }; const deleteGroup = async (groupId: string) => { if (!confirm("Êtes-vous sûr de vouloir supprimer ce groupe ?")) return; setActionLoading(groupId); try { const response = await fetch(`/api/groups/${groupId}`, { method: "DELETE" }); if (!response.ok) throw new Error("Failed to delete group"); setGroups(prev => prev.filter(g => g.id !== groupId)); toast({ title: "Succès", description: "Groupe supprimé" }); } catch (error) { toast({ title: "Erreur", description: "Échec de la suppression", variant: "destructive" }); } finally { setActionLoading(null); } }; const addMemberToGroup = async (userId: string) => { if (!editMode || editMode.action !== "members") return; setActionLoading(userId); try { const response = await fetch(`/api/groups/${editMode.id}/members`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userId }) }); if (!response.ok) throw new Error("Failed to add member"); const user = availableUsers.find(u => u.id === userId); if (user) { setGroupMembers(prev => [...prev, user]); setAvailableUsers(prev => prev.filter(u => u.id !== userId)); setGroups(prev => prev.map(g => g.id === editMode.id ? { ...g, membersCount: g.membersCount + 1 } : g )); } toast({ title: "Succès", description: "Membre ajouté" }); } catch (error) { toast({ title: "Erreur", description: "Échec de l'ajout", variant: "destructive" }); } finally { setActionLoading(null); } }; const removeMemberFromGroup = async (userId: string) => { if (!editMode || editMode.action !== "members") return; setActionLoading(userId); try { const response = await fetch(`/api/groups/${editMode.id}/members/${userId}`, { method: "DELETE" }); if (!response.ok) throw new Error("Failed to remove member"); const user = groupMembers.find(u => u.id === userId); if (user) { setGroupMembers(prev => prev.filter(u => u.id !== userId)); setAvailableUsers(prev => [...prev, user]); setGroups(prev => prev.map(g => g.id === editMode.id ? { ...g, membersCount: Math.max(0, g.membersCount - 1) } : g )); } toast({ title: "Succès", description: "Membre retiré" }); } catch (error) { toast({ title: "Erreur", description: "Échec du retrait", variant: "destructive" }); } finally { setActionLoading(null); } }; const toggleRole = (roleName: string) => { setEditData((prev: any) => ({ ...prev, roles: prev.roles.includes(roleName) ? prev.roles.filter((r: string) => r !== roleName) : [...prev.roles, roleName] })); }; const cancelEdit = () => { setEditMode(null); setEditData({}); setGroupMembers([]); setAvailableUsers([]); }; return (
{/* Header */}

Gestion des équipes

setSearchTerm(e.target.value)} />
{/* Tabs */}
{/* Content */}
{loading ? (
) : (
{/* Main List */}
{activeTab === "users" ? ( /* Users Table */ {filteredUsers.map(user => ( ))}
Utilisateur Email Rôles Actions
{user.firstName?.[0] || ""}{user.lastName?.[0] || user.username?.[0] || "?"}
{user.firstName} {user.lastName}
@{user.username}
{user.email}
{(user.roles || []).slice(0, 3).map(role => ( {role} ))} {(user.roles || []).length > 3 && ( +{user.roles.length - 3} )}
) : ( /* Groups Table */ {filteredGroups.map(group => ( ))}
Groupe Chemin Membres Actions
{group.name}
{group.path} {group.membersCount} membres
)} {/* Empty states */} {activeTab === "users" && filteredUsers.length === 0 && (

Aucun utilisateur trouvé

)} {activeTab === "groups" && filteredGroups.length === 0 && (

Aucun groupe trouvé

)}
{/* Side Panel for Editing */} {editMode && (

{editMode.action === "edit" && "Modifier"} {editMode.action === "roles" && "Gérer les rôles"} {editMode.action === "members" && "Gérer les membres"}

{/* Edit User Form */} {editMode.type === "user" && editMode.action === "edit" && (
setEditData({ ...editData, firstName: e.target.value })} className="mt-1 h-9" />
setEditData({ ...editData, lastName: e.target.value })} className="mt-1 h-9" />
setEditData({ ...editData, email: e.target.value })} className="mt-1 h-9" />
)} {/* Edit Roles Form */} {editMode.type === "user" && editMode.action === "roles" && (
{roles.map(role => ( ))}
)} {/* Edit Group Form */} {editMode.type === "group" && editMode.action === "edit" && (
setEditData({ ...editData, name: e.target.value })} className="mt-1 h-9" />
)} {/* Manage Members Form */} {editMode.type === "group" && editMode.action === "members" && (
{/* Current Members */}
{groupMembers.length === 0 ? (

Aucun membre

) : ( groupMembers.map(member => (
{member.firstName} {member.lastName}
)) )}
{/* Available Users */}
{availableUsers.length === 0 ? (

Tous les utilisateurs sont membres

) : ( availableUsers.map(user => (
{user.firstName} {user.lastName}
)) )}
)}
)}
)}
); }