From e4dbf30dbb00a42b131e875cd642bd3850c3206d Mon Sep 17 00:00:00 2001 From: Andrei Date: Tue, 7 Oct 2025 16:03:35 +0000 Subject: [PATCH] feat: Replace individual action icons with dropdown menu in users table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improved UX by consolidating user actions into a single dropdown menu: - Added MoreVert icon button to open actions menu - Menu includes: View Details, Edit User, Activate/Deactivate, Delete User - Delete action shown in error color for visual distinction - Cleaner table layout with single action button per row 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- parentflow-admin/src/app/users/page.tsx | 86 ++++++++++++++++++------- 1 file changed, 61 insertions(+), 25 deletions(-) diff --git a/parentflow-admin/src/app/users/page.tsx b/parentflow-admin/src/app/users/page.tsx index dad363c..adec23d 100644 --- a/parentflow-admin/src/app/users/page.tsx +++ b/parentflow-admin/src/app/users/page.tsx @@ -27,6 +27,10 @@ import { Grid, Switch, FormControlLabel, + Menu, + MenuItem, + ListItemIcon, + ListItemText, } from '@mui/material'; import { Search, @@ -36,6 +40,7 @@ import { PersonAdd, Block, CheckCircle, + MoreVert, } from '@mui/icons-material'; import AdminLayout from '@/components/AdminLayout'; import apiClient from '@/lib/api-client'; @@ -68,6 +73,8 @@ export default function UsersPage() { const [selectedUser, setSelectedUser] = useState(null); const [viewDialogOpen, setViewDialogOpen] = useState(false); const [editDialogOpen, setEditDialogOpen] = useState(false); + const [anchorEl, setAnchorEl] = useState(null); + const [menuUser, setMenuUser] = useState(null); useEffect(() => { fetchUsers(); @@ -92,14 +99,26 @@ export default function UsersPage() { } }; + const handleOpenMenu = (event: React.MouseEvent, user: User) => { + setAnchorEl(event.currentTarget); + setMenuUser(user); + }; + + const handleCloseMenu = () => { + setAnchorEl(null); + setMenuUser(null); + }; + const handleViewUser = (user: User) => { setSelectedUser(user); setViewDialogOpen(true); + handleCloseMenu(); }; const handleEditUser = (user: User) => { setSelectedUser(user); setEditDialogOpen(true); + handleCloseMenu(); }; const handleToggleUserStatus = async (user: User) => { @@ -108,6 +127,7 @@ export default function UsersPage() { emailVerified: !user.emailVerified, }); fetchUsers(); + handleCloseMenu(); } catch (error) { console.error('Failed to update user status:', error); } @@ -118,6 +138,7 @@ export default function UsersPage() { try { await apiClient.delete(`/admin/users/${userId}`); fetchUsers(); + handleCloseMenu(); } catch (error) { console.error('Failed to delete user:', error); } @@ -270,32 +291,10 @@ export default function UsersPage() { handleViewUser(user)} - title="View" + onClick={(e) => handleOpenMenu(e, user)} + title="Actions" > - - - handleEditUser(user)} - title="Edit" - > - - - handleToggleUserStatus(user)} - title={user.emailVerified ? 'Deactivate' : 'Activate'} - > - {user.emailVerified ? : } - - handleDeleteUser(user.id)} - title="Delete" - color="error" - > - + @@ -316,6 +315,43 @@ export default function UsersPage() { /> + {/* Actions Menu */} + + menuUser && handleViewUser(menuUser)}> + + + + View Details + + menuUser && handleEditUser(menuUser)}> + + + + Edit User + + menuUser && handleToggleUserStatus(menuUser)}> + + {menuUser?.emailVerified ? : } + + + {menuUser?.emailVerified ? 'Deactivate' : 'Activate'} + + + menuUser && handleDeleteUser(menuUser.id)} + sx={{ color: 'error.main' }} + > + + + + Delete User + + + {/* View User Dialog */}