update api users and groups and users 6
This commit is contained in:
parent
cfafcb312e
commit
9d0b462a67
@ -12,7 +12,7 @@ import {
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { MoreHorizontal, Trash, Edit, UserPlus } from "lucide-react";
|
import { MoreHorizontal, Trash, Edit, UserPlus, Key, Lock, Unlock } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@ -46,6 +46,7 @@ interface User {
|
|||||||
email: string;
|
email: string;
|
||||||
createdTimestamp: number;
|
createdTimestamp: number;
|
||||||
roles: string[];
|
roles: string[];
|
||||||
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Role {
|
interface Role {
|
||||||
@ -72,14 +73,16 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [newUserDialog, setNewUserDialog] = useState(false);
|
const [newUserDialog, setNewUserDialog] = useState(false);
|
||||||
const [editUserDialog, setEditUserDialog] = useState(false);
|
const [editUserDialog, setEditUserDialog] = useState(false);
|
||||||
|
const [manageRolesDialog, setManageRolesDialog] = useState(false);
|
||||||
const [selectedUser, setSelectedUser] = useState<User | null>(null);
|
const [selectedUser, setSelectedUser] = useState<User | null>(null);
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
username: "",
|
username: "",
|
||||||
firstName: "",
|
|
||||||
lastName: "",
|
lastName: "",
|
||||||
|
firstName: "",
|
||||||
email: "",
|
email: "",
|
||||||
password: "",
|
password: "",
|
||||||
roles: [] as string[],
|
roles: [] as string[],
|
||||||
|
enabled: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -149,6 +152,7 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
email: "",
|
email: "",
|
||||||
password: "",
|
password: "",
|
||||||
roles: [],
|
roles: [],
|
||||||
|
enabled: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
@ -176,10 +180,56 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
email: user.email || "",
|
email: user.email || "",
|
||||||
password: "",
|
password: "",
|
||||||
roles: user.roles || [],
|
roles: user.roles || [],
|
||||||
|
enabled: user.enabled,
|
||||||
});
|
});
|
||||||
setEditUserDialog(true);
|
setEditUserDialog(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleManageRoles = async (userId: string) => {
|
||||||
|
const user = users.find(u => u.id === userId);
|
||||||
|
if (!user) return;
|
||||||
|
|
||||||
|
setSelectedUser(user);
|
||||||
|
setFormData(prev => ({ ...prev, roles: user.roles || [] }));
|
||||||
|
setManageRolesDialog(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUpdateRoles = async () => {
|
||||||
|
if (!selectedUser) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/users/${selectedUser.id}/roles`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
roles: formData.roles,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Erreur lors de la mise à jour des rôles");
|
||||||
|
}
|
||||||
|
|
||||||
|
await fetchUsers();
|
||||||
|
|
||||||
|
setManageRolesDialog(false);
|
||||||
|
setSelectedUser(null);
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Succès",
|
||||||
|
description: "Les rôles ont été mis à jour avec succès",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
title: "Erreur",
|
||||||
|
description: error instanceof Error ? error.message : "Une erreur est survenue",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleUpdateUser = async (e: React.FormEvent) => {
|
const handleUpdateUser = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!selectedUser) return;
|
if (!selectedUser) return;
|
||||||
@ -197,15 +247,11 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(data.error || "Erreur lors de la modification de l'utilisateur");
|
throw new Error("Erreur lors de la modification de l'utilisateur");
|
||||||
}
|
}
|
||||||
|
|
||||||
setUsers(prevUsers => prevUsers.map(u =>
|
await fetchUsers();
|
||||||
u.id === selectedUser.id ? { ...u, ...data } : u
|
|
||||||
));
|
|
||||||
|
|
||||||
setEditUserDialog(false);
|
setEditUserDialog(false);
|
||||||
setSelectedUser(null);
|
setSelectedUser(null);
|
||||||
@ -223,55 +269,6 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleManageRoles = async (userId: string) => {
|
|
||||||
const user = users.find(u => u.id === userId);
|
|
||||||
if (!user) return;
|
|
||||||
|
|
||||||
setSelectedUser(user);
|
|
||||||
setFormData(prev => ({ ...prev, roles: user.roles || [] }));
|
|
||||||
setEditUserDialog(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUpdateRoles = async () => {
|
|
||||||
if (!selectedUser) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`/api/users/${selectedUser.id}/roles`, {
|
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
roles: formData.roles,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(data.error || "Erreur lors de la mise à jour des rôles");
|
|
||||||
}
|
|
||||||
|
|
||||||
setUsers(prevUsers => prevUsers.map(u =>
|
|
||||||
u.id === selectedUser.id ? { ...u, roles: formData.roles } : u
|
|
||||||
));
|
|
||||||
|
|
||||||
setEditUserDialog(false);
|
|
||||||
setSelectedUser(null);
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: "Succès",
|
|
||||||
description: "Les rôles ont été mis à jour avec succès",
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
toast({
|
|
||||||
title: "Erreur",
|
|
||||||
description: error instanceof Error ? error.message : "Une erreur est survenue",
|
|
||||||
variant: "destructive",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDelete = async (userId: string) => {
|
const handleDelete = async (userId: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/users/${userId}`, {
|
const response = await fetch(`/api/users/${userId}`, {
|
||||||
@ -299,6 +296,67 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleChangePassword = async (userId: string) => {
|
||||||
|
const newPassword = prompt("Entrez le nouveau mot de passe:");
|
||||||
|
if (!newPassword) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/users/${userId}/password`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ password: newPassword }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Erreur lors du changement de mot de passe");
|
||||||
|
}
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Succès",
|
||||||
|
description: "Le mot de passe a été modifié avec succès",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
title: "Erreur",
|
||||||
|
description: error instanceof Error ? error.message : "Une erreur est survenue",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleToggleUserStatus = async (userId: string, currentStatus: boolean) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/users/${userId}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ enabled: !currentStatus }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Erreur lors de la modification du statut de l'utilisateur");
|
||||||
|
}
|
||||||
|
|
||||||
|
setUsers(prevUsers => prevUsers.map(u =>
|
||||||
|
u.id === userId ? { ...u, enabled: !currentStatus } : u
|
||||||
|
));
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Succès",
|
||||||
|
description: `L'utilisateur a été ${!currentStatus ? "activé" : "désactivé"} avec succès`,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
title: "Erreur",
|
||||||
|
description: error instanceof Error ? error.message : "Une erreur est survenue",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const filteredUsers = useMemo(() => {
|
const filteredUsers = useMemo(() => {
|
||||||
let filtered = users;
|
let filtered = users;
|
||||||
|
|
||||||
@ -351,14 +409,6 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="firstName">Prénom</Label>
|
|
||||||
<Input
|
|
||||||
id="firstName"
|
|
||||||
value={formData.firstName}
|
|
||||||
onChange={(e) => setFormData(prev => ({ ...prev, firstName: e.target.value }))}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="lastName">Nom</Label>
|
<Label htmlFor="lastName">Nom</Label>
|
||||||
<Input
|
<Input
|
||||||
@ -367,6 +417,14 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
onChange={(e) => setFormData(prev => ({ ...prev, lastName: e.target.value }))}
|
onChange={(e) => setFormData(prev => ({ ...prev, lastName: e.target.value }))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="firstName">Prénom</Label>
|
||||||
|
<Input
|
||||||
|
id="firstName"
|
||||||
|
value={formData.firstName}
|
||||||
|
onChange={(e) => setFormData(prev => ({ ...prev, firstName: 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
|
||||||
@ -389,21 +447,43 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="roles">Rôles</Label>
|
<Label htmlFor="roles">Rôles</Label>
|
||||||
<Select
|
<div className="flex flex-wrap gap-2">
|
||||||
value={formData.roles[0] || ""}
|
{roles.map((role) => (
|
||||||
onValueChange={(value) => setFormData(prev => ({ ...prev, roles: [value] }))}
|
<div key={role.id} className="flex items-center space-x-2">
|
||||||
>
|
<input
|
||||||
<SelectTrigger>
|
type="checkbox"
|
||||||
<SelectValue placeholder="Sélectionner un rôle" />
|
id={`role-${role.id}`}
|
||||||
</SelectTrigger>
|
checked={formData.roles.includes(role.name)}
|
||||||
<SelectContent>
|
onChange={(e) => {
|
||||||
{roles.map((role) => (
|
setFormData(prev => ({
|
||||||
<SelectItem key={role.id} value={role.name}>
|
...prev,
|
||||||
{role.name}
|
roles: e.target.checked
|
||||||
</SelectItem>
|
? [...prev.roles, role.name]
|
||||||
))}
|
: prev.roles.filter(r => r !== role.name)
|
||||||
</SelectContent>
|
}));
|
||||||
</Select>
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor={`role-${role.id}`}>{role.name}</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
|
{formData.roles.map(role => (
|
||||||
|
<span key={role} className="px-2 py-1 bg-gray-100 rounded-md text-sm">
|
||||||
|
{role}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
roles: prev.roles.filter(r => r !== role)
|
||||||
|
}))}
|
||||||
|
className="ml-2 text-gray-500 hover:text-gray-700"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button type="submit" className="w-full">
|
<Button type="submit" className="w-full">
|
||||||
Créer l'utilisateur
|
Créer l'utilisateur
|
||||||
@ -454,6 +534,23 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
<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)}>
|
||||||
|
<Key className="mr-2 h-4 w-4" />
|
||||||
|
Changer le mot de passe
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem onClick={() => handleToggleUserStatus(user.id, user.enabled)}>
|
||||||
|
{user.enabled ? (
|
||||||
|
<>
|
||||||
|
<Lock className="mr-2 h-4 w-4" />
|
||||||
|
Désactiver
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Unlock className="mr-2 h-4 w-4" />
|
||||||
|
Activer
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="text-red-600"
|
className="text-red-600"
|
||||||
@ -476,14 +573,6 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
<DialogTitle>Modifier l'utilisateur</DialogTitle>
|
<DialogTitle>Modifier l'utilisateur</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<form onSubmit={handleUpdateUser} className="space-y-4">
|
<form onSubmit={handleUpdateUser} className="space-y-4">
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="edit-firstName">Prénom</Label>
|
|
||||||
<Input
|
|
||||||
id="edit-firstName"
|
|
||||||
value={formData.firstName}
|
|
||||||
onChange={(e) => setFormData(prev => ({ ...prev, firstName: e.target.value }))}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="edit-lastName">Nom</Label>
|
<Label htmlFor="edit-lastName">Nom</Label>
|
||||||
<Input
|
<Input
|
||||||
@ -492,6 +581,14 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
onChange={(e) => setFormData(prev => ({ ...prev, lastName: e.target.value }))}
|
onChange={(e) => setFormData(prev => ({ ...prev, lastName: e.target.value }))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="edit-firstName">Prénom</Label>
|
||||||
|
<Input
|
||||||
|
id="edit-firstName"
|
||||||
|
value={formData.firstName}
|
||||||
|
onChange={(e) => setFormData(prev => ({ ...prev, firstName: e.target.value }))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="edit-email">Email</Label>
|
<Label htmlFor="edit-email">Email</Label>
|
||||||
<Input
|
<Input
|
||||||
@ -503,21 +600,43 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="edit-roles">Rôles</Label>
|
<Label htmlFor="edit-roles">Rôles</Label>
|
||||||
<Select
|
<div className="flex flex-wrap gap-2">
|
||||||
value={formData.roles[0] || ""}
|
{roles.map((role) => (
|
||||||
onValueChange={(value) => setFormData(prev => ({ ...prev, roles: [value] }))}
|
<div key={role.id} className="flex items-center space-x-2">
|
||||||
>
|
<input
|
||||||
<SelectTrigger>
|
type="checkbox"
|
||||||
<SelectValue placeholder="Sélectionner un rôle" />
|
id={`edit-role-${role.id}`}
|
||||||
</SelectTrigger>
|
checked={formData.roles.includes(role.name)}
|
||||||
<SelectContent>
|
onChange={(e) => {
|
||||||
{roles.map((role) => (
|
setFormData(prev => ({
|
||||||
<SelectItem key={role.id} value={role.name}>
|
...prev,
|
||||||
{role.name}
|
roles: e.target.checked
|
||||||
</SelectItem>
|
? [...prev.roles, role.name]
|
||||||
))}
|
: prev.roles.filter(r => r !== role.name)
|
||||||
</SelectContent>
|
}));
|
||||||
</Select>
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor={`edit-role-${role.id}`}>{role.name}</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
|
{formData.roles.map(role => (
|
||||||
|
<span key={role} className="px-2 py-1 bg-gray-100 rounded-md text-sm">
|
||||||
|
{role}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
roles: prev.roles.filter(r => r !== role)
|
||||||
|
}))}
|
||||||
|
className="ml-2 text-gray-500 hover:text-gray-700"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2">
|
||||||
<Button type="submit" className="flex-1">
|
<Button type="submit" className="flex-1">
|
||||||
@ -535,6 +654,59 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|||||||
</form>
|
</form>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog open={manageRolesDialog} onOpenChange={setManageRolesDialog}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Gérer les rôles de {selectedUser?.username}</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label>Rôles</Label>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{roles.map((role) => (
|
||||||
|
<div key={role.id} className="flex items-center space-x-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id={`manage-role-${role.id}`}
|
||||||
|
checked={formData.roles.includes(role.name)}
|
||||||
|
onChange={(e) => {
|
||||||
|
setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
roles: e.target.checked
|
||||||
|
? [...prev.roles, role.name]
|
||||||
|
: prev.roles.filter(r => r !== role.name)
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<label htmlFor={`manage-role-${role.id}`}>{role.name}</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
|
{formData.roles.map(role => (
|
||||||
|
<span key={role} className="px-2 py-1 bg-gray-100 rounded-md text-sm">
|
||||||
|
{role}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setFormData(prev => ({
|
||||||
|
...prev,
|
||||||
|
roles: prev.roles.filter(r => r !== role)
|
||||||
|
}))}
|
||||||
|
className="ml-2 text-gray-500 hover:text-gray-700"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button onClick={handleUpdateRoles} className="w-full">
|
||||||
|
Mettre à jour les rôles
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user