From 8c3e70d3834cde555b5dcb82216e5d9074760dc4 Mon Sep 17 00:00:00 2001 From: seb Date: Tue, 23 Jun 2026 11:40:09 +0200 Subject: [PATCH] nav --- src/App.jsx | 54 +++++++++++++++++++++------- src/components/AppShell.jsx | 10 +++--- src/components/PageLayout.jsx | 67 +++++++++++++++++++++++++++++++---- src/components/UserMenu.jsx | 2 +- src/nav.js | 24 +++++++++++++ src/pages/SectionPage.jsx | 18 +++++++--- src/pages/SubPage.jsx | 9 +++++ 7 files changed, 156 insertions(+), 28 deletions(-) create mode 100644 src/pages/SubPage.jsx diff --git a/src/App.jsx b/src/App.jsx index 1235a89..eec455c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -5,7 +5,47 @@ import { useAuth } from './auth/useAuth'; import LoginPage from './components/LoginPage'; import AppShell from './components/AppShell'; import SectionPage from './pages/SectionPage'; -import { NAV_ITEMS } from './nav'; +import SubPage from './pages/SubPage'; +import { NAV_ITEMS, SUB_NAV } from './nav'; + +function sectionRoutes(path) { + const subNav = SUB_NAV[path]; + const routePath = path === '/' ? undefined : path.slice(1); + + if (subNav) { + return ( + }> + {subNav.map(({ segment, label }) => + segment ? ( + } + /> + ) : ( + } /> + ), + )} + + ); + } + + const label = NAV_ITEMS.find((n) => n.path === path)?.label; + + if (path === '/') { + return ( + } /> + ); + } + + return ( + } + /> + ); +} export default function App() { const { status } = useAuth(); @@ -34,17 +74,7 @@ export default function App() { }> - {NAV_ITEMS.map(({ path, label }) => - path === '/' ? ( - } /> - ) : ( - } - /> - ), - )} + {NAV_ITEMS.map((item) => sectionRoutes(item.path))} } /> diff --git a/src/components/AppShell.jsx b/src/components/AppShell.jsx index efd9d0d..30e444b 100644 --- a/src/components/AppShell.jsx +++ b/src/components/AppShell.jsx @@ -7,7 +7,7 @@ import { NavLink, Outlet } from 'react-router-dom'; import UserMenu from './UserMenu'; import { NAV_ITEMS } from '../nav'; -const SIDEBAR_WIDTH = 240; +const SIDEBAR_WIDTH = 144; export default function AppShell() { return ( @@ -31,8 +31,9 @@ export default function AppShell() { end={path === '/'} sx={(theme) => ({ borderRadius: 0, - px: 2, + px: 1.5, py: 1.25, + borderLeft: '3px solid transparent', color: theme.nav.item, '& .MuiListItemIcon-root': { color: 'inherit' }, '&:hover': { @@ -43,12 +44,11 @@ export default function AppShell() { bgcolor: theme.nav.itemActive, color: theme.nav.itemActiveText, fontWeight: 600, - borderLeft: `3px solid ${theme.nav.itemActiveAccent}`, - pl: '13px', + borderLeftColor: theme.nav.itemActiveAccent, }, })} > - + diff --git a/src/components/PageLayout.jsx b/src/components/PageLayout.jsx index 2fd8f70..73a5d86 100644 --- a/src/components/PageLayout.jsx +++ b/src/components/PageLayout.jsx @@ -1,13 +1,68 @@ import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; +import { NavLink } from 'react-router-dom'; + +const SUB_NAV_WIDTH = 80; + +export default function PageLayout({ title, subNav, basePath, children }) { + const hasSubNav = subNav?.length > 0; -export default function PageLayout({ title, children }) { return ( - - - {title} - - {children} + + + {hasSubNav && + subNav.map(({ segment, label, icon: Icon }) => { + const to = segment ? `${basePath}/${segment}` : basePath; + + return ( + + ({ + width: 80, + height: 80, + color: theme.palette.text.secondary, + bgcolor: 'transparent', + '&:hover': { + bgcolor: 'transparent', + color: theme.palette.primary.main, + }, + '&.active': { + bgcolor: 'transparent', + color: theme.palette.primary.main, + }, + })} + > + + + + ); + })} + + + + + {title} + + {children} + ); } diff --git a/src/components/UserMenu.jsx b/src/components/UserMenu.jsx index 80f4a48..c7e7480 100644 --- a/src/components/UserMenu.jsx +++ b/src/components/UserMenu.jsx @@ -17,7 +17,7 @@ export default function UserMenu() { return ( <> - + setAnchorEl(e.currentTarget)} sx={{ p: 0, width: 32, height: 32, borderRadius: '50%' }} diff --git a/src/nav.js b/src/nav.js index b5d8495..ac6a0fc 100644 --- a/src/nav.js +++ b/src/nav.js @@ -4,6 +4,13 @@ import LocalShippingIcon from '@mui/icons-material/LocalShipping'; import AssignmentIcon from '@mui/icons-material/Assignment'; import PeopleIcon from '@mui/icons-material/People'; import InfoIcon from '@mui/icons-material/Info'; +import ViewListIcon from '@mui/icons-material/ViewList'; +import CategoryIcon from '@mui/icons-material/Category'; +import WarehouseIcon from '@mui/icons-material/Warehouse'; +import ListAltIcon from '@mui/icons-material/ListAlt'; +import ArchiveIcon from '@mui/icons-material/Archive'; +import GroupsIcon from '@mui/icons-material/Groups'; +import ContactPageIcon from '@mui/icons-material/ContactPage'; export const NAV_ITEMS = [ { path: '/', label: 'Home', icon: HomeIcon }, @@ -13,3 +20,20 @@ export const NAV_ITEMS = [ { path: '/kunde', label: 'Kunde', icon: PeopleIcon }, { path: '/infos', label: 'Infos', icon: InfoIcon }, ]; + +export const SUB_NAV = { + '/artikel': [ + { segment: '', label: 'Übersicht', icon: ViewListIcon }, + { segment: 'kategorien', label: 'Kategorien', icon: CategoryIcon }, + { segment: 'lager', label: 'Lager', icon: WarehouseIcon }, + ], + '/auftrag': [ + { segment: '', label: 'Aktive Aufträge', icon: ListAltIcon }, + { segment: 'archiv', label: 'Archiv', icon: ArchiveIcon }, + ], + '/kunde': [ + { segment: '', label: 'Kundenliste', icon: PeopleIcon }, + { segment: 'gruppen', label: 'Gruppen', icon: GroupsIcon }, + { segment: 'kontakte', label: 'Kontakte', icon: ContactPageIcon }, + ], +}; diff --git a/src/pages/SectionPage.jsx b/src/pages/SectionPage.jsx index 448f217..35c6bcd 100644 --- a/src/pages/SectionPage.jsx +++ b/src/pages/SectionPage.jsx @@ -1,10 +1,20 @@ -import Typography from '@mui/material/Typography'; +import { Outlet, useLocation } from 'react-router-dom'; import PageLayout from '../components/PageLayout'; +import { NAV_ITEMS, SUB_NAV } from '../nav'; + +export default function SectionPage({ sectionPath, children }) { + const { pathname } = useLocation(); + const title = NAV_ITEMS.find((item) => item.path === sectionPath)?.label ?? ''; + const subNav = SUB_NAV[sectionPath]; + const activeSub = subNav?.find((item) => { + const subPath = item.segment ? `${sectionPath}/${item.segment}` : sectionPath; + return pathname === subPath || pathname === `${subPath}/`; + }); + const pageTitle = activeSub && subNav ? activeSub.label : title; -export default function SectionPage({ title }) { return ( - - Inhalt folgt. + + {children ?? } ); } diff --git a/src/pages/SubPage.jsx b/src/pages/SubPage.jsx new file mode 100644 index 0000000..5f18e34 --- /dev/null +++ b/src/pages/SubPage.jsx @@ -0,0 +1,9 @@ +import Typography from '@mui/material/Typography'; + +export default function SubPage({ title, description }) { + return ( + + {description ?? `${title} — Inhalt folgt.`} + + ); +}