Implements the main navigation and layout structure: - Sidebar component with collapsible navigation and keyboard shortcut - AppHeader with project switcher and user menu - AppBreadcrumbs with auto-generation from pathname - ProjectSwitcher dropdown for quick project navigation - UserMenu with profile, settings, and logout - AppLayout component combining all layout elements Features: - Responsive design (mobile sidebar sheet, desktop sidebar) - Keyboard navigation (Cmd/Ctrl+B to toggle sidebar) - Dark mode support - WCAG AA accessible (ARIA labels, focus management) All 125 tests passing. Follows design system guidelines. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
/**
|
|
* Application Header Component
|
|
* Top header bar for the main application layout
|
|
* Includes logo, project switcher, and user menu
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import Image from 'next/image';
|
|
import { Link } from '@/lib/i18n/routing';
|
|
import { cn } from '@/lib/utils';
|
|
import { ThemeToggle } from '@/components/theme';
|
|
import { LocaleSwitcher } from '@/components/i18n';
|
|
import { ProjectSwitcher } from './ProjectSwitcher';
|
|
import { UserMenu } from './UserMenu';
|
|
|
|
interface Project {
|
|
id: string;
|
|
slug: string;
|
|
name: string;
|
|
}
|
|
|
|
interface AppHeaderProps {
|
|
/** Currently selected project */
|
|
currentProject?: Project;
|
|
/** List of available projects */
|
|
projects?: Project[];
|
|
/** Callback when project is changed */
|
|
onProjectChange?: (projectSlug: string) => void;
|
|
/** Additional className */
|
|
className?: string;
|
|
}
|
|
|
|
export function AppHeader({
|
|
currentProject,
|
|
projects = [],
|
|
onProjectChange,
|
|
className,
|
|
}: AppHeaderProps) {
|
|
return (
|
|
<header
|
|
className={cn(
|
|
'sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60',
|
|
className
|
|
)}
|
|
data-testid="app-header"
|
|
>
|
|
<div className="flex h-14 items-center px-4 lg:px-6">
|
|
{/* Left side - Logo and Project Switcher */}
|
|
<div className="flex items-center gap-4">
|
|
{/* Logo - visible on mobile, hidden on desktop when sidebar is visible */}
|
|
<Link
|
|
href="/"
|
|
className="flex items-center gap-2 lg:hidden"
|
|
aria-label="Syndarix home"
|
|
>
|
|
<Image
|
|
src="/logo-icon.svg"
|
|
alt=""
|
|
width={28}
|
|
height={28}
|
|
className="h-7 w-7"
|
|
aria-hidden="true"
|
|
/>
|
|
<span className="font-semibold text-foreground">Syndarix</span>
|
|
</Link>
|
|
|
|
{/* Project Switcher */}
|
|
{projects.length > 0 && (
|
|
<div className="hidden sm:block">
|
|
<ProjectSwitcher
|
|
currentProject={currentProject}
|
|
projects={projects}
|
|
onProjectChange={onProjectChange}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Right side - Actions */}
|
|
<div className="ml-auto flex items-center gap-2">
|
|
<ThemeToggle />
|
|
<LocaleSwitcher />
|
|
<UserMenu />
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mobile Project Switcher */}
|
|
{projects.length > 0 && (
|
|
<div className="border-t px-4 py-2 sm:hidden">
|
|
<ProjectSwitcher
|
|
currentProject={currentProject}
|
|
projects={projects}
|
|
onProjectChange={onProjectChange}
|
|
/>
|
|
</div>
|
|
)}
|
|
</header>
|
|
);
|
|
}
|