From 9dbb5dbaab7f401e6798d0836aa9b716ccd9e0b1 Mon Sep 17 00:00:00 2001 From: BOHA Date: Sat, 6 Jun 2026 20:11:52 +0200 Subject: [PATCH] =?UTF-8?q?feat(mui):=20AppShell=20=E2=80=94=20responsive?= =?UTF-8?q?=20Drawer=20+=20topbar,=20guards=20+=20logout=20animation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- src/admin/ui/AppShell.tsx | 188 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 src/admin/ui/AppShell.tsx diff --git a/src/admin/ui/AppShell.tsx b/src/admin/ui/AppShell.tsx new file mode 100644 index 0000000..f73590f --- /dev/null +++ b/src/admin/ui/AppShell.tsx @@ -0,0 +1,188 @@ +import { useState, useCallback } from "react"; +import { Outlet, Navigate, useLocation } from "react-router-dom"; +import { motion } from "framer-motion"; +import Box from "@mui/material/Box"; +import Drawer from "@mui/material/Drawer"; +import IconButton from "@mui/material/IconButton"; +import ScopedCssBaseline from "@mui/material/ScopedCssBaseline"; +import { useAuth } from "../context/AuthContext"; +import { useTheme as useAppTheme } from "../../context/ThemeContext"; +import { setLogoutAlert } from "../utils/api"; +import SidebarNav from "./SidebarNav"; +import ShortcutsHelp from "../components/ShortcutsHelp"; + +const DRAWER_WIDTH = 248; + +export default function AppShell() { + const { isAuthenticated, loading, user, logout } = useAuth(); + const { theme, toggleTheme } = useAppTheme(); + const [mobileOpen, setMobileOpen] = useState(false); + const [loggingOut, setLoggingOut] = useState(false); + const location = useLocation(); + + const handleLogout = useCallback(() => { + setLoggingOut(true); + setMobileOpen(false); + setLogoutAlert(); + setTimeout(() => logout(), 400); + }, [logout]); + + if (loading) { + return ( + + +
+ + + ); + } + if (!isAuthenticated) return ; + if (user?.require2FA && !user?.totpEnabled && location.pathname !== "/") { + return ; + } + + return ( + + + + + + + + setMobileOpen(false)} + ModalProps={{ keepMounted: true }} + sx={{ + display: { xs: "block", md: "none" }, + "& .MuiDrawer-paper": { + width: DRAWER_WIDTH, + boxSizing: "border-box", + }, + }} + > + setMobileOpen(false)} + onLogout={handleLogout} + /> + + + + + setMobileOpen(true)} + aria-label="Otevřít menu" + sx={{ display: { md: "none" } }} + > + + + + + + + + + {theme === "dark" ? ( + + + + ) : ( + + + + + )} + + + + + + + + + + + ); +}