diff --git a/frontend/index.html b/frontend/index.html index c818fb6..de010d4 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,9 +2,20 @@ - - + + + + + + + + + + + + + Shinkan Jinkendo diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 2575da2..e3fb0c3 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,16 +1,55 @@ import React from 'react' -import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom' +import { BrowserRouter as Router, Routes, Route, Navigate, NavLink, useLocation } from 'react-router-dom' import { AuthProvider, useAuth } from './context/AuthContext' -import Navigation from './components/Navigation' +import DesktopSidebar from './components/DesktopSidebar' +import { getMainNavItems } from './config/appNav' import LoginPage from './pages/LoginPage' import Dashboard from './pages/Dashboard' import ProfilePage from './pages/ProfilePage' import ExercisesPage from './pages/ExercisesPage' import ClubsPage from './pages/ClubsPage' +import './app.css' + +// Bottom Navigation (Mobile) +function Nav({ isAdmin }) { + const items = getMainNavItems(isAdmin) + const loc = useLocation() + + const navItemActive = (pathname, item, routerIsActive) => { + if (item.to.startsWith('/admin')) return pathname.startsWith('/admin') + return routerIsActive + } + + return ( + + ) +} // Protected Route Component function ProtectedRoute({ children }) { - const { isAuthenticated, loading } = useAuth() + const { isAuthenticated, loading, user, logout } = useAuth() + + const handleLogout = () => { + if (confirm('Wirklich abmelden?')) { + logout() + window.location.href = '/' + } + } if (loading) { return ( @@ -26,12 +65,30 @@ function ProtectedRoute({ children }) { ) } - return isAuthenticated ? ( + if (!isAuthenticated) { + return + } + + const isAdmin = user?.role === 'admin' || user?.role === 'superadmin' + + return ( <> - - {children} + +
+
+
+
🥋 Shinkan
+
+
{children}
+
+
- ) : + ) } // Public Route Component (redirect to dashboard if already logged in) diff --git a/frontend/src/components/DesktopSidebar.jsx b/frontend/src/components/DesktopSidebar.jsx new file mode 100644 index 0000000..4642c43 --- /dev/null +++ b/frontend/src/components/DesktopSidebar.jsx @@ -0,0 +1,86 @@ +import { NavLink, useLocation } from 'react-router-dom' +import { LogOut } from 'lucide-react' +import { getMainNavItems } from '../config/appNav' + +function sidebarLinkActive(pathname, item, routerIsActive) { + if (item.to.startsWith('/admin')) return pathname.startsWith('/admin') + return routerIsActive +} + +/** + * Desktop-Sidebar (≥1024px) — Sichtbarkeit via CSS (.desktop-sidebar). + */ +export default function DesktopSidebar({ + isAdmin, + user, + onLogout +}) { + const loc = useLocation() + const items = getMainNavItems(isAdmin) + const tier = user?.tier || '' + + return ( + + ) +} diff --git a/frontend/src/config/appNav.js b/frontend/src/config/appNav.js new file mode 100644 index 0000000..0aa93a7 --- /dev/null +++ b/frontend/src/config/appNav.js @@ -0,0 +1,48 @@ +import { + LayoutDashboard, + BookOpen, + Calendar, + Building2, + Settings, + Shield +} from 'lucide-react' + +/** + * Shinkan Navigation Configuration + * Single source of truth für Bottom-Nav (Mobile) + Desktop-Sidebar + * + * @typedef {{ to: string, label: string, shortLabel?: string, end?: boolean, Icon: import('react').ForwardRefExoticComponent }} AppNavItem + */ + +/** @returns {Omit[]} */ +function baseItems() { + return [ + { to: '/', label: 'Übersicht', end: true }, + { to: '/exercises', label: 'Übungen', shortLabel: 'Übungen' }, + { to: '/planning', label: 'Planung' }, + { to: '/clubs', label: 'Vereine' }, + { to: '/settings', label: 'Einstellungen', shortLabel: 'Einst.' } + ] +} + +/** @param {boolean} isAdmin */ +export function getMainNavItems(isAdmin) { + const icons = [ + LayoutDashboard, + BookOpen, + Calendar, + Building2, + Settings + ] + + const raw = baseItems().map((item, i) => ({ + ...item, + Icon: icons[i] + })) + + if (isAdmin) { + raw.push({ to: '/admin', label: 'Admin', end: false, Icon: Shield }) + } + + return raw +}