mirror of
https://github.com/soconnor0919/beenvoice.git
synced 2025-12-14 10:04:44 -05:00
Update invoice page layout and mobile styling
- Improve responsive layout and spacing for better mobile experience - Refactor invoice skeleton loader for consistent appearance - Revise padding and spacing for better content hierarchy - Add sticky header navigation and floating action bar - Update icon sizes and button styles for better touch targets
This commit is contained in:
@@ -4,46 +4,64 @@ import { Skeleton } from "~/components/ui/skeleton";
|
|||||||
|
|
||||||
export function InvoiceDetailsSkeleton() {
|
export function InvoiceDetailsSkeleton() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6 pb-24">
|
||||||
<div className="grid gap-6 xl:grid-cols-3">
|
{/* Header */}
|
||||||
{/* Main Content */}
|
<div className="flex items-center justify-between">
|
||||||
<div className="space-y-6 xl:col-span-2">
|
<div>
|
||||||
|
<Skeleton className="h-8 w-48 sm:h-9 sm:w-64" />
|
||||||
|
<Skeleton className="mt-1 h-4 w-40 sm:w-48" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Skeleton className="h-8 w-20 sm:h-9 sm:w-24" />
|
||||||
|
<Skeleton className="h-8 w-16 sm:h-9 sm:w-20" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||||||
|
{/* Left Column */}
|
||||||
|
<div className="space-y-6 lg:col-span-2">
|
||||||
{/* Invoice Header Skeleton */}
|
{/* Invoice Header Skeleton */}
|
||||||
<Card className="border-0 shadow-sm">
|
<Card className="shadow-sm">
|
||||||
<CardContent className="p-4 sm:p-6">
|
<CardContent className="p-4 sm:p-6">
|
||||||
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div className="space-y-4">
|
||||||
<div>
|
<div className="flex items-start justify-between gap-6">
|
||||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
|
<div className="min-w-0 flex-1 space-y-2">
|
||||||
<Skeleton className="h-6 w-48 sm:h-8" />
|
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
|
||||||
<Skeleton className="h-6 w-16" />
|
<Skeleton className="h-6 w-40 sm:h-8 sm:w-48" />
|
||||||
|
<Skeleton className="h-5 w-16 sm:h-6" />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1 sm:space-y-0">
|
||||||
|
<Skeleton className="h-3 w-32 sm:h-4 sm:w-40" />
|
||||||
|
<Skeleton className="h-3 w-28 sm:hidden sm:h-4 sm:w-36" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex-shrink-0 text-right">
|
||||||
|
<Skeleton className="h-3 w-20 sm:h-4" />
|
||||||
|
<Skeleton className="mt-1 h-6 w-24 sm:h-8 sm:w-28" />
|
||||||
</div>
|
</div>
|
||||||
<Skeleton className="mt-1 h-4 w-64" />
|
|
||||||
</div>
|
|
||||||
<div className="text-left sm:text-right">
|
|
||||||
<Skeleton className="h-4 w-20" />
|
|
||||||
<Skeleton className="mt-1 h-6 w-24 sm:h-8" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Client & Business Information Skeleton */}
|
{/* Client & Business Info */}
|
||||||
<div className="grid gap-4 sm:gap-6 lg:grid-cols-2">
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
{Array.from({ length: 2 }).map((_, i) => (
|
{Array.from({ length: 2 }).map((_, i) => (
|
||||||
<Card key={i} className="border-0 shadow-sm">
|
<Card key={i} className="shadow-sm">
|
||||||
<CardHeader className="pb-3">
|
<CardHeader className="pb-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Skeleton className="h-4 w-4 sm:h-5 sm:w-5" />
|
<Skeleton className="h-4 w-4 sm:h-5 sm:w-5" />
|
||||||
<Skeleton className="h-6 w-16" />
|
<Skeleton className="h-5 w-16 sm:h-6" />
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-3 sm:space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<Skeleton className="h-5 w-32 sm:h-6" />
|
<Skeleton className="h-5 w-32 sm:h-6" />
|
||||||
<div className="space-y-2 sm:space-y-3">
|
<div className="space-y-3">
|
||||||
{Array.from({ length: 3 }).map((_, j) => (
|
{Array.from({ length: 3 }).map((_, j) => (
|
||||||
<div key={j} className="flex items-center gap-2 sm:gap-3">
|
<div key={j} className="flex items-center gap-3">
|
||||||
<Skeleton className="h-6 w-6 rounded-lg sm:h-8 sm:w-8" />
|
<Skeleton className="h-8 w-8 rounded-lg" />
|
||||||
<Skeleton className="h-3 w-28 sm:h-4" />
|
<Skeleton className="h-4 w-28" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -53,130 +71,81 @@ export function InvoiceDetailsSkeleton() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Invoice Items Skeleton */}
|
{/* Invoice Items Skeleton */}
|
||||||
<Card className="border-0 shadow-sm">
|
<Card className="shadow-sm">
|
||||||
<CardHeader className="pb-3">
|
<CardHeader>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Skeleton className="h-4 w-4 sm:h-5 sm:w-5" />
|
<Skeleton className="h-4 w-4 sm:h-5 sm:w-5" />
|
||||||
<Skeleton className="h-5 w-28 sm:h-6" />
|
<Skeleton className="h-5 w-28 sm:h-6" />
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="space-y-4">
|
||||||
<div className="overflow-x-auto">
|
{Array.from({ length: 3 }).map((_, i) => (
|
||||||
<table className="w-full min-w-[500px]">
|
<div key={i} className="space-y-3 rounded-lg border p-4">
|
||||||
<thead>
|
<div className="flex items-start justify-between gap-4">
|
||||||
<tr className="border-b">
|
<div className="min-w-0 flex-1">
|
||||||
{["Date", "Description", "Hours", "Rate", "Amount"].map(
|
<Skeleton className="mb-2 h-4 w-full sm:h-5 sm:w-3/4" />
|
||||||
(header) => (
|
<div className="space-y-1 sm:space-y-0">
|
||||||
<th key={header} className="p-2 text-left sm:p-4">
|
<Skeleton className="h-3 w-20 sm:h-4 sm:w-24" />
|
||||||
<Skeleton className="h-3 w-16 sm:h-4" />
|
<Skeleton className="h-3 w-16 sm:hidden sm:h-4 sm:w-20" />
|
||||||
</th>
|
<Skeleton className="h-3 w-24 sm:hidden sm:h-4 sm:w-28" />
|
||||||
),
|
</div>
|
||||||
)}
|
</div>
|
||||||
</tr>
|
<div className="flex-shrink-0 text-right">
|
||||||
</thead>
|
<Skeleton className="h-4 w-16 sm:h-5 sm:w-20" />
|
||||||
<tbody>
|
</div>
|
||||||
{Array.from({ length: 3 }).map((_, i) => (
|
</div>
|
||||||
<tr key={i} className="border-b last:border-0">
|
</div>
|
||||||
<td className="p-2 sm:p-4">
|
))}
|
||||||
<Skeleton className="h-3 w-20 sm:h-4" />
|
|
||||||
</td>
|
|
||||||
<td className="p-2 sm:p-4">
|
|
||||||
<Skeleton className="h-3 w-48 sm:h-4" />
|
|
||||||
</td>
|
|
||||||
<td className="p-2 sm:p-4">
|
|
||||||
<Skeleton className="h-3 w-12 sm:h-4" />
|
|
||||||
</td>
|
|
||||||
<td className="p-2 sm:p-4">
|
|
||||||
<Skeleton className="h-3 w-16 sm:h-4" />
|
|
||||||
</td>
|
|
||||||
<td className="p-2 sm:p-4">
|
|
||||||
<Skeleton className="h-3 w-20 sm:h-4" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Totals Section Skeleton */}
|
{/* Totals */}
|
||||||
<div className="bg-muted/20 border-t p-3 sm:p-4">
|
<div className="bg-muted/30 rounded-lg p-4">
|
||||||
<div className="flex justify-end">
|
<div className="space-y-3">
|
||||||
<div className="w-full max-w-64 space-y-2">
|
<div className="flex justify-between">
|
||||||
<div className="flex justify-between">
|
<Skeleton className="h-4 w-16" />
|
||||||
<Skeleton className="h-3 w-16 sm:h-4" />
|
<Skeleton className="h-4 w-20" />
|
||||||
<Skeleton className="h-3 w-20 sm:h-4" />
|
</div>
|
||||||
</div>
|
<div className="flex justify-between">
|
||||||
<div className="flex justify-between">
|
<Skeleton className="h-4 w-20" />
|
||||||
<Skeleton className="h-3 w-20 sm:h-4" />
|
<Skeleton className="h-4 w-16" />
|
||||||
<Skeleton className="h-3 w-20 sm:h-4" />
|
</div>
|
||||||
</div>
|
<Separator />
|
||||||
<Separator />
|
<div className="flex justify-between">
|
||||||
<div className="flex justify-between">
|
<Skeleton className="h-5 w-12" />
|
||||||
<Skeleton className="h-4 w-12 sm:h-6" />
|
<Skeleton className="h-5 w-24" />
|
||||||
<Skeleton className="h-4 w-24 sm:h-6" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Notes Skeleton */}
|
{/* Notes */}
|
||||||
<Card className="border-0 shadow-sm">
|
<Card className="shadow-sm">
|
||||||
<CardHeader className="pb-3">
|
<CardHeader>
|
||||||
<Skeleton className="h-5 w-16 sm:h-6" />
|
<Skeleton className="h-6 w-16" />
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Skeleton className="h-3 w-full sm:h-4" />
|
<Skeleton className="h-4 w-full" />
|
||||||
<Skeleton className="h-3 w-3/4 sm:h-4" />
|
<Skeleton className="h-4 w-3/4" />
|
||||||
<Skeleton className="h-3 w-1/2 sm:h-4" />
|
<Skeleton className="h-4 w-1/2" />
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Sidebar Skeleton */}
|
{/* Right Column - Actions */}
|
||||||
<div className="space-y-4 sm:space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Actions Skeleton */}
|
<Card className="sticky top-6 shadow-sm">
|
||||||
<Card className="border-0 shadow-sm">
|
<CardHeader>
|
||||||
<CardHeader className="pb-3">
|
|
||||||
<Skeleton className="h-5 w-16 sm:h-6" />
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-2 sm:space-y-3">
|
|
||||||
{Array.from({ length: 4 }).map((_, i) => (
|
|
||||||
<Skeleton key={i} className="h-8 w-full sm:h-10" />
|
|
||||||
))}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Details Skeleton */}
|
|
||||||
<Card className="border-0 shadow-sm">
|
|
||||||
<CardHeader className="pb-3">
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Skeleton className="h-4 w-4 sm:h-5 sm:w-5" />
|
<Skeleton className="h-5 w-5" />
|
||||||
<Skeleton className="h-5 w-16 sm:h-6" />
|
<Skeleton className="h-6 w-16" />
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-2 sm:space-y-3">
|
<CardContent className="space-y-3">
|
||||||
<div className="grid grid-cols-2 gap-2 sm:gap-3">
|
{Array.from({ length: 3 }).map((_, i) => (
|
||||||
{Array.from({ length: 6 }).map((_, i) => (
|
<Skeleton key={i} className="h-10 w-full" />
|
||||||
<div key={i} className="space-y-1">
|
))}
|
||||||
<Skeleton className="h-3 w-16 sm:h-4" />
|
|
||||||
<Skeleton className="h-3 w-20 sm:h-4" />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Danger Zone Skeleton */}
|
|
||||||
<Card className="border-red-200 shadow-sm dark:border-red-800">
|
|
||||||
<CardHeader className="pb-3">
|
|
||||||
<Skeleton className="h-5 w-24 sm:h-6" />
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<Skeleton className="h-8 w-full sm:h-10" />
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -57,13 +57,12 @@ export function PDFDownloadButton({
|
|||||||
onClick={handleDownloadPDF}
|
onClick={handleDownloadPDF}
|
||||||
disabled={isGenerating}
|
disabled={isGenerating}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
|
||||||
className={className}
|
className={className}
|
||||||
>
|
>
|
||||||
{isGenerating ? (
|
{isGenerating ? (
|
||||||
<Loader2 className="h-3 w-3 animate-spin sm:h-4 sm:w-4" />
|
<Loader2 className="h-5 w-5 animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
<Download className="h-3 w-3 sm:h-4 sm:w-4" />
|
<Download className="h-5 w-5" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
@@ -74,17 +73,16 @@ export function PDFDownloadButton({
|
|||||||
onClick={handleDownloadPDF}
|
onClick={handleDownloadPDF}
|
||||||
disabled={isGenerating}
|
disabled={isGenerating}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
size="default"
|
className={`shadow-sm ${className ?? ""}`}
|
||||||
className={`w-full shadow-sm ${className}`}
|
|
||||||
>
|
>
|
||||||
{isGenerating ? (
|
{isGenerating ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
<Loader2 className="h-5 w-5 animate-spin" />
|
||||||
<span>Generating PDF...</span>
|
<span>Generating PDF...</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Download className="mr-2 h-4 w-4" />
|
<Download className="h-5 w-5" />
|
||||||
<span>Download PDF</span>
|
<span>Download PDF</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -10,12 +10,9 @@ import { PageHeader } from "~/components/layout/page-header";
|
|||||||
import { PDFDownloadButton } from "./_components/pdf-download-button";
|
import { PDFDownloadButton } from "./_components/pdf-download-button";
|
||||||
import { SendInvoiceButton } from "./_components/send-invoice-button";
|
import { SendInvoiceButton } from "./_components/send-invoice-button";
|
||||||
import { InvoiceDetailsSkeleton } from "./_components/invoice-details-skeleton";
|
import { InvoiceDetailsSkeleton } from "./_components/invoice-details-skeleton";
|
||||||
import { InvoiceActionsDropdown } from "./_components/invoice-actions-dropdown";
|
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
|
||||||
Building,
|
Building,
|
||||||
Calendar,
|
|
||||||
Copy,
|
|
||||||
Edit,
|
Edit,
|
||||||
FileText,
|
FileText,
|
||||||
Mail,
|
Mail,
|
||||||
@@ -23,12 +20,6 @@ import {
|
|||||||
Phone,
|
Phone,
|
||||||
User,
|
User,
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
Trash2,
|
|
||||||
DollarSign,
|
|
||||||
Clock,
|
|
||||||
Eye,
|
|
||||||
Download,
|
|
||||||
Send,
|
|
||||||
Check,
|
Check,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
@@ -75,20 +66,19 @@ async function InvoiceContent({ invoiceId }: { invoiceId: string }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 pb-24">
|
<div className="space-y-6 pb-24">
|
||||||
{/* Header */}
|
<PageHeader
|
||||||
<div className="flex items-center justify-between">
|
title="Invoice Details"
|
||||||
<div>
|
description="View and manage invoice information"
|
||||||
<h1 className="text-foreground text-3xl font-bold">
|
variant="gradient"
|
||||||
Invoice Details
|
>
|
||||||
</h1>
|
<PDFDownloadButton invoiceId={invoice.id} variant="outline" />
|
||||||
<p className="text-muted-foreground mt-1">
|
<Button asChild variant="default">
|
||||||
View and manage invoice information
|
<Link href={`/dashboard/invoices/${invoice.id}/edit`}>
|
||||||
</p>
|
<Edit className="h-5 w-5" />
|
||||||
</div>
|
<span>Edit</span>
|
||||||
<div className="flex items-center gap-2">
|
</Link>
|
||||||
<InvoiceActionsDropdown invoiceId={invoice.id} />
|
</Button>
|
||||||
</div>
|
</PageHeader>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||||||
@@ -96,7 +86,7 @@ async function InvoiceContent({ invoiceId }: { invoiceId: string }) {
|
|||||||
<div className="space-y-6 lg:col-span-2">
|
<div className="space-y-6 lg:col-span-2">
|
||||||
{/* Invoice Header */}
|
{/* Invoice Header */}
|
||||||
<Card className="shadow-sm">
|
<Card className="shadow-sm">
|
||||||
<CardContent className="p-6">
|
<CardContent className="p-4 sm:p-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-start justify-between gap-6">
|
<div className="flex items-start justify-between gap-6">
|
||||||
<div className="min-w-0 flex-1 space-y-2">
|
<div className="min-w-0 flex-1 space-y-2">
|
||||||
@@ -276,7 +266,7 @@ async function InvoiceContent({ invoiceId }: { invoiceId: string }) {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
{invoice.items.map((item, index) => (
|
{invoice.items.map((item) => (
|
||||||
<div key={item.id} className="space-y-3 rounded-lg border p-4">
|
<div key={item.id} className="space-y-3 rounded-lg border p-4">
|
||||||
<div className="flex items-start justify-between gap-4">
|
<div className="flex items-start justify-between gap-4">
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
|
|||||||
193
src/app/page.tsx
193
src/app/page.tsx
@@ -24,31 +24,41 @@ export default function HomePage() {
|
|||||||
<AuthRedirect />
|
<AuthRedirect />
|
||||||
|
|
||||||
{/* Navigation */}
|
{/* Navigation */}
|
||||||
<nav className="sticky top-0 z-50 border-b bg-white/80 backdrop-blur-xl dark:bg-slate-900/80 dark:border-slate-700">
|
<nav className="sticky top-0 z-50 border-b bg-white/80 backdrop-blur-xl dark:border-slate-700 dark:bg-slate-900/80">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="flex h-16 items-center justify-between">
|
<div className="flex h-14 items-center justify-between sm:h-16">
|
||||||
<Logo />
|
<Logo />
|
||||||
<div className="hidden items-center space-x-8 md:flex">
|
<div className="hidden items-center space-x-6 md:flex">
|
||||||
<a
|
<a
|
||||||
href="#features"
|
href="#features"
|
||||||
className="text-slate-600 hover:text-slate-900 dark:text-slate-300 dark:hover:text-slate-100 transition-colors"
|
className="text-slate-600 transition-colors hover:text-slate-900 dark:text-slate-300 dark:hover:text-slate-100"
|
||||||
>
|
>
|
||||||
Features
|
Features
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="#pricing"
|
href="#pricing"
|
||||||
className="text-slate-600 hover:text-slate-900 dark:text-slate-300 dark:hover:text-slate-100 transition-colors"
|
className="text-slate-600 transition-colors hover:text-slate-900 dark:text-slate-300 dark:hover:text-slate-100"
|
||||||
>
|
>
|
||||||
Pricing
|
Pricing
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-2">
|
||||||
<Link href="/auth/signin">
|
<Link href="/auth/signin">
|
||||||
<Button variant="ghost">Sign In</Button>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="hidden sm:inline-flex"
|
||||||
|
>
|
||||||
|
Sign In
|
||||||
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/auth/register">
|
<Link href="/auth/register">
|
||||||
<Button className="bg-gradient-to-r from-emerald-600 to-teal-600 shadow-lg shadow-emerald-500/25 transition-all duration-300 hover:shadow-xl hover:shadow-emerald-500/30">
|
<Button
|
||||||
Get Started Free
|
size="sm"
|
||||||
|
className="bg-gradient-to-r from-emerald-600 to-teal-600 shadow-lg shadow-emerald-500/25 transition-all duration-300 hover:shadow-xl hover:shadow-emerald-500/30"
|
||||||
|
>
|
||||||
|
<span className="hidden sm:inline">Get Started Free</span>
|
||||||
|
<span className="sm:hidden">Start Free</span>
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -57,31 +67,31 @@ export default function HomePage() {
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
<section className="relative overflow-hidden bg-gradient-to-br from-blue-50 via-emerald-50 to-teal-50 dark:from-slate-800 dark:via-emerald-900/20 dark:to-teal-900/20 pt-20 pb-16">
|
<section className="relative overflow-hidden bg-gradient-to-br from-blue-50 via-emerald-50 to-teal-50 px-4 pt-12 pb-16 sm:pt-20 dark:from-slate-800 dark:via-emerald-900/20 dark:to-teal-900/20">
|
||||||
{/* Background decoration */}
|
{/* Background decoration */}
|
||||||
<div className="absolute inset-0">
|
<div className="absolute inset-0">
|
||||||
<div className="absolute top-0 left-1/4 h-96 w-96 rounded-full bg-gradient-to-r from-emerald-400/20 to-blue-400/20 dark:from-emerald-500/10 dark:to-blue-500/10 blur-3xl"></div>
|
<div className="absolute top-0 left-1/4 h-96 w-96 rounded-full bg-gradient-to-r from-emerald-400/20 to-blue-400/20 blur-3xl dark:from-emerald-500/10 dark:to-blue-500/10"></div>
|
||||||
<div className="absolute top-32 right-1/4 h-80 w-80 rounded-full bg-gradient-to-r from-teal-400/20 to-emerald-400/20 dark:from-teal-500/10 dark:to-emerald-500/10 blur-3xl"></div>
|
<div className="absolute top-32 right-1/4 h-80 w-80 rounded-full bg-gradient-to-r from-teal-400/20 to-emerald-400/20 blur-3xl dark:from-teal-500/10 dark:to-emerald-500/10"></div>
|
||||||
<div className="absolute bottom-0 left-1/2 h-64 w-64 rounded-full bg-gradient-to-r from-blue-400/20 to-purple-400/20 dark:from-blue-500/10 dark:to-purple-500/10 blur-3xl"></div>
|
<div className="absolute bottom-0 left-1/2 h-64 w-64 rounded-full bg-gradient-to-r from-blue-400/20 to-purple-400/20 blur-3xl dark:from-blue-500/10 dark:to-purple-500/10"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative container mx-auto px-4 text-center">
|
<div className="relative container mx-auto text-center">
|
||||||
<div className="mx-auto max-w-4xl">
|
<div className="mx-auto max-w-4xl">
|
||||||
<Badge
|
<Badge
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="mb-6 border-emerald-200 bg-emerald-100 text-emerald-800 dark:border-emerald-800 dark:bg-emerald-900 dark:text-emerald-200"
|
className="mb-4 border-emerald-200 bg-emerald-100 text-emerald-800 sm:mb-6 dark:border-emerald-800 dark:bg-emerald-900 dark:text-emerald-200"
|
||||||
>
|
>
|
||||||
<Sparkles className="mr-1 h-3 w-3" />
|
<Sparkles className="mr-1 h-3 w-3" />
|
||||||
100% Free Forever
|
100% Free Forever
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
||||||
<h1 className="mb-6 text-6xl font-bold tracking-tight text-slate-900 dark:text-slate-100 sm:text-7xl lg:text-8xl">
|
<h1 className="mb-4 text-4xl font-bold tracking-tight text-slate-900 sm:mb-6 sm:text-6xl lg:text-7xl dark:text-slate-100">
|
||||||
Simple Invoicing for
|
Simple Invoicing for
|
||||||
<span className="block bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent">
|
<span className="block bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent">
|
||||||
Freelancers
|
Freelancers
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p className="mx-auto mb-8 max-w-2xl text-xl leading-relaxed text-slate-600">
|
<p className="mx-auto mb-6 max-w-2xl text-lg leading-relaxed text-slate-600 sm:mb-8 sm:text-xl dark:text-slate-300">
|
||||||
Create professional invoices, manage clients, and track payments.
|
Create professional invoices, manage clients, and track payments.
|
||||||
Built specifically for freelancers and small businesses—
|
Built specifically for freelancers and small businesses—
|
||||||
<span className="bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text font-semibold text-transparent">
|
<span className="bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text font-semibold text-transparent">
|
||||||
@@ -90,29 +100,29 @@ export default function HomePage() {
|
|||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex flex-col items-center gap-4 sm:flex-row sm:justify-center">
|
<div className="flex flex-col items-center gap-3 sm:flex-row sm:justify-center sm:gap-4">
|
||||||
<Link href="/auth/register">
|
<Link href="/auth/register">
|
||||||
<Button
|
<Button
|
||||||
size="lg"
|
size="lg"
|
||||||
className="group bg-gradient-to-r from-emerald-500 to-teal-500 px-8 py-4 text-lg font-semibold shadow-xl shadow-emerald-500/30 transition-all duration-300 hover:from-emerald-600 hover:to-teal-600 hover:shadow-2xl hover:shadow-emerald-500/40"
|
className="group w-full bg-gradient-to-r from-emerald-500 to-teal-500 px-6 py-3 text-base font-semibold shadow-xl shadow-emerald-500/30 transition-all duration-300 hover:from-emerald-600 hover:to-teal-600 hover:shadow-2xl hover:shadow-emerald-500/40 sm:w-auto sm:px-8 sm:py-4 sm:text-lg"
|
||||||
>
|
>
|
||||||
Start Free
|
Start Free
|
||||||
<ArrowRight className="ml-2 h-5 w-5 transition-transform group-hover:translate-x-1" />
|
<ArrowRight className="ml-2 h-4 w-4 transition-transform group-hover:translate-x-1 sm:h-5 sm:w-5" />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="#demo">
|
<Link href="#features">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="lg"
|
size="lg"
|
||||||
className="group border-emerald-200 bg-white/50 px-8 py-4 text-lg text-emerald-700 backdrop-blur-sm hover:border-emerald-300 hover:bg-emerald-50"
|
className="group w-full border-emerald-200 bg-white/50 px-6 py-3 text-base text-emerald-700 backdrop-blur-sm hover:border-emerald-300 hover:bg-emerald-50 sm:w-auto sm:px-8 sm:py-4 sm:text-lg dark:border-emerald-700 dark:bg-emerald-900/20 dark:text-emerald-300 dark:hover:bg-emerald-900/40"
|
||||||
>
|
>
|
||||||
See Features
|
See Features
|
||||||
<ChevronRight className="ml-2 h-5 w-5 transition-transform group-hover:translate-x-1" />
|
<ChevronRight className="ml-2 h-4 w-4 transition-transform group-hover:translate-x-1 sm:h-5 sm:w-5" />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-12 flex items-center justify-center gap-8 text-sm text-slate-600">
|
<div className="mt-8 flex flex-col items-center justify-center gap-2 text-sm text-slate-600 sm:mt-12 sm:flex-row sm:gap-6 dark:text-slate-400">
|
||||||
{[
|
{[
|
||||||
"No credit card required",
|
"No credit card required",
|
||||||
"Setup in 2 minutes",
|
"Setup in 2 minutes",
|
||||||
@@ -120,7 +130,7 @@ export default function HomePage() {
|
|||||||
].map((text, i) => (
|
].map((text, i) => (
|
||||||
<div key={i} className="flex items-center gap-2">
|
<div key={i} className="flex items-center gap-2">
|
||||||
<Check className="h-4 w-4 text-emerald-500" />
|
<Check className="h-4 w-4 text-emerald-500" />
|
||||||
{text}
|
<span className="text-center">{text}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -128,58 +138,47 @@ export default function HomePage() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Stats */}
|
|
||||||
<section className="border-y bg-gradient-to-r from-emerald-50 to-teal-50 py-12">
|
|
||||||
<div className="container mx-auto px-4">
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="text-slate-600">
|
|
||||||
Free invoicing for independent professionals
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* Features Section */}
|
{/* Features Section */}
|
||||||
<section
|
<section
|
||||||
id="features"
|
id="features"
|
||||||
className="bg-gradient-to-br from-white via-blue-50/30 to-emerald-50/50 py-24"
|
className="bg-gradient-to-br from-white via-blue-50/30 to-emerald-50/50 py-16 sm:py-24 dark:from-slate-900 dark:via-slate-800/50 dark:to-emerald-900/20"
|
||||||
>
|
>
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="mb-16 text-center">
|
<div className="mb-12 text-center sm:mb-16">
|
||||||
<Badge
|
<Badge
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="mb-4 border-blue-200 bg-blue-100 text-blue-800"
|
className="mb-4 border-blue-200 bg-blue-100 text-blue-800 dark:border-blue-800 dark:bg-blue-900 dark:text-blue-200"
|
||||||
>
|
>
|
||||||
<Zap className="mr-1 h-3 w-3" />
|
<Zap className="mr-1 h-3 w-3" />
|
||||||
Supercharged Features
|
Supercharged Features
|
||||||
</Badge>
|
</Badge>
|
||||||
<h2 className="mb-4 text-5xl font-bold tracking-tight text-slate-900">
|
<h2 className="mb-4 text-3xl font-bold tracking-tight text-slate-900 sm:text-4xl lg:text-5xl dark:text-slate-100">
|
||||||
Everything you need to
|
Everything you need to
|
||||||
<span className="block bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent">
|
<span className="block bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent">
|
||||||
invoice professionally
|
invoice professionally
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mx-auto max-w-2xl text-xl text-slate-600">
|
<p className="mx-auto max-w-2xl text-lg text-slate-600 sm:text-xl dark:text-slate-300">
|
||||||
Simple, powerful features designed specifically for freelancers
|
Simple, powerful features designed specifically for freelancers
|
||||||
and small businesses.
|
and small businesses.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-8 lg:grid-cols-3">
|
<div className="grid gap-6 sm:gap-8 md:grid-cols-2 lg:grid-cols-3">
|
||||||
{/* Feature 1 */}
|
{/* Feature 1 */}
|
||||||
<Card className="group bg-white/70 shadow-lg backdrop-blur-sm transition-all duration-300 hover:bg-white/90 hover:shadow-xl">
|
<Card className="group bg-white/70 shadow-lg backdrop-blur-sm transition-all duration-300 hover:bg-white/90 hover:shadow-xl dark:bg-slate-800/50 dark:hover:bg-slate-800/70">
|
||||||
<CardContent className="p-8">
|
<CardContent className="p-6 sm:p-8">
|
||||||
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-emerald-500 to-teal-500 text-white shadow-lg">
|
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-emerald-500 to-teal-500 text-white shadow-lg">
|
||||||
<Rocket className="h-6 w-6" />
|
<Rocket className="h-6 w-6" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="mb-3 text-xl font-bold text-slate-900">
|
<h3 className="mb-3 text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||||
Quick Setup
|
Quick Setup
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mb-4 text-slate-600">
|
<p className="mb-4 text-slate-600 dark:text-slate-300">
|
||||||
Start creating invoices immediately. No complicated setup or
|
Start creating invoices immediately. No complicated setup or
|
||||||
configuration required.
|
configuration required.
|
||||||
</p>
|
</p>
|
||||||
<ul className="space-y-2 text-sm text-slate-600">
|
<ul className="space-y-2 text-sm text-slate-600 dark:text-slate-400">
|
||||||
<li className="flex items-center gap-2">
|
<li className="flex items-center gap-2">
|
||||||
<Check className="h-4 w-4 text-emerald-500" />
|
<Check className="h-4 w-4 text-emerald-500" />
|
||||||
Simple client management
|
Simple client management
|
||||||
@@ -197,19 +196,19 @@ export default function HomePage() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Feature 2 */}
|
{/* Feature 2 */}
|
||||||
<Card className="group bg-white/70 shadow-lg backdrop-blur-sm transition-all duration-300 hover:bg-white/90 hover:shadow-xl">
|
<Card className="group bg-white/70 shadow-lg backdrop-blur-sm transition-all duration-300 hover:bg-white/90 hover:shadow-xl dark:bg-slate-800/50 dark:hover:bg-slate-800/70">
|
||||||
<CardContent className="p-8">
|
<CardContent className="p-6 sm:p-8">
|
||||||
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-blue-500 to-indigo-500 text-white shadow-lg">
|
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-blue-500 to-indigo-500 text-white shadow-lg">
|
||||||
<BarChart3 className="h-6 w-6" />
|
<BarChart3 className="h-6 w-6" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="mb-3 text-xl font-bold text-slate-900">
|
<h3 className="mb-3 text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||||
Payment Tracking
|
Payment Tracking
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mb-4 text-slate-600">
|
<p className="mb-4 text-slate-600 dark:text-slate-300">
|
||||||
Keep track of invoice status and monitor which clients have
|
Keep track of invoice status and monitor which clients have
|
||||||
paid.
|
paid.
|
||||||
</p>
|
</p>
|
||||||
<ul className="space-y-2 text-sm text-slate-600">
|
<ul className="space-y-2 text-sm text-slate-600 dark:text-slate-400">
|
||||||
<li className="flex items-center gap-2">
|
<li className="flex items-center gap-2">
|
||||||
<Check className="h-4 w-4 text-emerald-500" />
|
<Check className="h-4 w-4 text-emerald-500" />
|
||||||
Invoice status tracking
|
Invoice status tracking
|
||||||
@@ -227,15 +226,15 @@ export default function HomePage() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Feature 3 */}
|
{/* Feature 3 */}
|
||||||
<Card className="group bg-white/70 shadow-lg backdrop-blur-sm transition-all duration-300 hover:bg-white/90 hover:shadow-xl">
|
<Card className="group bg-white/70 shadow-lg backdrop-blur-sm transition-all duration-300 hover:bg-white/90 hover:shadow-xl dark:bg-slate-800/50 dark:hover:bg-slate-800/70">
|
||||||
<CardContent className="p-8">
|
<CardContent className="p-6 sm:p-8">
|
||||||
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-purple-500 to-pink-500 text-white shadow-lg">
|
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-purple-500 to-pink-500 text-white shadow-lg">
|
||||||
<Globe className="h-6 w-6" />
|
<Globe className="h-6 w-6" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="mb-3 text-xl font-bold text-slate-900">
|
<h3 className="mb-3 text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||||
Professional Features
|
Professional Features
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mb-4 text-slate-600">
|
<p className="mb-4 text-slate-600 dark:text-slate-300">
|
||||||
Everything you need to look professional and get paid on time.
|
Everything you need to look professional and get paid on time.
|
||||||
</p>
|
</p>
|
||||||
<ul className="space-y-2 text-sm text-slate-600">
|
<ul className="space-y-2 text-sm text-slate-600">
|
||||||
@@ -261,35 +260,37 @@ export default function HomePage() {
|
|||||||
{/* Pricing Section */}
|
{/* Pricing Section */}
|
||||||
<section
|
<section
|
||||||
id="pricing"
|
id="pricing"
|
||||||
className="bg-gradient-to-br from-emerald-50 via-teal-50 to-blue-50 py-24"
|
className="bg-gradient-to-br from-emerald-50 via-teal-50 to-blue-50 py-16 sm:py-24 dark:from-emerald-900/20 dark:via-teal-900/20 dark:to-blue-900/20"
|
||||||
>
|
>
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="mb-16 text-center">
|
<div className="mb-12 text-center sm:mb-16">
|
||||||
<h2 className="mb-4 text-5xl font-bold tracking-tight text-slate-900">
|
<h2 className="mb-4 text-3xl font-bold tracking-tight text-slate-900 sm:text-4xl lg:text-5xl dark:text-slate-100">
|
||||||
Simple, transparent pricing
|
Simple, transparent pricing
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mx-auto max-w-2xl text-xl text-slate-600">
|
<p className="mx-auto max-w-2xl text-lg text-slate-600 sm:text-xl dark:text-slate-300">
|
||||||
Start free, stay free. No hidden fees, no gotchas, no limits on
|
Start free, stay free. No hidden fees, no gotchas, no limits on
|
||||||
your success.
|
your success.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx-auto max-w-md">
|
<div className="mx-auto max-w-md">
|
||||||
<Card className="relative border-2 border-emerald-500 bg-white/90 shadow-2xl backdrop-blur-sm">
|
<Card className="relative border-2 border-emerald-500 bg-white/90 shadow-2xl backdrop-blur-sm dark:border-emerald-400 dark:bg-slate-800/90">
|
||||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2">
|
<div className="absolute -top-4 left-1/2 -translate-x-1/2">
|
||||||
<Badge className="bg-emerald-500 px-6 py-1 text-white">
|
<Badge className="bg-emerald-500 px-6 py-1 text-white dark:bg-emerald-600">
|
||||||
Forever Free
|
Forever Free
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<CardContent className="p-8 text-center">
|
<CardContent className="p-6 text-center sm:p-8">
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="mb-2 bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-6xl font-bold text-transparent">
|
<div className="mb-2 bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-5xl font-bold text-transparent sm:text-6xl">
|
||||||
$0
|
$0
|
||||||
</div>
|
</div>
|
||||||
<div className="text-slate-600">per month, forever</div>
|
<div className="text-slate-600 dark:text-slate-400">
|
||||||
|
per month, forever
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-8 space-y-4 text-left">
|
<div className="mb-6 space-y-3 text-left sm:mb-8 sm:space-y-4">
|
||||||
{[
|
{[
|
||||||
"Unlimited invoices",
|
"Unlimited invoices",
|
||||||
"Unlimited clients",
|
"Unlimited clients",
|
||||||
@@ -302,18 +303,20 @@ export default function HomePage() {
|
|||||||
].map((feature, i) => (
|
].map((feature, i) => (
|
||||||
<div key={i} className="flex items-center gap-3">
|
<div key={i} className="flex items-center gap-3">
|
||||||
<Check className="h-5 w-5 flex-shrink-0 text-emerald-500" />
|
<Check className="h-5 w-5 flex-shrink-0 text-emerald-500" />
|
||||||
<span className="text-slate-700">{feature}</span>
|
<span className="text-slate-700 dark:text-slate-300">
|
||||||
|
{feature}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Link href="/auth/register">
|
<Link href="/auth/register">
|
||||||
<Button className="w-full bg-gradient-to-r from-emerald-500 to-teal-500 py-3 text-lg font-semibold shadow-lg shadow-emerald-500/30 transition-all duration-300 hover:from-emerald-600 hover:to-teal-600 hover:shadow-xl hover:shadow-emerald-500/40">
|
<Button className="w-full bg-gradient-to-r from-emerald-500 to-teal-500 py-3 text-base font-semibold shadow-lg shadow-emerald-500/30 transition-all duration-300 hover:from-emerald-600 hover:to-teal-600 hover:shadow-xl hover:shadow-emerald-500/40 sm:text-lg">
|
||||||
Get Started Now
|
Get Started Now
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<p className="mt-4 text-sm text-slate-600">
|
<p className="mt-4 text-sm text-slate-600 dark:text-slate-400">
|
||||||
No credit card required
|
No credit card required
|
||||||
</p>
|
</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -323,10 +326,10 @@ export default function HomePage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Why Choose */}
|
{/* Why Choose */}
|
||||||
<section className="bg-gradient-to-br from-white via-emerald-50/30 to-teal-50/50 py-24">
|
<section className="bg-gradient-to-br from-white via-emerald-50/30 to-teal-50/50 py-16 sm:py-24 dark:from-slate-900 dark:via-emerald-900/10 dark:to-teal-900/10">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="mb-16 text-center">
|
<div className="mb-12 text-center sm:mb-16">
|
||||||
<h2 className="mb-4 text-5xl font-bold tracking-tight text-slate-900">
|
<h2 className="mb-4 text-3xl font-bold tracking-tight text-slate-900 sm:text-4xl lg:text-5xl dark:text-slate-100">
|
||||||
Why freelancers
|
Why freelancers
|
||||||
<span className="block bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent">
|
<span className="block bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent">
|
||||||
choose BeenVoice
|
choose BeenVoice
|
||||||
@@ -334,15 +337,15 @@ export default function HomePage() {
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-8 md:grid-cols-3">
|
<div className="grid gap-6 sm:gap-8 md:grid-cols-3">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-emerald-500 to-teal-500 text-white shadow-lg">
|
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-emerald-500 to-teal-500 text-white shadow-lg">
|
||||||
<Zap className="h-6 w-6" />
|
<Zap className="h-6 w-6" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="mb-3 text-xl font-bold text-slate-900">
|
<h3 className="mb-3 text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||||
Quick & Simple
|
Quick & Simple
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-slate-600">
|
<p className="text-slate-600 dark:text-slate-300">
|
||||||
No learning curve. Start creating professional invoices in
|
No learning curve. Start creating professional invoices in
|
||||||
minutes, not hours.
|
minutes, not hours.
|
||||||
</p>
|
</p>
|
||||||
@@ -351,10 +354,10 @@ export default function HomePage() {
|
|||||||
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-blue-500 to-indigo-500 text-white shadow-lg">
|
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-blue-500 to-indigo-500 text-white shadow-lg">
|
||||||
<Shield className="h-6 w-6" />
|
<Shield className="h-6 w-6" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="mb-3 text-xl font-bold text-slate-900">
|
<h3 className="mb-3 text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||||
Always Free
|
Always Free
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-slate-600">
|
<p className="text-slate-600 dark:text-slate-300">
|
||||||
No hidden fees, no premium tiers. All features are free for as
|
No hidden fees, no premium tiers. All features are free for as
|
||||||
long as you need them.
|
long as you need them.
|
||||||
</p>
|
</p>
|
||||||
@@ -363,10 +366,10 @@ export default function HomePage() {
|
|||||||
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-purple-500 to-pink-500 text-white shadow-lg">
|
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-r from-purple-500 to-pink-500 text-white shadow-lg">
|
||||||
<Clock className="h-6 w-6" />
|
<Clock className="h-6 w-6" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="mb-3 text-xl font-bold text-slate-900">
|
<h3 className="mb-3 text-xl font-bold text-slate-900 dark:text-slate-100">
|
||||||
Save Time
|
Save Time
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-slate-600">
|
<p className="text-slate-600 dark:text-slate-300">
|
||||||
Focus on your work, not paperwork. Automated calculations and
|
Focus on your work, not paperwork. Automated calculations and
|
||||||
professional formatting.
|
professional formatting.
|
||||||
</p>
|
</p>
|
||||||
@@ -376,7 +379,7 @@ export default function HomePage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* CTA Section */}
|
{/* CTA Section */}
|
||||||
<section className="relative overflow-hidden bg-gradient-to-br from-emerald-500 via-teal-600 to-blue-700 py-24">
|
<section className="relative overflow-hidden bg-gradient-to-br from-emerald-500 via-teal-600 to-blue-700 py-16 sm:py-24">
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-emerald-500/95 via-teal-600/95 to-blue-700/95"></div>
|
<div className="absolute inset-0 bg-gradient-to-br from-emerald-500/95 via-teal-600/95 to-blue-700/95"></div>
|
||||||
<div className="absolute top-10 left-10 h-64 w-64 rounded-full bg-gradient-to-r from-white/20 to-emerald-300/20 blur-3xl"></div>
|
<div className="absolute top-10 left-10 h-64 w-64 rounded-full bg-gradient-to-r from-white/20 to-emerald-300/20 blur-3xl"></div>
|
||||||
<div className="absolute right-10 bottom-10 h-80 w-80 rounded-full bg-gradient-to-r from-teal-300/15 to-blue-300/15 blur-3xl"></div>
|
<div className="absolute right-10 bottom-10 h-80 w-80 rounded-full bg-gradient-to-r from-teal-300/15 to-blue-300/15 blur-3xl"></div>
|
||||||
@@ -384,11 +387,11 @@ export default function HomePage() {
|
|||||||
|
|
||||||
<div className="relative container mx-auto px-4 text-center">
|
<div className="relative container mx-auto px-4 text-center">
|
||||||
<div className="mx-auto max-w-3xl">
|
<div className="mx-auto max-w-3xl">
|
||||||
<h2 className="mb-6 text-5xl font-bold text-white">
|
<h2 className="mb-4 text-3xl font-bold text-white sm:mb-6 sm:text-4xl lg:text-5xl">
|
||||||
Ready to revolutionize
|
Ready to revolutionize
|
||||||
<span className="block">your invoicing?</span>
|
<span className="block">your invoicing?</span>
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mb-8 text-xl text-emerald-100">
|
<p className="mb-6 text-lg text-emerald-100 sm:mb-8 sm:text-xl">
|
||||||
Join thousands of entrepreneurs who've already transformed
|
Join thousands of entrepreneurs who've already transformed
|
||||||
their business with BeenVoice. Start your journey
|
their business with BeenVoice. Start your journey
|
||||||
today—completely free.
|
today—completely free.
|
||||||
@@ -399,15 +402,15 @@ export default function HomePage() {
|
|||||||
<Button
|
<Button
|
||||||
size="lg"
|
size="lg"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="group bg-white px-8 py-4 text-lg font-semibold text-emerald-700 shadow-xl transition-all duration-300 hover:bg-gradient-to-r hover:from-white hover:to-emerald-50 hover:shadow-2xl"
|
className="group w-full bg-white px-6 py-3 text-base font-semibold text-emerald-700 shadow-xl transition-all duration-300 hover:bg-gradient-to-r hover:from-white hover:to-emerald-50 hover:shadow-2xl sm:w-auto sm:px-8 sm:py-4 sm:text-lg"
|
||||||
>
|
>
|
||||||
Start Your Success Story
|
Start Your Success Story
|
||||||
<Rocket className="ml-2 h-5 w-5 transition-transform group-hover:translate-x-1" />
|
<Rocket className="ml-2 h-4 w-4 transition-transform group-hover:translate-x-1 sm:h-5 sm:w-5" />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-8 flex items-center justify-center gap-8 text-emerald-200">
|
<div className="mt-6 flex flex-col items-center justify-center gap-3 text-emerald-200 sm:mt-8 sm:flex-row sm:gap-6">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Heart className="h-4 w-4" />
|
<Heart className="h-4 w-4" />
|
||||||
Free forever
|
Free forever
|
||||||
@@ -426,42 +429,42 @@ export default function HomePage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<footer className="border-t bg-gradient-to-br from-slate-50 to-emerald-50/30 py-12">
|
<footer className="border-t bg-gradient-to-br from-slate-50 to-emerald-50/30 py-8 sm:py-12 dark:border-slate-700 dark:from-slate-900 dark:to-emerald-900/10">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Logo className="mx-auto mb-4" />
|
<Logo className="mx-auto mb-4" />
|
||||||
<p className="mb-6 text-slate-600">
|
<p className="mb-4 text-sm text-slate-600 sm:mb-6 sm:text-base dark:text-slate-400">
|
||||||
Simple invoicing for freelancers. Free, forever.
|
Simple invoicing for freelancers. Free, forever.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center justify-center gap-8 text-sm text-slate-600">
|
<div className="flex flex-wrap items-center justify-center gap-4 text-sm text-slate-600 sm:gap-6 dark:text-slate-400">
|
||||||
<Link
|
<Link
|
||||||
href="/auth/signin"
|
href="/auth/signin"
|
||||||
className="transition-colors hover:text-emerald-600"
|
className="transition-colors hover:text-emerald-600 dark:hover:text-emerald-400"
|
||||||
>
|
>
|
||||||
Sign In
|
Sign In
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/auth/register"
|
href="/auth/register"
|
||||||
className="transition-colors hover:text-emerald-600"
|
className="transition-colors hover:text-emerald-600 dark:hover:text-emerald-400"
|
||||||
>
|
>
|
||||||
Get Started
|
Get Started
|
||||||
</Link>
|
</Link>
|
||||||
<a
|
<a
|
||||||
href="#features"
|
href="#features"
|
||||||
className="transition-colors hover:text-emerald-600"
|
className="transition-colors hover:text-emerald-600 dark:hover:text-emerald-400"
|
||||||
>
|
>
|
||||||
Features
|
Features
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="#pricing"
|
href="#pricing"
|
||||||
className="transition-colors hover:text-emerald-600"
|
className="transition-colors hover:text-emerald-600 dark:hover:text-emerald-400"
|
||||||
>
|
>
|
||||||
Pricing
|
Pricing
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-8 border-t pt-8">
|
<div className="mt-6 border-t pt-6 sm:mt-8 sm:pt-8">
|
||||||
<p className="text-slate-600">
|
<p className="text-sm text-slate-600 sm:text-base dark:text-slate-400">
|
||||||
© 2024 BeenVoice. Built with ♥ for entrepreneurs.
|
© 2024 BeenVoice. Built with ♥ for entrepreneurs.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { FileText, DollarSign, Clock, Save, Check } from "lucide-react";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { FloatingActionBar } from "~/components/layout/floating-action-bar";
|
import { FloatingActionBar } from "~/components/layout/floating-action-bar";
|
||||||
import { InvoiceLineItems } from "~/components/forms/invoice-line-items";
|
import { InvoiceLineItems } from "~/components/forms/invoice-line-items";
|
||||||
|
import { PageHeader } from "~/components/layout/page-header";
|
||||||
|
|
||||||
const STATUS_OPTIONS = [
|
const STATUS_OPTIONS = [
|
||||||
{ value: "draft", label: "Draft" },
|
{ value: "draft", label: "Draft" },
|
||||||
@@ -39,35 +40,59 @@ interface InvoiceFormProps {
|
|||||||
// Custom skeleton for invoice form
|
// Custom skeleton for invoice form
|
||||||
function InvoiceFormSkeleton() {
|
function InvoiceFormSkeleton() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6 pb-24">
|
||||||
{/* Header */}
|
<div className="mb-8">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-start justify-between gap-4">
|
||||||
<div>
|
<div className="bg-muted/30 h-8 w-48 animate-pulse rounded sm:h-9 sm:w-64"></div>
|
||||||
<div className="bg-muted/30 h-9 w-64 animate-pulse rounded"></div>
|
|
||||||
<div className="bg-muted/30 mt-1 h-4 w-48 animate-pulse rounded"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="bg-muted/30 mt-2 h-4 w-36 animate-pulse rounded sm:w-48"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Form Content */}
|
{/* Form Content */}
|
||||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||||||
{/* Left Column - Content with Tabs */}
|
{/* Left Column - Content with Tabs */}
|
||||||
<div className="space-y-6 lg:col-span-2">
|
<div className="space-y-6 lg:col-span-2">
|
||||||
{/* Tabs */}
|
{/* Tabs - Mobile stacked, desktop side-by-side */}
|
||||||
<div className="bg-muted grid w-full grid-cols-2 rounded-lg p-1">
|
<div className="bg-muted grid w-full grid-cols-1 gap-1 rounded-lg p-1 sm:grid-cols-2">
|
||||||
<div className="bg-background h-10 rounded-md shadow-sm"></div>
|
<div className="bg-background h-9 rounded-md shadow-sm sm:h-10"></div>
|
||||||
<div className="bg-muted/30 h-10 rounded-md"></div>
|
<div className="bg-muted/30 h-9 rounded-md sm:h-10"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Invoice Details Card */}
|
{/* Invoice Details Card */}
|
||||||
<Card className="shadow-sm">
|
<Card className="shadow-sm">
|
||||||
<CardHeader>
|
<CardHeader className="pb-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="bg-muted/30 h-5 w-5 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-5 w-5 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-6 w-32 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-6 w-32 animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
{/* First row - stacked on mobile */}
|
||||||
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="bg-muted/30 h-4 w-20 animate-pulse rounded"></div>
|
||||||
|
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="bg-muted/30 h-4 w-16 animate-pulse rounded"></div>
|
||||||
|
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Second row */}
|
||||||
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="bg-muted/30 h-4 w-18 animate-pulse rounded"></div>
|
||||||
|
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="bg-muted/30 h-4 w-16 animate-pulse rounded"></div>
|
||||||
|
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Third row */}
|
||||||
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="bg-muted/30 h-4 w-24 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-4 w-24 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
||||||
@@ -77,32 +102,16 @@ function InvoiceFormSkeleton() {
|
|||||||
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
||||||
<div className="space-y-2">
|
{/* Status field */}
|
||||||
<div className="bg-muted/30 h-4 w-20 animate-pulse rounded"></div>
|
<div className="space-y-2">
|
||||||
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-4 w-16 animate-pulse rounded"></div>
|
||||||
</div>
|
<div className="bg-muted/30 h-10 w-full animate-pulse rounded sm:w-48"></div>
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="bg-muted/30 h-4 w-20 animate-pulse rounded"></div>
|
|
||||||
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="bg-muted/30 h-4 w-28 animate-pulse rounded"></div>
|
|
||||||
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="bg-muted/30 h-4 w-20 animate-pulse rounded"></div>
|
|
||||||
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Notes field */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="bg-muted/30 h-4 w-20 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-4 w-20 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="bg-muted/30 h-4 w-32 animate-pulse rounded"></div>
|
|
||||||
<div className="bg-muted/30 h-20 w-full animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-20 w-full animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -110,24 +119,30 @@ function InvoiceFormSkeleton() {
|
|||||||
|
|
||||||
{/* Invoice Items Card */}
|
{/* Invoice Items Card */}
|
||||||
<Card className="shadow-sm">
|
<Card className="shadow-sm">
|
||||||
<CardHeader>
|
<CardHeader className="pb-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="bg-muted/30 h-5 w-5 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-5 w-5 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-6 w-28 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-6 w-28 animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div className="space-y-4 rounded-lg border p-4">
|
{/* Line item skeleton */}
|
||||||
|
<div className="space-y-4 rounded-lg border p-3 sm:p-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="bg-muted/30 h-5 w-16 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-5 w-20 animate-pulse rounded"></div>
|
||||||
|
<div className="bg-muted/30 h-8 w-8 animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Description */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="bg-muted/30 h-4 w-24 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-4 w-20 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-15 w-full animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-16 w-full animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
|
|
||||||
|
{/* Date, Hours, Rate - stacked on mobile */}
|
||||||
|
<div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="bg-muted/30 h-4 w-12 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-4 w-10 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -135,10 +150,12 @@ function InvoiceFormSkeleton() {
|
|||||||
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="bg-muted/30 h-4 w-16 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-4 w-8 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Amount display */}
|
||||||
<div className="bg-muted/30 rounded-lg p-3">
|
<div className="bg-muted/30 rounded-lg p-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="bg-muted/50 h-4 w-16 animate-pulse rounded"></div>
|
<div className="bg-muted/50 h-4 w-16 animate-pulse rounded"></div>
|
||||||
@@ -146,6 +163,8 @@ function InvoiceFormSkeleton() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Add item button */}
|
||||||
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-10 w-full animate-pulse rounded"></div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -154,20 +173,21 @@ function InvoiceFormSkeleton() {
|
|||||||
{/* Right Column - Summary */}
|
{/* Right Column - Summary */}
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Card className="sticky top-6 shadow-sm">
|
<Card className="sticky top-6 shadow-sm">
|
||||||
<CardHeader>
|
<CardHeader className="pb-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="bg-muted/30 h-5 w-5 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-5 w-5 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-6 w-16 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-6 w-16 animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
|
{/* Totals */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="bg-muted/30 h-4 w-16 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-4 w-16 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-4 w-20 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-4 w-20 animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="bg-muted/30 h-4 w-20 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-4 w-12 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-4 w-16 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-4 w-16 animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
@@ -176,32 +196,43 @@ function InvoiceFormSkeleton() {
|
|||||||
<div className="bg-muted/30 h-5 w-24 animate-pulse rounded"></div>
|
<div className="bg-muted/30 h-5 w-24 animate-pulse rounded"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
|
{/* Stats */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex justify-between">
|
<div className="bg-muted/30 h-4 w-20 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-3 w-12 animate-pulse rounded"></div>
|
<div className="grid grid-cols-2 gap-2">
|
||||||
<div className="bg-muted/30 h-3 w-8 animate-pulse rounded"></div>
|
<div className="space-y-1">
|
||||||
</div>
|
<div className="bg-muted/30 h-3 w-12 animate-pulse rounded"></div>
|
||||||
<div className="flex justify-between">
|
<div className="bg-muted/30 h-4 w-8 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-3 w-12 animate-pulse rounded"></div>
|
</div>
|
||||||
<div className="bg-muted/30 h-3 w-20 animate-pulse rounded"></div>
|
<div className="space-y-1">
|
||||||
</div>
|
<div className="bg-muted/30 h-3 w-16 animate-pulse rounded"></div>
|
||||||
<div className="flex justify-between">
|
<div className="bg-muted/30 h-4 w-12 animate-pulse rounded"></div>
|
||||||
<div className="bg-muted/30 h-3 w-16 animate-pulse rounded"></div>
|
</div>
|
||||||
<div className="bg-muted/30 h-3 w-24 animate-pulse rounded"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Floating Action Bar Skeleton - Mobile only */}
|
||||||
|
<div className="fixed right-4 bottom-6 left-4 lg:hidden">
|
||||||
|
<div className="bg-background rounded-lg border p-4 shadow-lg">
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<div className="bg-muted/30 h-9 flex-1 animate-pulse rounded"></div>
|
||||||
|
<div className="bg-muted/30 h-9 w-20 animate-pulse rounded"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function InvoiceForm({ invoiceId }: InvoiceFormProps) {
|
export function InvoiceForm({ invoiceId }: InvoiceFormProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const headerRef = useRef<HTMLDivElement>(null);
|
|
||||||
const footerRef = useRef<HTMLDivElement>(null);
|
const footerRef = useRef<HTMLDivElement>(null);
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
invoiceNumber: `INV-${new Date().toISOString().slice(0, 10).replace(/-/g, "")}-${Date.now().toString().slice(-6)}`,
|
invoiceNumber: `INV-${new Date().toISOString().slice(0, 10).replace(/-/g, "")}-${Date.now().toString().slice(-6)}`,
|
||||||
@@ -263,8 +294,8 @@ export function InvoiceForm({ invoiceId }: InvoiceFormProps) {
|
|||||||
date: new Date(),
|
date: new Date(),
|
||||||
description: "",
|
description: "",
|
||||||
hours: 1,
|
hours: 1,
|
||||||
rate: formData.defaultHourlyRate,
|
rate: 100,
|
||||||
amount: formData.defaultHourlyRate,
|
amount: 100,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -292,7 +323,6 @@ export function InvoiceForm({ invoiceId }: InvoiceFormProps) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}, [formData.clientId, clients]);
|
}, [formData.clientId, clients]);
|
||||||
|
|
||||||
// Calculate totals
|
// Calculate totals
|
||||||
@@ -441,17 +471,13 @@ export function InvoiceForm({ invoiceId }: InvoiceFormProps) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Header */}
|
<PageHeader
|
||||||
<div ref={headerRef} className="flex items-center justify-between">
|
title={invoiceId ? "Edit Invoice" : "Create Invoice"}
|
||||||
<div>
|
description={
|
||||||
<h1 className="bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-3xl font-bold text-transparent">
|
invoiceId ? "Update invoice details" : "Create a new invoice"
|
||||||
{invoiceId ? "Edit Invoice" : "Create Invoice"}
|
}
|
||||||
</h1>
|
variant="gradient"
|
||||||
<p className="text-muted-foreground mt-1">
|
/>
|
||||||
{invoiceId ? "Update invoice details" : "Create a new invoice"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Form Content */}
|
{/* Form Content */}
|
||||||
<form id="invoice-form" onSubmit={handleSubmit} className="space-y-6">
|
<form id="invoice-form" onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
|||||||
Reference in New Issue
Block a user