feat: improve invoice view responsiveness and settings UX

- 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.
This commit is contained in:
2025-07-15 02:35:55 -04:00
parent f331136090
commit c9a664869c
71 changed files with 2795 additions and 3043 deletions
+79
View File
@@ -0,0 +1,79 @@
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>
);
}