# Frontend Components — Unit 3: Layout & Navigation ## Modified Components ### `AppLayout.tsx` (modify) **Path**: `frontend/src/components/layout/AppLayout.tsx` Remove `Topbar`. Add `MobileBar` and `SidebarOverlay`. Manage `isMenuOpen` state. ```tsx
{isMenuOpen && setIsMenuOpen(false)} />}
setIsMenuOpen(true)} />
``` --- ### `Sidebar.tsx` (modify) **Path**: `frontend/src/components/layout/Sidebar.tsx` Changes: - Add `roles?: Role[]` to `NavItem` — filter visible items by `user.role` - Add `SidebarFooter` section at the bottom with `LanguageSwitcher`, `ThemeToggle`, `UserMenu` - Accept optional `onClose?: () => void` prop (used by mobile overlay close button) - Retain `data-testid="app-sidebar"` Nav items and role visibility: | Label key | Route | Roles | Icon | |---|---|---|---| | `nav.dashboard` | `/dashboard` | all | `LayoutDashboard` | | `nav.users` | `/users` | Owner, Administrator | `Users` | | `nav.settings` | `/settings` | Owner | `Settings` | | `nav.cms` | `/cms` | Owner | `FileText` | | `nav.profile` | `/profile` | all | `User` | --- ## New Components ### `MobileBar.tsx` **Path**: `frontend/src/components/layout/MobileBar.tsx` Visible only on `< md`. Contains hamburger button and app name. Props: `onMenuOpen: () => void` ```tsx
{t('common.appName')}
``` --- ### `SidebarOverlay.tsx` **Path**: `frontend/src/components/layout/SidebarOverlay.tsx` Full-screen overlay wrapping `Sidebar` for mobile. Backdrop closes it on click. ```tsx
``` Props: `onClose: () => void` --- ### `ThemeToggle.tsx` **Path**: `frontend/src/components/layout/ThemeToggle.tsx` Toggles dark/light mode. Uses `useTheme()` hook. ```tsx ``` --- ### `useTheme.ts` **Path**: `frontend/src/hooks/useTheme.ts` ```ts function useTheme(): { theme: Theme; isDark: boolean; toggleTheme: () => void } ``` Reads initial value from `localStorage['cms-theme']` or `prefers-color-scheme`. Syncs DOM class on ``. --- ## Removed Components ### `Topbar.tsx` Deleted. `data-testid="app-topbar"` removed from the DOM. --- ## `index.html` Change Add no-flash inline script in `` before any stylesheet: ```html ``` --- ## i18n Additions **`nl/translation.json`**: ```json { "nav": { "settings": "Instellingen", "profile": "Profiel", "openMenu": "Navigatie openen" }, "theme": { "switchToLight": "Overschakelen naar licht thema", "switchToDark": "Overschakelen naar donker thema" } } ``` **`en/translation.json`**: ```json { "nav": { "settings": "Settings", "profile": "Profile", "openMenu": "Open navigation" }, "theme": { "switchToLight": "Switch to light theme", "switchToDark": "Switch to dark theme" } } ``` --- ## Test IDs Summary | Element | `data-testid` | |---|---| | Sidebar | `app-sidebar` | | Mobile bar | `app-mobile-bar` | | Hamburger button | `mobile-menu-button` | | Sidebar overlay container | `sidebar-overlay` | | Sidebar backdrop | `sidebar-backdrop` | | Theme toggle | `theme-toggle` | | Nav: dashboard | `nav-dashboard` | | Nav: users | `nav-users` | | Nav: settings | `nav-settings` | | Nav: cms | `nav-cms` | | Nav: profile | `nav-profile` | | Main content area | `app-main` |