ca-grow-ops-manager/frontend/src/components/layout/PageHeader.tsx
fullsizemalt 47de301f77
Some checks are pending
Test / backend-test (push) Waiting to run
Test / frontend-test (push) Waiting to run
style: Dark/Light mode contrast audit
- Boost text contrast in both themes
- Strengthen border visibility (subtle borders now visible)
- Convert 39 files from hardcoded dark:/light: to CSS vars
- Tertiary text now more readable on both backgrounds
2025-12-27 12:12:10 -08:00

110 lines
3.5 KiB
TypeScript

import { ReactNode } from 'react';
import { Link } from 'react-router-dom';
import { ArrowLeft, type LucideIcon } from 'lucide-react';
import { Breadcrumbs } from '../ui/Breadcrumbs';
interface PageHeaderProps {
title: string;
description?: string;
icon?: LucideIcon;
iconColor?: string;
backLink?: string;
backLabel?: string;
actions?: ReactNode;
showBreadcrumbs?: boolean;
className?: string;
}
/**
* Consistent page header component with title, description, breadcrumbs, and actions
*/
export function PageHeader({
title,
description,
icon: Icon,
iconColor = 'text-[var(--color-primary)]',
backLink,
backLabel = 'Back',
actions,
showBreadcrumbs = true,
className = ''
}: PageHeaderProps) {
return (
<div className={`mb-6 ${className}`}>
{/* Breadcrumbs */}
{showBreadcrumbs && <Breadcrumbs />}
{/* Back Link */}
{backLink && (
<Link
to={backLink}
className="inline-flex items-center gap-1.5 text-sm text-[var(--color-text-tertiary)] hover:text-slate-700 dark:text-[var(--color-text-tertiary)] dark:hover:text-slate-200 mb-3 group"
>
<ArrowLeft size={16} className="group-hover:-translate-x-0.5 transition-transform" />
{backLabel}
</Link>
)}
{/* Title Row */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div className="flex items-center gap-3">
{Icon && (
<div className={`p-2 rounded-xl bg-slate-100 dark:bg-slate-800 ${iconColor}`}>
<Icon size={24} />
</div>
)}
<div>
<h1 className="text-xl sm:text-2xl font-bold text-[var(--color-text-primary)]">
{title}
</h1>
{description && (
<p className="text-sm text-[var(--color-text-tertiary)] mt-0.5">
{description}
</p>
)}
</div>
</div>
{/* Actions */}
{actions && (
<div className="flex items-center gap-2 flex-shrink-0">
{actions}
</div>
)}
</div>
</div>
);
}
/**
* Common action button styles for PageHeader
*/
export function PageHeaderButton({
children,
variant = 'primary',
onClick,
disabled,
className = ''
}: {
children: ReactNode;
variant?: 'primary' | 'secondary' | 'ghost';
onClick?: () => void;
disabled?: boolean;
className?: string;
}) {
const variants = {
primary: 'bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-white shadow-sm',
secondary: 'bg-[var(--color-bg-tertiary)] hover:bg-slate-200 dark:hover:bg-slate-600 text-[var(--color-text-primary)]',
ghost: 'hover:bg-slate-100 dark:hover:bg-slate-700 text-[var(--color-text-secondary)]'
};
return (
<button
onClick={onClick}
disabled={disabled}
className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors disabled:opacity-50 ${variants[variant]} ${className}`}
>
{children}
</button>
);
}