feat(01-02): portal layout with header, sidebar, theme toggle, locale switcher

- Zustand sidebar store with persist middleware (collapse/expand, mobile open)
- Sticky header with logo, breadcrumb, theme toggle, locale switcher, user avatar
- Collapsible sidebar with Dashboard/Marketplace nav, accordion categories, footer
- Mobile responsive: hamburger menu with overlay sidebar on small screens
- Theme toggle cycling light/dark/system with mounted guard
- Locale switcher setting NEXT_LOCALE cookie with router.refresh
- Empty dashboard state with grid icon, "Keine Widgets aktiv" text, add button
- AppShell composing header + sidebar + responsive main content area
- All user-visible strings via useTranslations (UI-03 compliance)
This commit is contained in:
2026-06-18 10:27:46 +02:00
parent f3791c6cdd
commit 6039387857
12 changed files with 577 additions and 9 deletions
@@ -0,0 +1,34 @@
'use client';
import { useEffect, useState } from 'react';
import { useSidebarStore } from '@/lib/stores/sidebar-store';
import { Header } from '@/components/layout/header';
import { Sidebar } from '@/components/layout/sidebar';
export function AppShell({ children }: { children: React.ReactNode }) {
const isCollapsed = useSidebarStore((s) => s.isCollapsed);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
// Desktop: sidebar pushes content via margin-left
// Mobile: sidebar overlays, no margin needed (handled by app-shell-main class in globals.css)
const sidebarWidth = isCollapsed
? 'var(--sidebar-width-collapsed)'
: 'var(--sidebar-width)';
return (
<div className="min-h-screen bg-background">
<Header />
<Sidebar />
<main
className="app-shell-main min-h-[calc(100vh-var(--header-height))] transition-all duration-200 p-6"
style={mounted ? { '--current-sidebar-width': sidebarWidth } as React.CSSProperties : undefined}
>
{children}
</main>
</div>
);
}