From fae46c83951e95a5fecc721c565c76313bd62afe Mon Sep 17 00:00:00 2001 From: Michelle Date: Sun, 1 Mar 2026 14:18:21 +0100 Subject: [PATCH] feat(admin): add context menu for user actions with dynamic positioning --- src/i18n/de.json | 1 + src/i18n/en.json | 1 + src/pages/Admin.jsx | 92 ++++++++++++++++++++++++++++++--------------- 3 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/i18n/de.json b/src/i18n/de.json index 4b8e247..7d41010 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -230,6 +230,7 @@ "presentationRemoveFailed": "Präsentation konnte nicht entfernt werden", "presentationAllowedTypes": "PDF, PPT, PPTX, ODP, DOC, DOCX · max. 50 MB", "presentationCurrent": "Aktuell:", + "shareTitle": "Raum teilen", "shareDescription": "Teilen Sie diesen Raum mit anderen Benutzern, damit diese ihn in ihrem Dashboard sehen und beitreten k\u00f6nnen.", "shareSearchPlaceholder": "Benutzer suchen (Name oder E-Mail)...", "shareAdded": "Benutzer hinzugef\u00fcgt", diff --git a/src/i18n/en.json b/src/i18n/en.json index 989ccbb..54faaf0 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -230,6 +230,7 @@ "presentationRemoveFailed": "Could not remove presentation", "presentationAllowedTypes": "PDF, PPT, PPTX, ODP, DOC, DOCX · max. 50 MB", "presentationCurrent": "Current:", + "shareTitle": "Share Room", "shareDescription": "Share this room with other users so they can see it in their dashboard and join meetings.", "shareSearchPlaceholder": "Search users (name or email)...", "shareAdded": "User added", diff --git a/src/pages/Admin.jsx b/src/pages/Admin.jsx index 22b8897..db6ed0c 100644 --- a/src/pages/Admin.jsx +++ b/src/pages/Admin.jsx @@ -27,6 +27,8 @@ export default function Admin() { const [showCreateUser, setShowCreateUser] = useState(false); const [creatingUser, setCreatingUser] = useState(false); const [newUser, setNewUser] = useState({ name: '', display_name: '', email: '', password: '', role: 'user' }); + const menuBtnRefs = useRef({}); + const [menuPos, setMenuPos] = useState(null); // Invite state const [invites, setInvites] = useState([]); @@ -88,6 +90,7 @@ export default function Admin() { toast.error(err.response?.data?.error || t('admin.roleUpdateFailed')); } setOpenMenu(null); + setMenuPos(null); }; const handleDelete = async (userId, userName) => { @@ -100,6 +103,7 @@ export default function Admin() { toast.error(err.response?.data?.error || t('admin.userDeleteFailed')); } setOpenMenu(null); + setMenuPos(null); }; const handleResetPassword = async (e) => { @@ -580,43 +584,32 @@ export default function Admin() { {new Date(u.created_at).toLocaleDateString(language === 'de' ? 'de-DE' : 'en-US')} -
+
- - {openMenu === u.id && u.id !== user.id && ( - <> -
setOpenMenu(null)} /> -
- - - -
- - )}
@@ -633,6 +626,43 @@ export default function Admin() { )}
+ {/* Context menu portal */} + {openMenu && menuPos && openMenu !== user.id && (() => { + const u = users.find(u => u.id === openMenu); + if (!u) return null; + return ( + <> +
{ setOpenMenu(null); setMenuPos(null); }} /> +
+ + + +
+ + ); + })()} + {/* Reset password modal */} {resetPwModal && (