All Q1-Q5 answered A: - Topbar removed; LanguageSwitcher + UserMenu move to sidebar footer - Mobile: slide-over overlay with hamburger button - Theme toggle in sidebar footer - No-flash init via inline script in index.html - Role-filtered sidebar items client-side Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2.2 KiB
2.2 KiB
Business Logic Model — Unit 3: Layout & Navigation
Component Hierarchy
AppLayout
├── Sidebar desktop: always visible (md+)
│ ├── SidebarHeader (logo + app name)
│ ├── NavList
│ │ └── NavItem × N filtered by user.role
│ └── SidebarFooter
│ ├── LanguageSwitcher moved from Topbar
│ ├── ThemeToggle new
│ └── UserMenu moved from Topbar
├── MobileBar mobile only (< md)
│ └── hamburger button → opens SidebarOverlay
├── SidebarOverlay rendered only when mobile menu is open
│ ├── backdrop (closes on click)
│ └── Sidebar (with onClose prop)
└── <main>
└── <Outlet />
NavItem Filtering
const visibleItems = NAV_ITEMS.filter(item =>
!item.roles || item.roles.includes(user.role)
);
item.roles === undefined means visible to all authenticated users. Evaluated at render time.
Mobile Overlay State
isMenuOpen: boolean state lives in AppLayout. Passed as:
onMenuOpentoMobileBaronClosetoSidebarOverlay→ forwarded asonClosetoSidebar
Closes automatically on route change via useEffect watching the current pathname.
Theme Initialisation (no flash)
A blocking inline <script> in index.html runs synchronously before React loads:
<script>
(function() {
var stored = localStorage.getItem('cms-theme');
var theme = stored
? stored
: (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.classList.add(theme);
})();
</script>
The useTheme hook reads from localStorage on mount and keeps toggle in sync with the DOM class.
Theme Toggle Logic (useTheme)
function toggleTheme() {
const next = theme === 'dark' ? 'light' : 'dark';
setTheme(next);
localStorage.setItem('cms-theme', next);
document.documentElement.classList.remove('light', 'dark');
document.documentElement.classList.add(next);
}
Logout Flow (unchanged)
UserMenu in sidebar footer calls useAuth().logout() then navigate({ to: '/login' }).