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" ? ( + + + + ) : ( + + + + + )} + + + + + + + + + + + ); +}