update api users and groups and users 10

This commit is contained in:
Alma 2025-04-09 21:25:10 +02:00
parent 01a3a3dd55
commit 992d5ec2a0
2 changed files with 201 additions and 22 deletions

View File

@ -37,6 +37,12 @@ interface Group {
membersCount: number;
}
interface User {
id: string;
username: string;
email: string;
}
interface ApiError {
message: string;
}
@ -54,6 +60,9 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
const [modifyGroupDialog, setModifyGroupDialog] = useState(false);
const [selectedGroup, setSelectedGroup] = useState<Group | null>(null);
const [modifiedGroupName, setModifiedGroupName] = useState("");
const [manageMembersDialog, setManageMembersDialog] = useState(false);
const [groupMembers, setGroupMembers] = useState<User[]>([]);
const [availableUsers, setAvailableUsers] = useState<User[]>([]);
useEffect(() => {
fetchGroups();
@ -200,19 +209,91 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
};
const handleManageMembers = async (groupId: string) => {
try {
const group = groups.find(g => g.id === groupId);
if (!group) return;
// TODO: Implement member management UI
setSelectedGroup(group);
try {
// Fetch group members
const membersResponse = await fetch(`/api/groups/${groupId}/members`);
if (!membersResponse.ok) throw new Error("Failed to fetch group members");
const members = await membersResponse.json();
setGroupMembers(members);
// Fetch all users
const usersResponse = await fetch("/api/users");
if (!usersResponse.ok) throw new Error("Failed to fetch users");
const users = await usersResponse.json();
setAvailableUsers(users.filter((user: User) => !members.some((m: User) => m.id === user.id)));
setManageMembersDialog(true);
} catch (error) {
toast({
title: "Info",
description: "La gestion des membres sera bientôt disponible",
title: "Erreur",
description: "Erreur lors de la récupération des membres",
variant: "destructive",
});
}
};
const handleAddMember = async (userId: string) => {
if (!selectedGroup) return;
try {
const response = await fetch(`/api/groups/${selectedGroup.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));
}
toast({
title: "Succès",
description: "Membre ajouté avec succès",
});
} catch (error) {
toast({
title: "Erreur",
description: error instanceof Error ? error.message : "Une erreur est survenue",
description: "Erreur lors de l'ajout du membre",
variant: "destructive",
});
}
};
const handleRemoveMember = async (userId: string) => {
if (!selectedGroup) return;
try {
const response = await fetch(`/api/groups/${selectedGroup.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) {
setAvailableUsers(prev => [...prev, user]);
setGroupMembers(prev => prev.filter(u => u.id !== userId));
}
toast({
title: "Succès",
description: "Membre retiré avec succès",
});
} catch (error) {
toast({
title: "Erreur",
description: "Erreur lors du retrait du membre",
variant: "destructive",
});
}
@ -350,6 +431,71 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
</div>
</DialogContent>
</Dialog>
<Dialog open={manageMembersDialog} onOpenChange={(open) => {
if (!open) {
setSelectedGroup(null);
setGroupMembers([]);
setAvailableUsers([]);
}
setManageMembersDialog(open);
}}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Gérer les membres du groupe {selectedGroup?.name}</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<div className="space-y-2">
<Label>Membres actuels</Label>
<div className="border rounded-md p-4 space-y-2">
{groupMembers.length === 0 ? (
<p className="text-sm text-gray-500">Aucun membre</p>
) : (
groupMembers.map(member => (
<div key={member.id} className="flex items-center justify-between p-2 hover:bg-gray-50 rounded-md">
<div>
<p className="font-medium">{member.username}</p>
<p className="text-sm text-gray-500">{member.email}</p>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => handleRemoveMember(member.id)}
>
Retirer
</Button>
</div>
))
)}
</div>
</div>
<div className="space-y-2">
<Label>Ajouter des membres</Label>
<div className="border rounded-md p-4 space-y-2">
{availableUsers.length === 0 ? (
<p className="text-sm text-gray-500">Aucun utilisateur disponible</p>
) : (
availableUsers.map(user => (
<div key={user.id} className="flex items-center justify-between p-2 hover:bg-gray-50 rounded-md">
<div>
<p className="font-medium">{user.username}</p>
<p className="text-sm text-gray-500">{user.email}</p>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => handleAddMember(user.id)}
>
Ajouter
</Button>
</div>
))
)}
</div>
</div>
</div>
</DialogContent>
</Dialog>
</div>
);
}

View File

@ -134,7 +134,11 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
body: JSON.stringify({
...formData,
firstName: formData.firstName,
lastName: formData.lastName,
}),
});
const data = await response.json();
@ -433,14 +437,6 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="lastName">Nom</Label>
<Input
id="lastName"
value={formData.lastName}
onChange={(e) => setFormData(prev => ({ ...prev, lastName: e.target.value }))}
/>
</div>
<div className="space-y-2">
<Label htmlFor="firstName">Prénom</Label>
<Input
@ -449,6 +445,14 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
onChange={(e) => setFormData(prev => ({ ...prev, firstName: e.target.value }))}
/>
</div>
<div className="space-y-2">
<Label htmlFor="lastName">Nom</Label>
<Input
id="lastName"
value={formData.lastName}
onChange={(e) => setFormData(prev => ({ ...prev, lastName: e.target.value }))}
/>
</div>
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
@ -543,26 +547,52 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
<TableCell className="text-right">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<Button
variant="ghost"
className="h-8 w-8 p-0"
onClick={(e) => {
e.stopPropagation();
}}
>
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem onClick={() => handleEdit(user.id)}>
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
handleEdit(user.id);
}}
>
<Edit className="mr-2 h-4 w-4" />
Modifier
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleManageRoles(user.id)}>
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
handleManageRoles(user.id);
}}
>
<UserPlus className="mr-2 h-4 w-4" />
Gérer les rôles
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleChangePassword(user.id)}>
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
handleChangePassword(user.id);
}}
>
<Key className="mr-2 h-4 w-4" />
Changer le mot de passe
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleToggleUserStatus(user.id, user.enabled)}>
<DropdownMenuItem
onClick={(e) => {
e.stopPropagation();
handleToggleUserStatus(user.id, user.enabled);
}}
>
{user.enabled ? (
<>
<Lock className="mr-2 h-4 w-4" />
@ -578,7 +608,10 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
<DropdownMenuSeparator />
<DropdownMenuItem
className="text-red-600"
onClick={() => handleDelete(user.id)}
onClick={(e) => {
e.stopPropagation();
handleDelete(user.id);
}}
>
<Trash className="mr-2 h-4 w-4" />
Supprimer