Convert AdminPanel to modal overlay with close button and add admin dashboard menu option
Refactored AdminPanel from inline component to modal overlay triggered from user menu. Added `open` and `onClose` props to control visibility, wrapped panel in modal overlay with backdrop click-to-close behavior, and added close button to header. Implemented admin-only menu item in TopBar user dropdown with shield icon and role label. Added state management for dashboard visibility in App.jsx with proper cleanup
This commit is contained in:
@@ -61,6 +61,7 @@ export default function App() {
|
||||
const [chipOpen, setChipOpen] = useState(false);
|
||||
const [chipEntry, setChipEntry] = useState(null);
|
||||
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
||||
const [adminOpen, setAdminOpen] = useState(false);
|
||||
|
||||
const [pwOpen, setPwOpen] = useState(false);
|
||||
const [pw1, setPw1] = useState("");
|
||||
@@ -689,11 +690,15 @@ export default function App() {
|
||||
openPwModal={openPwModal}
|
||||
openDesignModal={openDesignModal}
|
||||
openStatsModal={openStatsModal}
|
||||
openAdminPanel={() => {
|
||||
setAdminOpen(true);
|
||||
setUserMenuOpen(false);
|
||||
}}
|
||||
doLogout={doLogout}
|
||||
onOpenNewGame={() => setNewGameOpen(true)}
|
||||
/>
|
||||
|
||||
{me.role === "admin" && <AdminPanel />}
|
||||
<AdminPanel open={me.role === "admin" && adminOpen} onClose={() => setAdminOpen(false)} />
|
||||
|
||||
<GamePickerCard
|
||||
games={games}
|
||||
|
||||
@@ -4,10 +4,10 @@ import { styles } from "../styles/styles";
|
||||
import { stylesTokens } from "../styles/theme";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
export default function AdminPanel() {
|
||||
export default function AdminPanel({ open: dashboardOpen = false, onClose }) {
|
||||
const [users, setUsers] = useState([]);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [userModalOpen, setUserModalOpen] = useState(false);
|
||||
const [displayName, setDisplayName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
@@ -15,7 +15,7 @@ export default function AdminPanel() {
|
||||
const [msg, setMsg] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
if (!dashboardOpen && !userModalOpen) return;
|
||||
|
||||
const prev = document.body.style.overflow;
|
||||
document.body.style.overflow = "hidden";
|
||||
@@ -23,7 +23,14 @@ export default function AdminPanel() {
|
||||
return () => {
|
||||
document.body.style.overflow = prev;
|
||||
};
|
||||
}, [open]);
|
||||
}, [dashboardOpen, userModalOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!dashboardOpen) {
|
||||
setUserModalOpen(false);
|
||||
setMsg("");
|
||||
}
|
||||
}, [dashboardOpen]);
|
||||
|
||||
const loadUsers = async () => {
|
||||
const u = await api("/admin/users");
|
||||
@@ -51,7 +58,7 @@ export default function AdminPanel() {
|
||||
setMsg("✅ User erstellt.");
|
||||
await loadUsers();
|
||||
resetForm();
|
||||
setOpen(false);
|
||||
setUserModalOpen(false);
|
||||
} catch (e) {
|
||||
setMsg("❌ Fehler: " + (e?.message || "unknown"));
|
||||
}
|
||||
@@ -68,17 +75,29 @@ export default function AdminPanel() {
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setOpen(false);
|
||||
setUserModalOpen(false);
|
||||
setMsg("");
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={styles.adminWrap}>
|
||||
if (!dashboardOpen) return null;
|
||||
|
||||
return createPortal(
|
||||
<div style={styles.modalOverlay} onMouseDown={onClose}>
|
||||
<div
|
||||
style={{ ...styles.modalCard, width: "min(760px, 100%)", padding: 0 }}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div style={{ ...styles.adminWrap, marginTop: 0, border: "none", boxShadow: "none", backdropFilter: "none", WebkitBackdropFilter: "none" }}>
|
||||
<div style={styles.adminTop}>
|
||||
<div style={styles.adminTitle}>Admin Dashboard</div>
|
||||
<button onClick={() => setOpen(true)} style={styles.primaryBtn}>
|
||||
<div style={{ display: "flex", gap: 8, alignItems: "center" }}>
|
||||
<button onClick={() => setUserModalOpen(true)} style={styles.primaryBtn}>
|
||||
+ User anlegen
|
||||
</button>
|
||||
<button onClick={onClose} style={styles.modalCloseBtn} aria-label="Dashboard schließen">
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 12, fontWeight: 900, color: stylesTokens.textGold }}>
|
||||
@@ -125,7 +144,7 @@ export default function AdminPanel() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{open &&
|
||||
{userModalOpen &&
|
||||
createPortal(
|
||||
<div style={styles.modalOverlay} onMouseDown={closeModal}>
|
||||
<div style={styles.modalCard} onMouseDown={(e) => e.stopPropagation()}>
|
||||
@@ -201,5 +220,8 @@ export default function AdminPanel() {
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,13 @@ export default function TopBar({
|
||||
openPwModal,
|
||||
openDesignModal,
|
||||
openStatsModal,
|
||||
openAdminPanel,
|
||||
doLogout,
|
||||
onOpenNewGame,
|
||||
}) {
|
||||
const displayName = me ? ((me.display_name || "").trim() || me.email) : "";
|
||||
const isAdmin = me?.role === "admin";
|
||||
const roleLabel = isAdmin ? "Admin" : "User";
|
||||
|
||||
return (
|
||||
<div className="hp-topbar" style={styles.topBar}>
|
||||
@@ -31,8 +34,8 @@ export default function TopBar({
|
||||
style={styles.userBtn}
|
||||
title="User Menü"
|
||||
>
|
||||
<span style={{ fontSize: 16 }}>👤</span>
|
||||
<span>User</span>
|
||||
<span style={{ fontSize: 16 }}>{isAdmin ? "🛡️" : "👤"}</span>
|
||||
<span>{roleLabel}</span>
|
||||
<span style={{ opacity: 0.7 }}>▾</span>
|
||||
</button>
|
||||
|
||||
@@ -60,6 +63,21 @@ export default function TopBar({
|
||||
Statistik
|
||||
</button>
|
||||
|
||||
{isAdmin && (
|
||||
<>
|
||||
<div style={styles.userDropdownDivider} />
|
||||
<button
|
||||
onClick={() => {
|
||||
setUserMenuOpen(false);
|
||||
openAdminPanel?.();
|
||||
}}
|
||||
style={styles.userDropdownItem}
|
||||
>
|
||||
Admin Dashboard
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div style={styles.userDropdownDivider} />
|
||||
|
||||
<button onClick={openPwModal} style={styles.userDropdownItem}>
|
||||
|
||||
Reference in New Issue
Block a user