Files
syndarix/frontend/src/components/layout/AppLayout.tsx
Felipe Cardoso a4c91cb8c3 refactor(frontend): clean up code by consolidating multi-line JSX into single lines where feasible
- Refactored JSX elements to improve readability by collapsing multi-line props and attributes into single lines if their length permits.
- Improved consistency in component imports by grouping and consolidating them.
- No functional changes, purely restructuring for clarity and maintainability.
2026-01-01 11:46:57 +01:00

156 lines
3.9 KiB
TypeScript

/**
* Application Layout Component
* Main layout wrapper that combines sidebar, header, breadcrumbs, and content area
* Provides the standard application shell for authenticated pages
*/
'use client';
import { ReactNode } from 'react';
import { cn } from '@/lib/utils';
import { Sidebar } from './Sidebar';
import { AppHeader } from './AppHeader';
import { AppBreadcrumbs, type BreadcrumbItem } from './AppBreadcrumbs';
interface Project {
id: string;
slug: string;
name: string;
}
interface AppLayoutProps {
/** Page content */
children: ReactNode;
/** Current project (for project-specific pages) */
currentProject?: Project;
/** List of available projects */
projects?: Project[];
/** Callback when project is changed */
onProjectChange?: (projectSlug: string) => void;
/** Custom breadcrumb items (overrides auto-generation) */
breadcrumbs?: BreadcrumbItem[];
/** Hide breadcrumbs */
hideBreadcrumbs?: boolean;
/** Hide sidebar */
hideSidebar?: boolean;
/** Additional className for main content area */
className?: string;
/** Additional className for the outer wrapper */
wrapperClassName?: string;
}
export function AppLayout({
children,
currentProject,
projects = [],
onProjectChange,
breadcrumbs,
hideBreadcrumbs = false,
hideSidebar = false,
className,
wrapperClassName,
}: AppLayoutProps) {
return (
<div
className={cn('flex min-h-screen flex-col bg-background', wrapperClassName)}
data-testid="app-layout"
>
{/* Header */}
<AppHeader
currentProject={currentProject}
projects={projects}
onProjectChange={onProjectChange}
/>
{/* Main content area with sidebar */}
<div className="flex flex-1">
{/* Sidebar */}
{!hideSidebar && <Sidebar projectSlug={currentProject?.slug} />}
{/* Content area */}
<div className="flex flex-1 flex-col">
{/* Breadcrumbs */}
{!hideBreadcrumbs && <AppBreadcrumbs items={breadcrumbs} />}
{/* Main content */}
<main className={cn('flex-1', className)} id="main-content" tabIndex={-1}>
{children}
</main>
</div>
</div>
</div>
);
}
/**
* Page Container Component
* Standard container for page content with consistent padding and max-width
*/
interface PageContainerProps {
/** Page content */
children: ReactNode;
/** Maximum width constraint */
maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '4xl' | '6xl' | 'full';
/** Additional className */
className?: string;
}
const maxWidthClasses: Record<string, string> = {
sm: 'max-w-sm',
md: 'max-w-md',
lg: 'max-w-lg',
xl: 'max-w-xl',
'2xl': 'max-w-2xl',
'4xl': 'max-w-4xl',
'6xl': 'max-w-6xl',
full: 'max-w-full',
};
export function PageContainer({ children, maxWidth = '6xl', className }: PageContainerProps) {
return (
<div
className={cn(
'container mx-auto px-4 py-6 lg:px-6 lg:py-8',
maxWidthClasses[maxWidth],
className
)}
data-testid="page-container"
>
{children}
</div>
);
}
/**
* Page Header Component
* Consistent header for page titles and actions
*/
interface PageHeaderProps {
/** Page title */
title: string;
/** Optional description */
description?: string;
/** Action buttons or other content */
actions?: ReactNode;
/** Additional className */
className?: string;
}
export function PageHeader({ title, description, actions, className }: PageHeaderProps) {
return (
<div
className={cn(
'flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between',
className
)}
data-testid="page-header"
>
<div className="space-y-1">
<h1 className="text-2xl font-bold tracking-tight sm:text-3xl">{title}</h1>
{description && <p className="text-muted-foreground">{description}</p>}
</div>
{actions && <div className="flex items-center gap-2">{actions}</div>}
</div>
);
}