# 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) └──
└── ``` ## NavItem Filtering ```ts 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: - `onMenuOpen` to `MobileBar` - `onClose` to `SidebarOverlay` → forwarded as `onClose` to `Sidebar` Closes automatically on route change via `useEffect` watching the current pathname. ## Theme Initialisation (no flash) A blocking inline ` ``` The `useTheme` hook reads from `localStorage` on mount and keeps toggle in sync with the DOM class. ## Theme Toggle Logic (`useTheme`) ```ts 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' })`.