update api users and groups and users 10
This commit is contained in:
parent
01a3a3dd55
commit
992d5ec2a0
@ -37,6 +37,12 @@ interface Group {
|
|||||||
membersCount: number;
|
membersCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface ApiError {
|
interface ApiError {
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
@ -54,6 +60,9 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
|
|||||||
const [modifyGroupDialog, setModifyGroupDialog] = useState(false);
|
const [modifyGroupDialog, setModifyGroupDialog] = useState(false);
|
||||||
const [selectedGroup, setSelectedGroup] = useState<Group | null>(null);
|
const [selectedGroup, setSelectedGroup] = useState<Group | null>(null);
|
||||||
const [modifiedGroupName, setModifiedGroupName] = useState("");
|
const [modifiedGroupName, setModifiedGroupName] = useState("");
|
||||||
|
const [manageMembersDialog, setManageMembersDialog] = useState(false);
|
||||||
|
const [groupMembers, setGroupMembers] = useState<User[]>([]);
|
||||||
|
const [availableUsers, setAvailableUsers] = useState<User[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchGroups();
|
fetchGroups();
|
||||||
@ -200,19 +209,91 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleManageMembers = async (groupId: string) => {
|
const handleManageMembers = async (groupId: string) => {
|
||||||
try {
|
|
||||||
const group = groups.find(g => g.id === groupId);
|
const group = groups.find(g => g.id === groupId);
|
||||||
if (!group) return;
|
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({
|
toast({
|
||||||
title: "Info",
|
title: "Erreur",
|
||||||
description: "La gestion des membres sera bientôt disponible",
|
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) {
|
} catch (error) {
|
||||||
toast({
|
toast({
|
||||||
title: "Erreur",
|
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",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -350,6 +431,71 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
|
|||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -134,7 +134,11 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(formData),
|
body: JSON.stringify({
|
||||||
|
...formData,
|
||||||
|
firstName: formData.firstName,
|
||||||
|
lastName: formData.lastName,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@ -433,14 +437,6 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</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">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="firstName">Prénom</Label>
|
<Label htmlFor="firstName">Prénom</Label>
|
||||||
<Input
|
<Input
|
||||||
@ -449,6 +445,14 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
onChange={(e) => setFormData(prev => ({ ...prev, firstName: e.target.value }))}
|
onChange={(e) => setFormData(prev => ({ ...prev, firstName: e.target.value }))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="email">Email</Label>
|
<Label htmlFor="email">Email</Label>
|
||||||
<Input
|
<Input
|
||||||
@ -543,26 +547,52 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
<TableCell className="text-right">
|
<TableCell className="text-right">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<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>
|
<span className="sr-only">Open menu</span>
|
||||||
<MoreHorizontal className="h-4 w-4" />
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||||
<DropdownMenuItem onClick={() => handleEdit(user.id)}>
|
<DropdownMenuItem
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleEdit(user.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Edit className="mr-2 h-4 w-4" />
|
<Edit className="mr-2 h-4 w-4" />
|
||||||
Modifier
|
Modifier
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => handleManageRoles(user.id)}>
|
<DropdownMenuItem
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleManageRoles(user.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<UserPlus className="mr-2 h-4 w-4" />
|
<UserPlus className="mr-2 h-4 w-4" />
|
||||||
Gérer les rôles
|
Gérer les rôles
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => handleChangePassword(user.id)}>
|
<DropdownMenuItem
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleChangePassword(user.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Key className="mr-2 h-4 w-4" />
|
<Key className="mr-2 h-4 w-4" />
|
||||||
Changer le mot de passe
|
Changer le mot de passe
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => handleToggleUserStatus(user.id, user.enabled)}>
|
<DropdownMenuItem
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleToggleUserStatus(user.id, user.enabled);
|
||||||
|
}}
|
||||||
|
>
|
||||||
{user.enabled ? (
|
{user.enabled ? (
|
||||||
<>
|
<>
|
||||||
<Lock className="mr-2 h-4 w-4" />
|
<Lock className="mr-2 h-4 w-4" />
|
||||||
@ -578,7 +608,10 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="text-red-600"
|
className="text-red-600"
|
||||||
onClick={() => handleDelete(user.id)}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleDelete(user.id);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Trash className="mr-2 h-4 w-4" />
|
<Trash className="mr-2 h-4 w-4" />
|
||||||
Supprimer
|
Supprimer
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user