equipe ui

This commit is contained in:
alma 2025-05-04 10:44:03 +02:00
parent 7c3239996c
commit bc02fdfb98
4 changed files with 461 additions and 395 deletions

View File

@ -497,17 +497,16 @@ export default function EmailSidebar({
type="button" type="button"
className="ml-1 text-gray-400 hover:text-gray-600 cursor-pointer flex items-center justify-center h-5 w-5" className="ml-1 text-gray-400 hover:text-gray-600 cursor-pointer flex items-center justify-center h-5 w-5"
tabIndex={-1} tabIndex={-1}
onClick={e => e.stopPropagation()}
aria-label="Account options" aria-label="Account options"
> >
<span style={{ fontSize: '18px', lineHeight: 1 }}></span> <span style={{ fontSize: '18px', lineHeight: 1 }}></span>
</button> </button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
<DropdownMenuItem onClick={e => { e.stopPropagation(); onEditAccount(account); }}> <DropdownMenuItem onClick={() => onEditAccount(account)}>
Edit Edit
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={e => { e.stopPropagation(); onDeleteAccount(account); }}> <DropdownMenuItem onClick={() => onDeleteAccount(account)}>
Delete Delete
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect, useCallback } from "react";
import { import {
Table, Table,
TableBody, TableBody,
@ -53,6 +53,41 @@ interface GroupsTableProps {
userRole?: string[]; userRole?: string[];
} }
// DialogWrapper component to better handle dialog state
function DialogWrapper({
open,
onOpenChange,
onAfterClose,
triggerComponent,
children,
className
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
onAfterClose?: () => void;
triggerComponent?: React.ReactNode;
children: React.ReactNode;
className?: string;
}) {
const handleOpenChange = useCallback((open: boolean) => {
onOpenChange(open);
if (!open && onAfterClose) {
// Use a longer timeout to ensure all animations are complete
setTimeout(onAfterClose, 300);
}
}, [onOpenChange, onAfterClose]);
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
{triggerComponent && <DialogTrigger asChild>{triggerComponent}</DialogTrigger>}
<DialogContent className={className}>
{children}
</DialogContent>
</Dialog>
);
}
export function GroupsTable({ userRole = [] }: GroupsTableProps) { export function GroupsTable({ userRole = [] }: GroupsTableProps) {
const [groups, setGroups] = useState<Group[]>([]); const [groups, setGroups] = useState<Group[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@ -340,6 +375,22 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
} }
}; };
// Reset callbacks
const resetNewGroupForm = useCallback(() => {
setNewGroupName("");
}, []);
const resetModifyGroupForm = useCallback(() => {
setSelectedGroup(null);
setModifiedGroupName("");
}, []);
const resetMembersForm = useCallback(() => {
setSelectedGroup(null);
setGroupMembers([]);
setAvailableUsers([]);
}, []);
if (loading) return <div className="text-center p-4">Loading...</div>; if (loading) return <div className="text-center p-4">Loading...</div>;
return ( return (
@ -352,44 +403,39 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="max-w-xs bg-white text-gray-900 border-gray-300" className="max-w-xs bg-white text-gray-900 border-gray-300"
/> />
<Dialog open={newGroupDialog} onOpenChange={(open) => { <DialogWrapper
setNewGroupDialog(open); open={newGroupDialog}
if (!open) { onOpenChange={setNewGroupDialog}
// Reset state when dialog closes onAfterClose={resetNewGroupForm}
setTimeout(() => { triggerComponent={
setNewGroupName("");
}, 100);
}
}}>
<DialogTrigger asChild>
<Button className="bg-blue-600 hover:bg-blue-700 text-white"> <Button className="bg-blue-600 hover:bg-blue-700 text-white">
<Plus className="mr-2 h-4 w-4" /> Ajouter un groupe <Plus className="mr-2 h-4 w-4" /> Ajouter un groupe
</Button> </Button>
</DialogTrigger> }
<DialogContent className="bg-white text-black border border-gray-300"> className="bg-white text-black border border-gray-300"
<DialogHeader> >
<DialogTitle className="text-gray-900">Nouveau Groupe</DialogTitle> <DialogHeader>
</DialogHeader> <DialogTitle className="text-gray-900">Nouveau Groupe</DialogTitle>
<div className="space-y-4"> </DialogHeader>
<div className="space-y-2"> <div className="space-y-4">
<Label htmlFor="groupName" className="text-gray-900">Nom du groupe</Label> <div className="space-y-2">
<Input <Label htmlFor="groupName" className="text-gray-900">Nom du groupe</Label>
id="groupName" <Input
value={newGroupName} id="groupName"
onChange={(e) => setNewGroupName(e.target.value)} value={newGroupName}
placeholder="Entrez le nom du groupe" onChange={(e) => setNewGroupName(e.target.value)}
className="bg-white text-gray-900 border-gray-300" placeholder="Entrez le nom du groupe"
/> className="bg-white text-gray-900 border-gray-300"
</div> />
<Button
onClick={handleCreateGroup}
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
>
Créer le groupe
</Button>
</div> </div>
</DialogContent> <Button
</Dialog> onClick={handleCreateGroup}
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
>
Créer le groupe
</Button>
</div>
</DialogWrapper>
</div> </div>
<Table className="bg-white border border-gray-300 rounded-md"> <Table className="bg-white border border-gray-300 rounded-md">
@ -455,130 +501,111 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
</TableBody> </TableBody>
</Table> </Table>
<Dialog <DialogWrapper
open={modifyGroupDialog} open={modifyGroupDialog}
onOpenChange={(open) => { onOpenChange={setModifyGroupDialog}
setModifyGroupDialog(open); onAfterClose={resetModifyGroupForm}
if (!open) { className="bg-white text-black border border-gray-300"
// Reset state when dialog closes
setTimeout(() => {
setSelectedGroup(null);
setModifiedGroupName("");
}, 100);
}
}}
> >
<DialogContent className="bg-white text-black border border-gray-300"> <DialogHeader>
<DialogHeader> <DialogTitle className="text-gray-900">Modifier le groupe</DialogTitle>
<DialogTitle className="text-gray-900">Modifier le groupe</DialogTitle> </DialogHeader>
</DialogHeader> <div className="space-y-4">
<div className="space-y-4"> <div className="space-y-2">
<div className="space-y-2"> <Label htmlFor="modifiedGroupName" className="text-gray-900">Nom du groupe</Label>
<Label htmlFor="modifiedGroupName" className="text-gray-900">Nom du groupe</Label> <Input
<Input id="modifiedGroupName"
id="modifiedGroupName" value={modifiedGroupName}
value={modifiedGroupName} onChange={(e) => setModifiedGroupName(e.target.value)}
onChange={(e) => setModifiedGroupName(e.target.value)} className="bg-white text-gray-900 border-gray-300"
className="bg-white text-gray-900 border-gray-300" autoFocus
autoFocus />
/>
</div>
<Button
onClick={handleUpdateGroup}
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
>
Mettre à jour
</Button>
</div> </div>
</DialogContent> <Button
</Dialog> onClick={handleUpdateGroup}
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
>
Mettre à jour
</Button>
</div>
</DialogWrapper>
<Dialog <DialogWrapper
open={manageMembersDialog} open={manageMembersDialog}
onOpenChange={(open) => { onOpenChange={setManageMembersDialog}
setManageMembersDialog(open); onAfterClose={resetMembersForm}
if (!open) { className="sm:max-w-[600px] max-h-[90vh] overflow-y-auto bg-white text-black border border-gray-300"
// Reset state when dialog closes
setTimeout(() => {
setSelectedGroup(null);
setGroupMembers([]);
setAvailableUsers([]);
}, 100);
}
}}
> >
<DialogContent className="sm:max-w-[600px] max-h-[90vh] overflow-y-auto bg-white text-black border border-gray-300"> <DialogHeader>
<DialogHeader> <DialogTitle className="text-gray-900">
<DialogTitle className="text-gray-900"> Gérer les membres - {selectedGroup?.name}
Gérer les membres - {selectedGroup?.name} </DialogTitle>
</DialogTitle> </DialogHeader>
</DialogHeader> <div className="space-y-4">
<div className="space-y-4"> <div>
<div> <h3 className="text-lg font-medium mb-2 text-gray-900">Membres actuels</h3>
<h3 className="text-lg font-medium mb-2 text-gray-900">Membres actuels</h3> {groupMembers.length === 0 ? (
{groupMembers.length === 0 ? ( <p className="text-gray-600">Ce groupe n'a pas de membres.</p>
<p className="text-gray-600">Ce groupe n'a pas de membres.</p> ) : (
) : ( <div className="space-y-2">
<div className="space-y-2"> {groupMembers.map((member) => (
{groupMembers.map((member) => ( <div key={member.id} className="flex items-center justify-between p-2 bg-gray-50 rounded-md border border-gray-200">
<div key={member.id} className="flex items-center justify-between p-2 bg-gray-50 rounded-md border border-gray-200"> <div className="text-gray-900">
<div className="text-gray-900"> <div className="font-medium">{member.username}</div>
<div className="font-medium">{member.username}</div> <div className="text-sm text-gray-600">{member.firstName} {member.lastName} ({member.email})</div>
<div className="text-sm text-gray-600">{member.firstName} {member.lastName} ({member.email})</div>
</div>
<Button
variant="outline"
size="sm"
onClick={() => handleRemoveMember(member.id)}
className="text-red-600 hover:bg-red-50 hover:text-red-700 border-red-200"
>
<Trash className="h-4 w-4" />
</Button>
</div> </div>
))} <Button
</div> variant="outline"
)} size="sm"
</div> onClick={() => handleRemoveMember(member.id)}
className="text-red-600 hover:bg-red-50 hover:text-red-700 border-red-200"
<div> >
<h3 className="text-lg font-medium mb-2 text-gray-900">Ajouter des membres</h3> <Trash className="h-4 w-4" />
<Input </Button>
type="text" </div>
placeholder="Rechercher un utilisateur..." ))}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="mb-2 bg-white text-gray-900 border-gray-300"
/>
<div className="space-y-2 max-h-[250px] overflow-y-auto">
{availableUsers
.filter(user =>
!groupMembers.some(member => member.id === user.id) &&
(user.username.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.firstName.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.lastName.toLowerCase().includes(searchTerm.toLowerCase()))
)
.map((user) => (
<div key={user.id} className="flex items-center justify-between p-2 bg-gray-50 rounded-md border border-gray-200">
<div className="text-gray-900">
<div className="font-medium">{user.username}</div>
<div className="text-sm text-gray-600">{user.firstName} {user.lastName} ({user.email})</div>
</div>
<Button
variant="outline"
size="sm"
onClick={() => handleAddMember(user.id)}
className="text-green-600 hover:bg-green-50 hover:text-green-700 border-green-200"
>
<Plus className="h-4 w-4" />
</Button>
</div>
))}
</div> </div>
)}
</div>
<div>
<h3 className="text-lg font-medium mb-2 text-gray-900">Ajouter des membres</h3>
<Input
type="text"
placeholder="Rechercher un utilisateur..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="mb-2 bg-white text-gray-900 border-gray-300"
/>
<div className="space-y-2 max-h-[250px] overflow-y-auto">
{availableUsers
.filter(user =>
!groupMembers.some(member => member.id === user.id) &&
(user.username.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.firstName.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.lastName.toLowerCase().includes(searchTerm.toLowerCase()))
)
.map((user) => (
<div key={user.id} className="flex items-center justify-between p-2 bg-gray-50 rounded-md border border-gray-200">
<div className="text-gray-900">
<div className="font-medium">{user.username}</div>
<div className="text-sm text-gray-600">{user.firstName} {user.lastName} ({user.email})</div>
</div>
<Button
variant="outline"
size="sm"
onClick={() => handleAddMember(user.id)}
className="text-green-600 hover:bg-green-50 hover:text-green-700 border-green-200"
>
<Plus className="h-4 w-4" />
</Button>
</div>
))}
</div> </div>
</div> </div>
</DialogContent> </div>
</Dialog> </DialogWrapper>
</div> </div>
); );
} }

View File

@ -41,9 +41,17 @@ const DialogContent = React.forwardRef<
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className className
)} )}
onPointerDownOutside={(e) => {
// Allow clicks outside to propagate normally
e.preventDefault();
}}
{...props} {...props}
> >
{children} {children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content> </DialogPrimitive.Content>
</DialogPortal> </DialogPortal>
)) ))

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect, useMemo } from "react"; import { useState, useEffect, useMemo, useCallback } from "react";
import { import {
Table, Table,
TableBody, TableBody,
@ -65,6 +65,41 @@ interface UsersTableProps {
const ITEMS_PER_PAGE = 10; const ITEMS_PER_PAGE = 10;
// DialogWrapper component to better handle dialog state
function DialogWrapper({
open,
onOpenChange,
onAfterClose,
triggerComponent,
children,
className
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
onAfterClose?: () => void;
triggerComponent?: React.ReactNode;
children: React.ReactNode;
className?: string;
}) {
const handleOpenChange = useCallback((open: boolean) => {
onOpenChange(open);
if (!open && onAfterClose) {
// Use a longer timeout to ensure all animations are complete
setTimeout(onAfterClose, 300);
}
}, [onOpenChange, onAfterClose]);
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
{triggerComponent && <DialogTrigger asChild>{triggerComponent}</DialogTrigger>}
<DialogContent className={className}>
{children}
</DialogContent>
</Dialog>
);
}
export function UsersTable({ userRole = [] }: UsersTableProps) { export function UsersTable({ userRole = [] }: UsersTableProps) {
const { data: session, status } = useSession(); const { data: session, status } = useSession();
const [users, setUsers] = useState<User[]>([]); const [users, setUsers] = useState<User[]>([]);
@ -403,6 +438,37 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
currentPage * ITEMS_PER_PAGE currentPage * ITEMS_PER_PAGE
); );
// Define reset function callbacks
const resetNewUserForm = useCallback(() => {
setFormData({
username: "",
lastName: "",
firstName: "",
email: "",
password: "",
roles: [],
enabled: true,
});
}, []);
const resetEditForm = useCallback(() => {
setFormData({
username: "",
lastName: "",
firstName: "",
email: "",
password: "",
roles: [],
enabled: true,
});
setSelectedUser(null);
}, []);
const resetRolesForm = useCallback(() => {
setFormData(prev => ({ ...prev, roles: [] }));
setSelectedUser(null);
}, []);
if (!session) return null; if (!session) return null;
if (loading) return <div className="text-center p-4">Loading...</div>; if (loading) return <div className="text-center p-4">Loading...</div>;
@ -416,115 +482,103 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="max-w-sm bg-white text-gray-900 border-gray-300" className="max-w-sm bg-white text-gray-900 border-gray-300"
/> />
<Dialog open={newUserDialog} onOpenChange={(open) => { <DialogWrapper
setNewUserDialog(open); open={newUserDialog}
if (!open) { onOpenChange={setNewUserDialog}
setTimeout(() => { onAfterClose={resetNewUserForm}
setFormData({ triggerComponent={
username: "",
lastName: "",
firstName: "",
email: "",
password: "",
roles: [],
enabled: true,
});
}, 100);
}
}}>
<DialogTrigger asChild>
<Button className="bg-blue-600 hover:bg-blue-700 text-white"> <Button className="bg-blue-600 hover:bg-blue-700 text-white">
Ajouter un utilisateur Ajouter un utilisateur
</Button> </Button>
</DialogTrigger> }
<DialogContent className="max-h-[85vh] overflow-y-auto bg-white text-black border border-gray-300"> className="max-h-[85vh] overflow-y-auto bg-white text-black border border-gray-300"
<DialogHeader> >
<DialogTitle className="text-gray-900">Nouvel Utilisateur</DialogTitle> <DialogHeader>
</DialogHeader> <DialogTitle className="text-gray-900">Nouvel Utilisateur</DialogTitle>
<form onSubmit={handleAddUser} className="space-y-3"> </DialogHeader>
<div className="grid grid-cols-2 gap-3"> <form onSubmit={handleAddUser} className="space-y-3">
<div className="space-y-2"> <div className="grid grid-cols-2 gap-3">
<Label htmlFor="username" className="text-gray-900">Nom d'utilisateur</Label>
<Input
id="username"
value={formData.username}
onChange={(e) => setFormData(prev => ({ ...prev, username: e.target.value.trim() }))}
required
className="bg-white text-gray-900 border-gray-300"
/>
</div>
<div className="space-y-2">
<Label htmlFor="email" className="text-gray-900">Email</Label>
<Input
id="email"
type="email"
value={formData.email}
onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value.trim() }))}
required
className="bg-white text-gray-900 border-gray-300"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-2">
<Label htmlFor="firstName" className="text-gray-900">Prénom</Label>
<Input
id="firstName"
value={formData.firstName}
onChange={(e) => setFormData(prev => ({ ...prev, firstName: e.target.value.trim() }))}
required
className="bg-white text-gray-900 border-gray-300"
/>
</div>
<div className="space-y-2">
<Label htmlFor="lastName" className="text-gray-900">Nom</Label>
<Input
id="lastName"
value={formData.lastName}
onChange={(e) => setFormData(prev => ({ ...prev, lastName: e.target.value.trim() }))}
required
className="bg-white text-gray-900 border-gray-300"
/>
</div>
</div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="password" className="text-gray-900">Mot de passe</Label> <Label htmlFor="username" className="text-gray-900">Nom d'utilisateur</Label>
<Input <Input
id="password" id="username"
type="password" value={formData.username}
value={formData.password} onChange={(e) => setFormData(prev => ({ ...prev, username: e.target.value.trim() }))}
onChange={(e) => setFormData(prev => ({ ...prev, password: e.target.value }))}
required required
className="bg-white text-gray-900 border-gray-300" className="bg-white text-gray-900 border-gray-300"
/> />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label className="text-gray-900">Rôles</Label> <Label htmlFor="email" className="text-gray-900">Email</Label>
<div className="grid grid-cols-2 gap-2 max-h-[120px] overflow-y-auto border border-gray-300 rounded-md p-2 bg-white"> <Input
{roles.map((role) => ( id="email"
<div key={role.id} className="flex items-center space-x-2"> type="email"
<Checkbox value={formData.email}
id={`role-${role.id}`} onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value.trim() }))}
checked={formData.roles.includes(role.name)} required
onCheckedChange={(checked) => { className="bg-white text-gray-900 border-gray-300"
setFormData(prev => ({ />
...prev,
roles: checked
? [...prev.roles, role.name]
: prev.roles.filter(r => r !== role.name)
}));
}}
className="border-gray-500"
/>
<Label htmlFor={`role-${role.id}`} className="text-sm text-gray-900">{role.name}</Label>
</div>
))}
</div>
</div> </div>
<Button type="submit" className="w-full bg-blue-600 hover:bg-blue-700 text-white">Créer Utilisateur</Button> </div>
</form> <div className="grid grid-cols-2 gap-3">
</DialogContent> <div className="space-y-2">
</Dialog> <Label htmlFor="firstName" className="text-gray-900">Prénom</Label>
<Input
id="firstName"
value={formData.firstName}
onChange={(e) => setFormData(prev => ({ ...prev, firstName: e.target.value.trim() }))}
required
className="bg-white text-gray-900 border-gray-300"
/>
</div>
<div className="space-y-2">
<Label htmlFor="lastName" className="text-gray-900">Nom</Label>
<Input
id="lastName"
value={formData.lastName}
onChange={(e) => setFormData(prev => ({ ...prev, lastName: e.target.value.trim() }))}
required
className="bg-white text-gray-900 border-gray-300"
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="password" className="text-gray-900">Mot de passe</Label>
<Input
id="password"
type="password"
value={formData.password}
onChange={(e) => setFormData(prev => ({ ...prev, password: e.target.value }))}
required
className="bg-white text-gray-900 border-gray-300"
/>
</div>
<div className="space-y-2">
<Label className="text-gray-900">Rôles</Label>
<div className="grid grid-cols-2 gap-2 max-h-[120px] overflow-y-auto border border-gray-300 rounded-md p-2 bg-white">
{roles.map((role) => (
<div key={role.id} className="flex items-center space-x-2">
<Checkbox
id={`role-${role.id}`}
checked={formData.roles.includes(role.name)}
onCheckedChange={(checked) => {
setFormData(prev => ({
...prev,
roles: checked
? [...prev.roles, role.name]
: prev.roles.filter(r => r !== role.name)
}));
}}
className="border-gray-500"
/>
<Label htmlFor={`role-${role.id}`} className="text-sm text-gray-900">{role.name}</Label>
</div>
))}
</div>
</div>
<Button type="submit" className="w-full bg-blue-600 hover:bg-blue-700 text-white">Créer Utilisateur</Button>
</form>
</DialogWrapper>
</div> </div>
<Table className="bg-white border border-gray-300 rounded-md"> <Table className="bg-white border border-gray-300 rounded-md">
@ -564,10 +618,6 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
<Button <Button
variant="ghost" variant="ghost"
className="h-8 w-8 p-0" className="h-8 w-8 p-0"
onClick={(e) => {
e.preventDefault();
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" />
@ -628,147 +678,129 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
</TableBody> </TableBody>
</Table> </Table>
<Dialog open={editUserDialog} onOpenChange={(open) => { <DialogWrapper
if (!open) { open={editUserDialog}
setTimeout(() => { onOpenChange={setEditUserDialog}
setFormData({ onAfterClose={resetEditForm}
username: "", className="bg-white text-black border border-gray-300"
lastName: "", >
firstName: "", <DialogHeader>
email: "", <DialogTitle className="text-gray-900">Modifier l'utilisateur</DialogTitle>
password: "", </DialogHeader>
roles: [], <form onSubmit={handleUpdateUser} className="space-y-4">
enabled: true, <div className="space-y-2">
}); <Label htmlFor="edit-firstName" className="text-gray-900">Prénom</Label>
setSelectedUser(null); <Input
}, 100); id="edit-firstName"
} value={formData.firstName}
setEditUserDialog(open); onChange={(e) => setFormData(prev => ({ ...prev, firstName: e.target.value }))}
}}> className="bg-white text-gray-900 border-gray-300"
<DialogContent className="bg-white text-black border border-gray-300"> autoFocus
<DialogHeader> />
<DialogTitle className="text-gray-900">Modifier l'utilisateur</DialogTitle> </div>
</DialogHeader> <div className="space-y-2">
<form onSubmit={handleUpdateUser} className="space-y-4"> <Label htmlFor="edit-lastName" className="text-gray-900">Nom</Label>
<div className="space-y-2"> <Input
<Label htmlFor="edit-firstName" className="text-gray-900">Prénom</Label> id="edit-lastName"
<Input value={formData.lastName}
id="edit-firstName" onChange={(e) => setFormData(prev => ({ ...prev, lastName: e.target.value }))}
value={formData.firstName} className="bg-white text-gray-900 border-gray-300"
onChange={(e) => setFormData(prev => ({ ...prev, firstName: e.target.value }))} />
className="bg-white text-gray-900 border-gray-300" </div>
autoFocus <div className="space-y-2">
/> <Label htmlFor="edit-email" className="text-gray-900">Email</Label>
</div> <Input
<div className="space-y-2"> id="edit-email"
<Label htmlFor="edit-lastName" className="text-gray-900">Nom</Label> type="email"
<Input value={formData.email}
id="edit-lastName" onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
value={formData.lastName} className="bg-white text-gray-900 border-gray-300"
onChange={(e) => setFormData(prev => ({ ...prev, lastName: e.target.value }))} />
className="bg-white text-gray-900 border-gray-300" </div>
/> <div className="flex justify-end space-x-2">
</div> <Button
<div className="space-y-2"> type="button"
<Label htmlFor="edit-email" className="text-gray-900">Email</Label> variant="outline"
<Input onClick={() => {
id="edit-email" setEditUserDialog(false);
type="email" setSelectedUser(null);
value={formData.email} setFormData({
onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))} username: "",
className="bg-white text-gray-900 border-gray-300" lastName: "",
/> firstName: "",
</div> email: "",
<div className="flex justify-end space-x-2"> password: "",
<Button roles: [],
type="button" enabled: true,
variant="outline" });
onClick={() => { }}
setEditUserDialog(false); className="border-gray-300 text-gray-800 hover:bg-gray-100"
setSelectedUser(null); >
setFormData({ Annuler
username: "", </Button>
lastName: "", <Button type="submit" className="bg-blue-600 hover:bg-blue-700 text-white">
firstName: "", Modifier
email: "", </Button>
password: "", </div>
roles: [], </form>
enabled: true, </DialogWrapper>
});
}}
className="border-gray-300 text-gray-800 hover:bg-gray-100"
>
Annuler
</Button>
<Button type="submit" className="bg-blue-600 hover:bg-blue-700 text-white">
Modifier
</Button>
</div>
</form>
</DialogContent>
</Dialog>
<Dialog open={manageRolesDialog} onOpenChange={(open) => { <DialogWrapper
if (!open) { open={manageRolesDialog}
setTimeout(() => { onOpenChange={setManageRolesDialog}
setFormData(prev => ({ ...prev, roles: [] })); onAfterClose={resetRolesForm}
setSelectedUser(null); className="bg-white text-black border border-gray-300"
}, 100); >
} <DialogHeader>
setManageRolesDialog(open); <DialogTitle className="text-gray-900">Gérer les rôles pour {selectedUser?.username}</DialogTitle>
}}> </DialogHeader>
<DialogContent className="bg-white text-black border border-gray-300"> <div className="space-y-4">
<DialogHeader> <div className="space-y-2">
<DialogTitle className="text-gray-900">Gérer les rôles pour {selectedUser?.username}</DialogTitle> <Label className="text-gray-900">Rôles disponibles</Label>
</DialogHeader> <div className="grid grid-cols-2 gap-2 max-h-[200px] overflow-y-auto border border-gray-300 rounded-md p-2 bg-white">
<div className="space-y-4"> {roles.map((role) => (
<div className="space-y-2"> <div key={role.id} className="flex items-center space-x-2 p-2 rounded-md hover:bg-gray-100">
<Label className="text-gray-900">Rôles disponibles</Label> <Checkbox
<div className="grid grid-cols-2 gap-2 max-h-[200px] overflow-y-auto border border-gray-300 rounded-md p-2 bg-white"> id={`manage-role-${role.id}`}
{roles.map((role) => ( checked={formData.roles.includes(role.name)}
<div key={role.id} className="flex items-center space-x-2 p-2 rounded-md hover:bg-gray-100"> onCheckedChange={(checked) => {
<Checkbox setFormData(prev => ({
id={`manage-role-${role.id}`} ...prev,
checked={formData.roles.includes(role.name)} roles: checked
onCheckedChange={(checked) => { ? [...prev.roles, role.name]
setFormData(prev => ({ : prev.roles.filter(r => r !== role.name)
...prev, }));
roles: checked }}
? [...prev.roles, role.name] className="border-gray-500"
: prev.roles.filter(r => r !== role.name) />
})); <Label
}} htmlFor={`manage-role-${role.id}`}
className="border-gray-500" className="text-sm text-gray-900"
/> >
<Label {role.name}
htmlFor={`manage-role-${role.id}`} </Label>
className="text-sm text-gray-900" </div>
> ))}
{role.name}
</Label>
</div>
))}
</div>
</div>
<div className="flex justify-end space-x-2">
<Button
variant="outline"
onClick={() => {
setManageRolesDialog(false);
setSelectedUser(null);
setFormData(prev => ({ ...prev, roles: [] }));
}}
className="border-gray-300 text-gray-800 hover:bg-gray-100"
>
Annuler
</Button>
<Button onClick={handleUpdateRoles} className="bg-blue-600 hover:bg-blue-700 text-white">
Mettre à jour les rôles
</Button>
</div> </div>
</div> </div>
</DialogContent> <div className="flex justify-end space-x-2">
</Dialog> <Button
variant="outline"
onClick={() => {
setManageRolesDialog(false);
setSelectedUser(null);
setFormData(prev => ({ ...prev, roles: [] }));
}}
className="border-gray-300 text-gray-800 hover:bg-gray-100"
>
Annuler
</Button>
<Button onClick={handleUpdateRoles} className="bg-blue-600 hover:bg-blue-700 text-white">
Mettre à jour les rôles
</Button>
</div>
</div>
</DialogWrapper>
</div> </div>
); );
} }