Files
slp-modular-cms/aidlc-docs/features/cms-frontend/construction/unit-3/functional-design/business-logic-model.md
T
SluijsensandClaude Haiku 4.5 31cc45d79b docs(unit-3): Functional design for Layout & Navigation
- Role-filtered sidebar (Owner/Admin/User visibility rules BR-U3-01–06)
- Topbar removed; UserMenu + LanguageSwitcher move to sidebar footer
- Mobile slide-over sidebar with hamburger button
- Theme toggle (light/dark) with localStorage persistence and no-flash init
- New components: MobileBar, SidebarOverlay, ThemeToggle, useTheme
- Unit 2 marked complete in aidlc-state

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-22 11:44:24 +02:00

1.8 KiB

Business Logic Model — Unit 3: Layout & Navigation

Component Hierarchy

AppLayout
├── Sidebar (desktop: always visible)
│   ├── Logo / AppName
│   ├── NavList (role-filtered NavItems)
│   │   └── NavItem (Link with activeProps)
│   └── SidebarFooter
│       ├── LanguageSwitcher
│       ├── ThemeToggle
│       └── UserMenu (name, email, logout)
├── MobileBar (mobile only — hamburger + app name)
│   └── opens → SidebarOverlay (Sidebar rendered in overlay)
└── <main>
    └── <Outlet />

Theme Initialisation (no flash)

Theme is applied in a blocking inline script in index.html — before React hydrates — to prevent a flash of the wrong theme:

<script>
  (function() {
    var stored = localStorage.getItem('cms-theme');
    var theme = stored || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
    document.documentElement.classList.add(theme);
  })();
</script>

React's useTheme hook reads from localStorage on mount and keeps the toggle in sync.

NavItem Filtering Logic

const visibleItems = NAV_ITEMS.filter(item =>
    !item.roles || item.roles.includes(user.role)
);

item.roles being undefined means "visible to everyone". This is evaluated at render time whenever user.role changes.

Mobile Sidebar State

Local useState<boolean> in AppLayout (or MobileBar). The sidebar overlay uses a <dialog> or a Tailwind-animated translate-x panel. Closes on:

  • Backdrop click (onBackdropClick)
  • Close button click
  • TanStack Router navigation (via useEffect watching location.pathname)

Logout Flow (unchanged from Unit 1/2)

UserMenu in sidebar footer calls useAuth().logout() then navigate({ to: '/login' }).