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:
@@ -0,0 +1,76 @@
|
||||
'use client';
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useSidebarStore } from '@/lib/stores/sidebar-store';
|
||||
import { ThemeToggle } from '@/components/theme-toggle';
|
||||
import { LocaleSwitcher } from '@/components/locale-switcher';
|
||||
|
||||
export function Header() {
|
||||
const t = useTranslations();
|
||||
const setMobileOpen = useSidebarStore((s) => s.setMobileOpen);
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-50 flex h-[var(--header-height)] items-center border-b border-border bg-background px-4 shadow-sm">
|
||||
{/* Left: Logo + Mobile hamburger */}
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => setMobileOpen(true)}
|
||||
className="inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-muted hover:text-foreground transition-colors md:hidden"
|
||||
aria-label={t('common.menu')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<line x1="3" y1="6" x2="21" y2="6" />
|
||||
<line x1="3" y1="12" x2="21" y2="12" />
|
||||
<line x1="3" y1="18" x2="21" y2="18" />
|
||||
</svg>
|
||||
</button>
|
||||
<span className="text-lg font-bold text-primary">
|
||||
{t('common.appName')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Center: Breadcrumb / page title */}
|
||||
<div className="flex-1 px-4 text-center">
|
||||
<span className="text-sm text-muted-foreground truncate">
|
||||
{t('header.breadcrumb.home')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Right: Actions */}
|
||||
<div className="flex items-center gap-2">
|
||||
<ThemeToggle />
|
||||
<LocaleSwitcher />
|
||||
{/* User avatar placeholder - wired to auth in Phase 2 */}
|
||||
<div
|
||||
className="h-8 w-8 rounded-full bg-muted flex items-center justify-center text-xs text-muted-foreground"
|
||||
aria-label={t('header.userMenu')}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="12" cy="7" r="4" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user