This commit is contained in:
seb
2026-06-23 11:21:22 +02:00
parent 09f53b5ac2
commit 3009a3fb96
10 changed files with 148 additions and 35 deletions

View File

@@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Dashfront</title>
</head>

60
package-lock.json generated
View File

@@ -14,7 +14,8 @@
"@mui/material": "^9.1.2",
"notistack": "^3.0.2",
"react": "^19.2.7",
"react-dom": "^19.2.7"
"react-dom": "^19.2.7",
"react-router-dom": "^7.18.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^6.0.2",
@@ -1036,6 +1037,19 @@
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"license": "MIT"
},
"node_modules/cookie": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/cosmiconfig": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
@@ -1772,6 +1786,44 @@
"integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==",
"license": "MIT"
},
"node_modules/react-router": {
"version": "7.18.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.0.tgz",
"integrity": "sha512-pTTGt8J+ji1NOmYnjzT+bAJy/1zD+Jp4ziO6cL7T3ZLvXKtusO7BpFqlRXitqpcPVqllsIXFHRMt+2/k3Xn6HQ==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
}
}
},
"node_modules/react-router-dom": {
"version": "7.18.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.0.tgz",
"integrity": "sha512-Fi0yY6kgtKae/Th2xibdWK0KSdYZ4B53Gyf6wRtomOKWgpNm7H7+DyfDhncdz9FKbpS+1jmDhg3F4WoGJ+yFOA==",
"license": "MIT",
"dependencies": {
"react-router": "7.18.0"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
@@ -1858,6 +1910,12 @@
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"license": "MIT"
},
"node_modules/set-cookie-parser": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
"license": "MIT"
},
"node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",

View File

@@ -16,7 +16,8 @@
"@mui/material": "^9.1.2",
"notistack": "^3.0.2",
"react": "^19.2.7",
"react-dom": "^19.2.7"
"react-dom": "^19.2.7",
"react-router-dom": "^7.18.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^6.0.2",

12
public/favicon.svg Normal file
View File

@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="6" fill="#3b5bdb"/>
<text
x="16"
y="22"
text-anchor="middle"
font-family="system-ui, sans-serif"
font-size="18"
font-weight="700"
fill="#fff"
>D</text>
</svg>

After

Width:  |  Height:  |  Size: 287 B

View File

@@ -1,10 +1,12 @@
import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
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';
// Top-level router by auth status. Kept tiny so it rarely re-renders.
export default function App() {
const { status } = useAuth();
@@ -23,5 +25,28 @@ export default function App() {
);
}
return status === 'authed' ? <AppShell /> : <LoginPage />;
if (status !== 'authed') {
return <LoginPage />;
}
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<AppShell />}>
{NAV_ITEMS.map(({ path, label }) =>
path === '/' ? (
<Route key={path} index element={<SectionPage title={label} />} />
) : (
<Route
key={path}
path={path.slice(1)}
element={<SectionPage title={label} />}
/>
),
)}
<Route path="*" element={<Navigate to="/" replace />} />
</Route>
</Routes>
</BrowserRouter>
);
}

View File

@@ -1,21 +1,15 @@
import { useState } from 'react';
import Box from '@mui/material/Box';
import List from '@mui/material/List';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import { NavLink, Outlet } from 'react-router-dom';
import UserMenu from './UserMenu';
import { NAV_ITEMS } from '../nav';
const SIDEBAR_WIDTH = 240;
// App layout. State lives in the children (UserMenu, page content) so the
// shell itself does not re-render when their state changes.
export default function AppShell() {
const [activeId, setActiveId] = useState(NAV_ITEMS[0].id);
const activeItem = NAV_ITEMS.find((item) => item.id === activeId) ?? NAV_ITEMS[0];
const ActivePage = activeItem.Page;
return (
<Box sx={{ display: 'flex', minHeight: '100vh', bgcolor: 'grey.50' }}>
<Box
@@ -29,13 +23,17 @@ export default function AppShell() {
}}
>
<List disablePadding sx={{ flexGrow: 1 }}>
{NAV_ITEMS.map(({ id, label, icon: Icon }) => (
{NAV_ITEMS.map(({ path, label, icon: Icon }) => (
<ListItemButton
key={id}
disableGutters
selected={id === activeId}
onClick={() => setActiveId(id)}
sx={{ borderRadius: 0, px: 2 }}
key={path}
component={NavLink}
to={path}
end={path === '/'}
sx={{
borderRadius: 0,
px: 2,
'&.active': { bgcolor: 'action.selected' },
}}
>
<ListItemIcon sx={{ minWidth: 40 }}>
<Icon />
@@ -49,7 +47,7 @@ export default function AppShell() {
</Box>
<Box component="main" sx={{ flexGrow: 1, py: 4, px: 3 }}>
<ActivePage />
<Outlet />
</Box>
</Box>
);

View File

@@ -0,0 +1,13 @@
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
export default function PageLayout({ title, children }) {
return (
<Box>
<Typography variant="h5" sx={{ fontWeight: 700, mb: 2 }}>
{title}
</Typography>
{children}
</Box>
);
}

View File

@@ -1,11 +1,15 @@
import DashboardIcon from '@mui/icons-material/Dashboard';
import DashboardPage from './pages/DashboardPage';
import HomeIcon from '@mui/icons-material/Home';
import Inventory2Icon from '@mui/icons-material/Inventory2';
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';
export const NAV_ITEMS = [
{
id: 'dashboard',
label: 'Dashboard',
icon: DashboardIcon,
Page: DashboardPage,
},
{ path: '/', label: 'Home', icon: HomeIcon },
{ path: '/artikel', label: 'Artikel', icon: Inventory2Icon },
{ path: '/lieferung', label: 'Lieferung', icon: LocalShippingIcon },
{ path: '/auftrag', label: 'Auftrag', icon: AssignmentIcon },
{ path: '/kunde', label: 'Kunde', icon: PeopleIcon },
{ path: '/infos', label: 'Infos', icon: InfoIcon },
];

View File

@@ -1,9 +0,0 @@
import Typography from '@mui/material/Typography';
export default function DashboardPage() {
return (
<Typography variant="h5" sx={{ fontWeight: 700 }}>
Dashboard
</Typography>
);
}

10
src/pages/SectionPage.jsx Normal file
View File

@@ -0,0 +1,10 @@
import Typography from '@mui/material/Typography';
import PageLayout from '../components/PageLayout';
export default function SectionPage({ title }) {
return (
<PageLayout title={title}>
<Typography color="text.secondary">Inhalt folgt.</Typography>
</PageLayout>
);
}