c9a664869c
- Replace custom invoice items table with responsive DataTable component - Fix server/client component error by creating InvoiceItemsTable client component - Merge danger zone with actions sidebar and use destructive button variant - Standardize button text sizing across all action buttons - Remove false claims from homepage (testimonials, ratings, fake user counts) - Focus homepage messaging on freelancers with honest feature descriptions - Fix dark mode support throughout app by replacing hard-coded colors with semantic classes - Remove aggressive red styling from settings, add subtle red accents only - Align import/export buttons and improve delete confirmation UX - Update dark mode background to have subtle green tint instead of pure black - Fix HTML nesting error in AlertDialog by using div instead of nested p tags This update makes the invoice view properly responsive, removes misleading marketing claims, and ensures consistent dark mode support across the entire application.
80 lines
2.1 KiB
TypeScript
80 lines
2.1 KiB
TypeScript
import React from "react";
|
|
|
|
interface PageHeaderProps {
|
|
title: string;
|
|
description?: string;
|
|
children?: React.ReactNode; // For action buttons or other header content
|
|
className?: string;
|
|
variant?: "default" | "gradient" | "large" | "large-gradient";
|
|
titleClassName?: string;
|
|
}
|
|
|
|
export function PageHeader({
|
|
title,
|
|
description,
|
|
children,
|
|
className = "",
|
|
variant = "default",
|
|
titleClassName,
|
|
}: PageHeaderProps) {
|
|
const getTitleClasses = () => {
|
|
const baseClasses = "font-bold";
|
|
|
|
switch (variant) {
|
|
case "gradient":
|
|
return `${baseClasses} text-3xl bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent`;
|
|
case "large":
|
|
return `${baseClasses} text-4xl text-foreground`;
|
|
case "large-gradient":
|
|
return `${baseClasses} text-4xl bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent`;
|
|
default:
|
|
return `${baseClasses} text-3xl text-foreground`;
|
|
}
|
|
};
|
|
|
|
const getDescriptionSpacing = () => {
|
|
return variant === "large" || variant === "large-gradient"
|
|
? "mt-2"
|
|
: "mt-1";
|
|
};
|
|
|
|
return (
|
|
<div className={`mb-8 ${className}`}>
|
|
<div className="flex items-start justify-between gap-4">
|
|
<h1 className={titleClassName ?? getTitleClasses()}>{title}</h1>
|
|
{children && (
|
|
<div className="flex flex-shrink-0 gap-2 sm:gap-3 [&>*]:h-8 [&>*]:px-2 [&>*]:text-sm sm:[&>*]:h-10 sm:[&>*]:px-4 sm:[&>*]:text-base [&>*>span]:hidden sm:[&>*>span]:inline [&>*>svg]:mr-0 sm:[&>*>svg]:mr-2">
|
|
{children}
|
|
</div>
|
|
)}
|
|
</div>
|
|
{description && (
|
|
<p
|
|
className={`text-muted-foreground ${getDescriptionSpacing()} text-lg`}
|
|
>
|
|
{description}
|
|
</p>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Convenience wrapper for dashboard page with larger gradient title
|
|
export function DashboardPageHeader({
|
|
title,
|
|
description,
|
|
children,
|
|
className = "",
|
|
}: Omit<PageHeaderProps, "variant">) {
|
|
return (
|
|
<PageHeader
|
|
title={title}
|
|
description={description}
|
|
variant="large-gradient"
|
|
className={className}
|
|
>
|
|
{children}
|
|
</PageHeader>
|
|
);
|
|
}
|