Files
pragma-stack/frontend/src/components/layout/AppHeader.tsx
Felipe Cardoso 6e645835dc feat(frontend): Implement navigation and layout (#44)
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>
2025-12-30 01:35:39 +01:00

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>
);
}