From bc02fdfb9879d09fba714ce631e03a29859a9ef9 Mon Sep 17 00:00:00 2001 From: alma Date: Sun, 4 May 2025 10:44:03 +0200 Subject: [PATCH] equipe ui --- components/email/EmailSidebar.tsx | 5 +- components/groups/groups-table.tsx | 329 +++++++++--------- components/ui/dialog.tsx | 8 + components/users/users-table.tsx | 514 +++++++++++++++-------------- 4 files changed, 461 insertions(+), 395 deletions(-) diff --git a/components/email/EmailSidebar.tsx b/components/email/EmailSidebar.tsx index b016d083..1f142436 100644 --- a/components/email/EmailSidebar.tsx +++ b/components/email/EmailSidebar.tsx @@ -497,17 +497,16 @@ export default function EmailSidebar({ type="button" className="ml-1 text-gray-400 hover:text-gray-600 cursor-pointer flex items-center justify-center h-5 w-5" tabIndex={-1} - onClick={e => e.stopPropagation()} aria-label="Account options" > - { e.stopPropagation(); onEditAccount(account); }}> + onEditAccount(account)}> Edit - { e.stopPropagation(); onDeleteAccount(account); }}> + onDeleteAccount(account)}> Delete diff --git a/components/groups/groups-table.tsx b/components/groups/groups-table.tsx index 69ee038f..1c6960d8 100644 --- a/components/groups/groups-table.tsx +++ b/components/groups/groups-table.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useCallback } from "react"; import { Table, TableBody, @@ -53,6 +53,41 @@ interface GroupsTableProps { 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 ( + + {triggerComponent && {triggerComponent}} + + {children} + + + ); +} + export function GroupsTable({ userRole = [] }: GroupsTableProps) { const [groups, setGroups] = useState([]); 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
Loading...
; return ( @@ -352,44 +403,39 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) { onChange={(e) => setSearchTerm(e.target.value)} className="max-w-xs bg-white text-gray-900 border-gray-300" /> - { - setNewGroupDialog(open); - if (!open) { - // Reset state when dialog closes - setTimeout(() => { - setNewGroupName(""); - }, 100); - } - }}> - + Ajouter un groupe - - - - Nouveau Groupe - -
-
- - setNewGroupName(e.target.value)} - placeholder="Entrez le nom du groupe" - className="bg-white text-gray-900 border-gray-300" - /> -
- + } + className="bg-white text-black border border-gray-300" + > + + Nouveau Groupe + +
+
+ + setNewGroupName(e.target.value)} + placeholder="Entrez le nom du groupe" + className="bg-white text-gray-900 border-gray-300" + />
- -
+ + + @@ -455,130 +501,111 @@ export function GroupsTable({ userRole = [] }: GroupsTableProps) {
- { - setModifyGroupDialog(open); - if (!open) { - // Reset state when dialog closes - setTimeout(() => { - setSelectedGroup(null); - setModifiedGroupName(""); - }, 100); - } - }} + - - - Modifier le groupe - -
-
- - setModifiedGroupName(e.target.value)} - className="bg-white text-gray-900 border-gray-300" - autoFocus - /> -
- + + Modifier le groupe + +
+
+ + setModifiedGroupName(e.target.value)} + className="bg-white text-gray-900 border-gray-300" + autoFocus + />
- -
+ + + - { - setManageMembersDialog(open); - if (!open) { - // Reset state when dialog closes - setTimeout(() => { - setSelectedGroup(null); - setGroupMembers([]); - setAvailableUsers([]); - }, 100); - } - }} + - - - - Gérer les membres - {selectedGroup?.name} - - -
-
-

Membres actuels

- {groupMembers.length === 0 ? ( -

Ce groupe n'a pas de membres.

- ) : ( -
- {groupMembers.map((member) => ( -
-
-
{member.username}
-
{member.firstName} {member.lastName} ({member.email})
-
- + + + Gérer les membres - {selectedGroup?.name} + + +
+
+

Membres actuels

+ {groupMembers.length === 0 ? ( +

Ce groupe n'a pas de membres.

+ ) : ( +
+ {groupMembers.map((member) => ( +
+
+
{member.username}
+
{member.firstName} {member.lastName} ({member.email})
- ))} -
- )} -
- -
-

Ajouter des membres

- setSearchTerm(e.target.value)} - className="mb-2 bg-white text-gray-900 border-gray-300" - /> -
- {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) => ( -
-
-
{user.username}
-
{user.firstName} {user.lastName} ({user.email})
-
- -
- ))} + +
+ ))}
+ )} +
+ +
+

Ajouter des membres

+ setSearchTerm(e.target.value)} + className="mb-2 bg-white text-gray-900 border-gray-300" + /> +
+ {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) => ( +
+
+
{user.username}
+
{user.firstName} {user.lastName} ({user.email})
+
+ +
+ ))}
- -
+ + ); } \ No newline at end of file diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx index 176109e6..f2ebb362 100644 --- a/components/ui/dialog.tsx +++ b/components/ui/dialog.tsx @@ -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", className )} + onPointerDownOutside={(e) => { + // Allow clicks outside to propagate normally + e.preventDefault(); + }} {...props} > {children} + + + Close + )) diff --git a/components/users/users-table.tsx b/components/users/users-table.tsx index eaf79079..2cd562c4 100644 --- a/components/users/users-table.tsx +++ b/components/users/users-table.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect, useMemo } from "react"; +import { useState, useEffect, useMemo, useCallback } from "react"; import { Table, TableBody, @@ -65,6 +65,41 @@ interface UsersTableProps { 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 ( + + {triggerComponent && {triggerComponent}} + + {children} + + + ); +} + export function UsersTable({ userRole = [] }: UsersTableProps) { const { data: session, status } = useSession(); const [users, setUsers] = useState([]); @@ -403,6 +438,37 @@ export function UsersTable({ userRole = [] }: UsersTableProps) { 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 (loading) return
Loading...
; @@ -416,115 +482,103 @@ export function UsersTable({ userRole = [] }: UsersTableProps) { onChange={(e) => setSearchTerm(e.target.value)} className="max-w-sm bg-white text-gray-900 border-gray-300" /> - { - setNewUserDialog(open); - if (!open) { - setTimeout(() => { - setFormData({ - username: "", - lastName: "", - firstName: "", - email: "", - password: "", - roles: [], - enabled: true, - }); - }, 100); - } - }}> - + Ajouter un utilisateur - - - - Nouvel Utilisateur - -
-
-
- - setFormData(prev => ({ ...prev, username: e.target.value.trim() }))} - required - className="bg-white text-gray-900 border-gray-300" - /> -
-
- - setFormData(prev => ({ ...prev, email: e.target.value.trim() }))} - required - className="bg-white text-gray-900 border-gray-300" - /> -
-
-
-
- - setFormData(prev => ({ ...prev, firstName: e.target.value.trim() }))} - required - className="bg-white text-gray-900 border-gray-300" - /> -
-
- - setFormData(prev => ({ ...prev, lastName: e.target.value.trim() }))} - required - className="bg-white text-gray-900 border-gray-300" - /> -
-
+ } + className="max-h-[85vh] overflow-y-auto bg-white text-black border border-gray-300" + > + + Nouvel Utilisateur + + +
- + setFormData(prev => ({ ...prev, password: e.target.value }))} + 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" />
- -
- {roles.map((role) => ( -
- { - setFormData(prev => ({ - ...prev, - roles: checked - ? [...prev.roles, role.name] - : prev.roles.filter(r => r !== role.name) - })); - }} - className="border-gray-500" - /> - -
- ))} -
+ + setFormData(prev => ({ ...prev, email: e.target.value.trim() }))} + required + className="bg-white text-gray-900 border-gray-300" + />
- - - -
+ +
+
+ + setFormData(prev => ({ ...prev, firstName: e.target.value.trim() }))} + required + className="bg-white text-gray-900 border-gray-300" + /> +
+
+ + setFormData(prev => ({ ...prev, lastName: e.target.value.trim() }))} + required + className="bg-white text-gray-900 border-gray-300" + /> +
+
+
+ + setFormData(prev => ({ ...prev, password: e.target.value }))} + required + className="bg-white text-gray-900 border-gray-300" + /> +
+
+ +
+ {roles.map((role) => ( +
+ { + setFormData(prev => ({ + ...prev, + roles: checked + ? [...prev.roles, role.name] + : prev.roles.filter(r => r !== role.name) + })); + }} + className="border-gray-500" + /> + +
+ ))} +
+
+ + + @@ -564,10 +618,6 @@ export function UsersTable({ userRole = [] }: UsersTableProps) {
- { - if (!open) { - setTimeout(() => { - setFormData({ - username: "", - lastName: "", - firstName: "", - email: "", - password: "", - roles: [], - enabled: true, - }); - setSelectedUser(null); - }, 100); - } - setEditUserDialog(open); - }}> - - - Modifier l'utilisateur - -
-
- - setFormData(prev => ({ ...prev, firstName: e.target.value }))} - className="bg-white text-gray-900 border-gray-300" - autoFocus - /> -
-
- - setFormData(prev => ({ ...prev, lastName: e.target.value }))} - className="bg-white text-gray-900 border-gray-300" - /> -
-
- - setFormData(prev => ({ ...prev, email: e.target.value }))} - className="bg-white text-gray-900 border-gray-300" - /> -
-
- - -
-
-
-
+ + + Modifier l'utilisateur + +
+
+ + setFormData(prev => ({ ...prev, firstName: e.target.value }))} + className="bg-white text-gray-900 border-gray-300" + autoFocus + /> +
+
+ + setFormData(prev => ({ ...prev, lastName: e.target.value }))} + className="bg-white text-gray-900 border-gray-300" + /> +
+
+ + setFormData(prev => ({ ...prev, email: e.target.value }))} + className="bg-white text-gray-900 border-gray-300" + /> +
+
+ + +
+
+
- { - if (!open) { - setTimeout(() => { - setFormData(prev => ({ ...prev, roles: [] })); - setSelectedUser(null); - }, 100); - } - setManageRolesDialog(open); - }}> - - - Gérer les rôles pour {selectedUser?.username} - -
-
- -
- {roles.map((role) => ( -
- { - setFormData(prev => ({ - ...prev, - roles: checked - ? [...prev.roles, role.name] - : prev.roles.filter(r => r !== role.name) - })); - }} - className="border-gray-500" - /> - -
- ))} -
-
-
- - + + + Gérer les rôles pour {selectedUser?.username} + +
+
+ +
+ {roles.map((role) => ( +
+ { + setFormData(prev => ({ + ...prev, + roles: checked + ? [...prev.roles, role.name] + : prev.roles.filter(r => r !== role.name) + })); + }} + className="border-gray-500" + /> + +
+ ))}
- -
+
+ + +
+ + ); }