Begin dark mode!
This commit is contained in:
@@ -42,16 +42,25 @@ interface InvoiceViewProps {
|
||||
}
|
||||
|
||||
const statusConfig = {
|
||||
draft: { label: "Draft", color: "bg-gray-100 text-gray-800", icon: FileText },
|
||||
sent: { label: "Sent", color: "bg-blue-100 text-blue-800", icon: Send },
|
||||
draft: {
|
||||
label: "Draft",
|
||||
color: "bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300",
|
||||
icon: FileText,
|
||||
},
|
||||
sent: {
|
||||
label: "Sent",
|
||||
color: "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400",
|
||||
icon: Send,
|
||||
},
|
||||
paid: {
|
||||
label: "Paid",
|
||||
color: "bg-green-100 text-green-800",
|
||||
color:
|
||||
"bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400",
|
||||
icon: DollarSign,
|
||||
},
|
||||
overdue: {
|
||||
label: "Overdue",
|
||||
color: "bg-red-100 text-red-800",
|
||||
color: "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400",
|
||||
icon: AlertCircle,
|
||||
},
|
||||
} as const;
|
||||
@@ -144,10 +153,10 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
return (
|
||||
<div className="py-12 text-center">
|
||||
<FileText className="mx-auto mb-4 h-12 w-12 text-gray-400" />
|
||||
<h3 className="mb-2 text-lg font-medium text-gray-900">
|
||||
<h3 className="mb-2 text-lg font-medium text-gray-900 dark:text-white">
|
||||
Invoice not found
|
||||
</h3>
|
||||
<p className="mb-4 text-gray-500">
|
||||
<p className="mb-4 text-gray-500 dark:text-gray-400">
|
||||
The invoice you're looking for doesn't exist or has been
|
||||
deleted.
|
||||
</p>
|
||||
@@ -165,9 +174,9 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
<div className="space-y-6">
|
||||
{/* Status Alert */}
|
||||
{isOverdue && (
|
||||
<Card className="border-red-200 bg-red-50">
|
||||
<Card className="border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-900/20">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-2 text-red-700">
|
||||
<div className="flex items-center gap-2 text-red-700 dark:text-red-400">
|
||||
<AlertCircle className="h-5 w-5" />
|
||||
<span className="font-medium">This invoice is overdue</span>
|
||||
</div>
|
||||
@@ -179,32 +188,38 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
{/* Main Content */}
|
||||
<div className="space-y-6 lg:col-span-2">
|
||||
{/* Invoice Header Card */}
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
|
||||
<CardContent>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="rounded-lg bg-emerald-100 p-2">
|
||||
<FileText className="h-6 w-6 text-emerald-600" />
|
||||
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
|
||||
<FileText className="h-6 w-6 text-emerald-600 dark:text-emerald-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
{invoice.invoiceNumber}
|
||||
</h2>
|
||||
<p className="text-gray-600">Professional Invoice</p>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
Professional Invoice
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-6 text-sm">
|
||||
<div>
|
||||
<span className="text-gray-500">Issue Date</span>
|
||||
<p className="font-medium text-gray-900">
|
||||
<span className="text-gray-500 dark:text-gray-400">
|
||||
Issue Date
|
||||
</span>
|
||||
<p className="font-medium text-gray-900 dark:text-white">
|
||||
{formatDate(invoice.issueDate)}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">Due Date</span>
|
||||
<p className="font-medium text-gray-900">
|
||||
<span className="text-gray-500 dark:text-gray-400">
|
||||
Due Date
|
||||
</span>
|
||||
<p className="font-medium text-gray-900 dark:text-white">
|
||||
{formatDate(invoice.dueDate)}
|
||||
</p>
|
||||
</div>
|
||||
@@ -221,7 +236,7 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
.label
|
||||
}
|
||||
</Badge>
|
||||
<div className="text-3xl font-bold text-emerald-600">
|
||||
<div className="text-3xl font-bold text-emerald-600 dark:text-emerald-400">
|
||||
{formatCurrency(invoice.totalAmount)}
|
||||
</div>
|
||||
<Button
|
||||
@@ -247,38 +262,38 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
</Card>
|
||||
|
||||
{/* Client Information */}
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-emerald-700">
|
||||
<CardTitle className="flex items-center gap-2 text-emerald-700 dark:text-emerald-400">
|
||||
<User className="h-5 w-5" />
|
||||
Bill To
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">
|
||||
{invoice.client?.name}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 text-sm md:grid-cols-2">
|
||||
{invoice.client?.email && (
|
||||
<div className="flex items-center gap-2 text-gray-600">
|
||||
<Mail className="h-4 w-4 text-gray-400" />
|
||||
<div className="flex items-center gap-2 text-gray-600 dark:text-gray-300">
|
||||
<Mail className="h-4 w-4 text-gray-400 dark:text-gray-500" />
|
||||
{invoice.client.email}
|
||||
</div>
|
||||
)}
|
||||
{invoice.client?.phone && (
|
||||
<div className="flex items-center gap-2 text-gray-600">
|
||||
<Phone className="h-4 w-4 text-gray-400" />
|
||||
<div className="flex items-center gap-2 text-gray-600 dark:text-gray-300">
|
||||
<Phone className="h-4 w-4 text-gray-400 dark:text-gray-500" />
|
||||
{invoice.client.phone}
|
||||
</div>
|
||||
)}
|
||||
{(invoice.client?.addressLine1 ??
|
||||
invoice.client?.city ??
|
||||
invoice.client?.state) && (
|
||||
<div className="flex items-start gap-2 text-gray-600 md:col-span-2">
|
||||
<MapPin className="mt-0.5 h-4 w-4 flex-shrink-0 text-gray-400" />
|
||||
<div className="flex items-start gap-2 text-gray-600 md:col-span-2 dark:text-gray-300">
|
||||
<MapPin className="mt-0.5 h-4 w-4 flex-shrink-0 text-gray-400 dark:text-gray-500" />
|
||||
<div>
|
||||
{invoice.client?.addressLine1 && (
|
||||
<div>{invoice.client.addressLine1}</div>
|
||||
@@ -310,31 +325,31 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
</Card>
|
||||
|
||||
{/* Invoice Items */}
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-emerald-700">
|
||||
<CardTitle className="flex items-center gap-2 text-emerald-700 dark:text-emerald-400">
|
||||
<Clock className="h-5 w-5" />
|
||||
Invoice Items
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="overflow-hidden rounded-lg border border-gray-200">
|
||||
<div className="overflow-hidden rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
<table className="w-full">
|
||||
<thead className="bg-gray-50">
|
||||
<thead className="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-sm font-semibold text-gray-700">
|
||||
<th className="px-4 py-3 text-left text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
Date
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-sm font-semibold text-gray-700">
|
||||
<th className="px-4 py-3 text-left text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
Description
|
||||
</th>
|
||||
<th className="px-4 py-3 text-right text-sm font-semibold text-gray-700">
|
||||
<th className="px-4 py-3 text-right text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
Hours
|
||||
</th>
|
||||
<th className="px-4 py-3 text-right text-sm font-semibold text-gray-700">
|
||||
<th className="px-4 py-3 text-right text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
Rate
|
||||
</th>
|
||||
<th className="px-4 py-3 text-right text-sm font-semibold text-gray-700">
|
||||
<th className="px-4 py-3 text-right text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
Amount
|
||||
</th>
|
||||
</tr>
|
||||
@@ -343,21 +358,21 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
{invoice.items?.map((item, index) => (
|
||||
<tr
|
||||
key={item.id || index}
|
||||
className="border-t border-gray-100 hover:bg-gray-50"
|
||||
className="border-t border-gray-100 hover:bg-gray-50 dark:border-gray-600 dark:hover:bg-gray-700"
|
||||
>
|
||||
<td className="px-4 py-3 text-sm text-gray-900">
|
||||
<td className="px-4 py-3 text-sm text-gray-900 dark:text-gray-300">
|
||||
{formatDate(item.date)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-gray-900">
|
||||
<td className="px-4 py-3 text-sm text-gray-900 dark:text-gray-300">
|
||||
{item.description}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right text-sm text-gray-900">
|
||||
<td className="px-4 py-3 text-right text-sm text-gray-900 dark:text-gray-300">
|
||||
{item.hours}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right text-sm text-gray-900">
|
||||
<td className="px-4 py-3 text-right text-sm text-gray-900 dark:text-gray-300">
|
||||
{formatCurrency(item.rate)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right text-sm font-medium text-gray-900">
|
||||
<td className="px-4 py-3 text-right text-sm font-medium text-gray-900 dark:text-gray-300">
|
||||
{formatCurrency(item.amount)}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -370,12 +385,14 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
|
||||
{/* Notes */}
|
||||
{invoice.notes && (
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-emerald-700">Notes</CardTitle>
|
||||
<CardTitle className="text-emerald-700 dark:text-emerald-400">
|
||||
Notes
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="whitespace-pre-wrap text-gray-700">
|
||||
<p className="whitespace-pre-wrap text-gray-700 dark:text-gray-300">
|
||||
{invoice.notes}
|
||||
</p>
|
||||
</CardContent>
|
||||
@@ -386,9 +403,11 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
{/* Sidebar */}
|
||||
<div className="space-y-6">
|
||||
{/* Status Actions */}
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-emerald-700">Status Actions</CardTitle>
|
||||
<CardTitle className="text-emerald-700 dark:text-emerald-400">
|
||||
Status Actions
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{invoice.status === "draft" && (
|
||||
@@ -426,41 +445,47 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
|
||||
{invoice.status === "paid" && (
|
||||
<div className="py-4 text-center">
|
||||
<DollarSign className="mx-auto mb-2 h-8 w-8 text-green-600" />
|
||||
<p className="font-medium text-green-600">Invoice Paid</p>
|
||||
<DollarSign className="mx-auto mb-2 h-8 w-8 text-green-600 dark:text-green-400" />
|
||||
<p className="font-medium text-green-600 dark:text-green-400">
|
||||
Invoice Paid
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Invoice Summary */}
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm">
|
||||
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-emerald-700">Summary</CardTitle>
|
||||
<CardTitle className="text-emerald-700 dark:text-emerald-400">
|
||||
Summary
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-gray-600">Subtotal</span>
|
||||
<span className="font-medium">
|
||||
<span className="text-gray-600 dark:text-gray-300">
|
||||
Subtotal
|
||||
</span>
|
||||
<span className="font-medium dark:text-white">
|
||||
{formatCurrency(invoice.totalAmount)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-gray-600">Tax</span>
|
||||
<span className="font-medium">$0.00</span>
|
||||
<span className="text-gray-600 dark:text-gray-300">Tax</span>
|
||||
<span className="font-medium dark:text-white">$0.00</span>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="flex justify-between text-lg font-bold">
|
||||
<span>Total</span>
|
||||
<span className="text-emerald-600">
|
||||
<span className="dark:text-white">Total</span>
|
||||
<span className="text-emerald-600 dark:text-emerald-400">
|
||||
{formatCurrency(invoice.totalAmount)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200 pt-4 text-center">
|
||||
<p className="text-sm text-gray-500">
|
||||
<div className="border-t border-gray-200 pt-4 text-center dark:border-gray-700">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{invoice.items?.length ?? 0} item
|
||||
{invoice.items?.length !== 1 ? "s" : ""}
|
||||
</p>
|
||||
@@ -469,15 +494,17 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
</Card>
|
||||
|
||||
{/* Danger Zone */}
|
||||
<Card className="border-0 border-red-200 bg-white/80 shadow-xl backdrop-blur-sm">
|
||||
<Card className="border-0 border-red-200 bg-white/80 shadow-xl backdrop-blur-sm dark:border-red-800 dark:bg-gray-800/80">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-red-700">Danger Zone</CardTitle>
|
||||
<CardTitle className="text-red-700 dark:text-red-400">
|
||||
Danger Zone
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Button
|
||||
onClick={handleDelete}
|
||||
variant="outline"
|
||||
className="w-full border-red-200 text-red-700 hover:bg-red-50"
|
||||
className="w-full border-red-200 text-red-700 hover:bg-red-50 dark:border-red-800 dark:text-red-400 dark:hover:bg-red-900/20"
|
||||
>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
Delete Invoice
|
||||
@@ -489,12 +516,12 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
||||
<DialogContent className="border-0 bg-white/95 shadow-2xl backdrop-blur-sm">
|
||||
<DialogContent className="border-0 bg-white/95 shadow-2xl backdrop-blur-sm dark:bg-gray-800/95">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-xl font-bold text-gray-800">
|
||||
<DialogTitle className="text-xl font-bold text-gray-800 dark:text-white">
|
||||
Delete Invoice
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-gray-600">
|
||||
<DialogDescription className="text-gray-600 dark:text-gray-300">
|
||||
Are you sure you want to delete this invoice? This action cannot
|
||||
be undone and will permanently remove the invoice and all its
|
||||
data.
|
||||
@@ -504,7 +531,7 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setDeleteDialogOpen(false)}
|
||||
className="border-gray-300 text-gray-700 hover:bg-gray-50"
|
||||
className="border-gray-300 text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user