Theme overhaul
This commit is contained in:
@@ -73,7 +73,7 @@ export function ClientList() {
|
||||
return (
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
{Array.from({ length: 3 }, (_, i: number) => (
|
||||
<Card key={i} className="card-primary">
|
||||
<Card key={i} className="bg-card border-border border">
|
||||
<CardHeader>
|
||||
<div className="h-4 animate-pulse rounded bg-gray-200" />
|
||||
</CardHeader>
|
||||
@@ -91,9 +91,9 @@ export function ClientList() {
|
||||
|
||||
if (!clients || clients.length === 0) {
|
||||
return (
|
||||
<Card className="card-primary">
|
||||
<Card className="bg-card border-border border">
|
||||
<CardHeader className="text-center">
|
||||
<CardTitle className="text-brand-gradient text-2xl font-bold">
|
||||
<CardTitle className="text-primary text-2xl font-bold">
|
||||
No Clients Yet
|
||||
</CardTitle>
|
||||
<CardDescription className="text-lg">
|
||||
@@ -142,20 +142,20 @@ export function ClientList() {
|
||||
{filteredClients.map((client) => (
|
||||
<Card
|
||||
key={client.id}
|
||||
className="group card-primary transition-all duration-300 hover:shadow-lg"
|
||||
className="group bg-card border-border border transition-all duration-300 hover:shadow-lg"
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between text-lg">
|
||||
<span className="text-accent group-hover:text-icon-emerald font-semibold transition-colors">
|
||||
<span className="text-foreground group-hover:text-primary font-semibold transition-colors">
|
||||
{client.name}
|
||||
</span>
|
||||
<div className="flex space-x-1 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
<Link href={`/clients/${client.id}`}>
|
||||
<Link href={`/dashboard/clients/${client.id}`}>
|
||||
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href={`/clients/${client.id}/edit`}>
|
||||
<Link href={`/dashboard/clients/${client.id}/edit`}>
|
||||
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -173,25 +173,25 @@ export function ClientList() {
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{client.email && (
|
||||
<div className="text-secondary flex items-center text-sm">
|
||||
<div className="bg-brand-muted mr-3 rounded p-1.5">
|
||||
<Mail className="text-icon-emerald h-3 w-3" />
|
||||
<div className="text-muted-foreground flex items-center text-sm">
|
||||
<div className="bg-muted mr-3 rounded p-1.5">
|
||||
<Mail className="text-muted-foreground h-3 w-3" />
|
||||
</div>
|
||||
{client.email}
|
||||
</div>
|
||||
)}
|
||||
{client.phone && (
|
||||
<div className="text-secondary flex items-center text-sm">
|
||||
<div className="bg-brand-muted-blue mr-3 rounded p-1.5">
|
||||
<Phone className="text-icon-blue h-3 w-3" />
|
||||
<div className="text-muted-foreground flex items-center text-sm">
|
||||
<div className="bg-muted mr-3 rounded p-1.5">
|
||||
<Phone className="text-muted-foreground h-3 w-3" />
|
||||
</div>
|
||||
{client.phone}
|
||||
</div>
|
||||
)}
|
||||
{(client.addressLine1 ?? client.city ?? client.state) && (
|
||||
<div className="text-secondary flex items-start text-sm">
|
||||
<div className="bg-brand-muted-teal mt-0.5 mr-3 flex-shrink-0 rounded p-1.5">
|
||||
<MapPin className="text-icon-teal h-3 w-3" />
|
||||
<div className="text-muted-foreground flex items-start text-sm">
|
||||
<div className="bg-muted mt-0.5 mr-3 flex-shrink-0 rounded p-1.5">
|
||||
<MapPin className="text-muted-foreground h-3 w-3" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
{client.addressLine1 && <div>{client.addressLine1}</div>}
|
||||
@@ -213,12 +213,12 @@ export function ClientList() {
|
||||
</div>
|
||||
|
||||
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
||||
<DialogContent className="card-primary">
|
||||
<DialogContent className="bg-card border-border border">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-accent text-xl font-bold">
|
||||
<DialogTitle className="text-foreground text-xl font-bold">
|
||||
Delete Client
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-secondary">
|
||||
<DialogDescription className="text-muted-foreground">
|
||||
Are you sure you want to delete this client? This action cannot be
|
||||
undone.
|
||||
</DialogDescription>
|
||||
@@ -227,14 +227,14 @@ export function ClientList() {
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setDeleteDialogOpen(false)}
|
||||
className="text-secondary"
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={confirmDelete}
|
||||
className="bg-red-600 hover:bg-red-700"
|
||||
className="bg-destructive hover:bg-destructive/90"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
|
||||
@@ -28,10 +28,10 @@ export function CurrentOpenInvoiceCard() {
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Card className="card-primary">
|
||||
<Card className="bg-card border-border border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="card-title-secondary">
|
||||
<FileText className="text-icon-emerald h-5 w-5" />
|
||||
<CardTitle className="text-foreground flex items-center gap-2">
|
||||
<FileText className="text-primary h-5 w-5" />
|
||||
Current Open Invoice
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
@@ -49,10 +49,10 @@ export function CurrentOpenInvoiceCard() {
|
||||
|
||||
if (!currentInvoice) {
|
||||
return (
|
||||
<Card className="card-primary">
|
||||
<Card className="bg-card border-border border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="card-title-secondary">
|
||||
<FileText className="text-icon-emerald h-5 w-5" />
|
||||
<CardTitle className="text-foreground flex items-center gap-2">
|
||||
<FileText className="text-primary h-5 w-5" />
|
||||
Current Open Invoice
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
@@ -80,10 +80,10 @@ export function CurrentOpenInvoiceCard() {
|
||||
const totalAmount = currentInvoice.totalAmount;
|
||||
|
||||
return (
|
||||
<Card className="card-primary">
|
||||
<Card className="bg-card border-border border">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="card-title-secondary">
|
||||
<FileText className="text-icon-emerald h-5 w-5" />
|
||||
<CardTitle className="text-foreground flex items-center gap-2">
|
||||
<FileText className="text-primary h-5 w-5" />
|
||||
Current Open Invoice
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
@@ -91,13 +91,13 @@ export function CurrentOpenInvoiceCard() {
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge className="badge-secondary text-xs">
|
||||
<Badge className="bg-secondary text-secondary-foreground text-xs">
|
||||
{currentInvoice.invoiceNumber}
|
||||
</Badge>
|
||||
<Badge className="badge-outline text-xs">Draft</Badge>
|
||||
<Badge className="border text-xs">Draft</Badge>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-icon-emerald text-sm font-medium">
|
||||
<p className="text-primary text-sm font-medium">
|
||||
{formatCurrency(totalAmount)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -215,12 +215,12 @@ export function DataTable<TData, TValue>({
|
||||
|
||||
{/* Filter Bar Card */}
|
||||
{(showSearch || filterableColumns.length > 0 || showColumnVisibility) && (
|
||||
<Card className="card-primary py-2">
|
||||
<Card className="bg-card border-border border py-2">
|
||||
<CardContent className="px-3 py-0">
|
||||
<div className="flex items-center gap-2">
|
||||
{showSearch && (
|
||||
<div className="relative min-w-0 flex-1">
|
||||
<Search className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
|
||||
<Search className="text-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
|
||||
<Input
|
||||
placeholder={searchPlaceholder}
|
||||
value={globalFilter ?? ""}
|
||||
@@ -244,7 +244,7 @@ export function DataTable<TData, TValue>({
|
||||
>
|
||||
<SelectTrigger className="h-9 w-9 p-0 sm:w-[180px] sm:px-3 [&>svg]:hidden sm:[&>svg]:inline-flex">
|
||||
<div className="flex w-full items-center justify-center">
|
||||
<Filter className="h-4 w-4 sm:hidden" />
|
||||
<Filter className="text-foreground h-4 w-4 sm:hidden" />
|
||||
<span className="hidden sm:inline">
|
||||
<SelectValue placeholder={column.title} />
|
||||
</span>
|
||||
@@ -272,7 +272,7 @@ export function DataTable<TData, TValue>({
|
||||
>
|
||||
<X className="h-4 w-4 sm:hidden" />
|
||||
<span className="hidden sm:flex sm:items-center">
|
||||
<Filter className="mr-2 h-3.5 w-3.5" />
|
||||
<Filter className="text-foreground mr-2 h-3.5 w-3.5" />
|
||||
Clear filters
|
||||
</span>
|
||||
</Button>
|
||||
@@ -315,7 +315,7 @@ export function DataTable<TData, TValue>({
|
||||
)}
|
||||
|
||||
{/* Table Content Card */}
|
||||
<Card className="card-primary overflow-hidden p-0">
|
||||
<Card className="bg-card border-border overflow-hidden border p-0">
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
@@ -400,7 +400,7 @@ export function DataTable<TData, TValue>({
|
||||
|
||||
{/* Pagination Bar Card */}
|
||||
{showPagination && (
|
||||
<Card className="card-primary py-2">
|
||||
<Card className="bg-card border-border border py-2">
|
||||
<CardContent className="px-3 py-0">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -561,17 +561,17 @@ export function DataTableSkeleton({
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Filter bar skeleton */}
|
||||
<Card className="card-primary py-2">
|
||||
<Card className="bg-card border-border border py-2">
|
||||
<CardContent className="px-3 py-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="bg-muted/30 h-9 w-full flex-1 animate-pulse rounded-md sm:max-w-sm"></div>
|
||||
<div className="bg-muted/30 h-9 w-24 animate-pulse rounded-md"></div>
|
||||
<div className="bg-muted/30 h-9 w-full flex-1 animate-pulse sm:max-w-sm"></div>
|
||||
<div className="bg-muted/30 h-9 w-24 animate-pulse"></div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Table skeleton */}
|
||||
<Card className="card-primary overflow-hidden p-0">
|
||||
<Card className="bg-card border-border overflow-hidden border p-0">
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
@@ -632,7 +632,7 @@ export function DataTableSkeleton({
|
||||
</Card>
|
||||
|
||||
{/* Pagination skeleton */}
|
||||
<Card className="card-primary py-2">
|
||||
<Card className="bg-card border-border border py-2">
|
||||
<CardContent className="px-3 py-0">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -87,7 +87,7 @@ function SortableItem({
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
className={`card-secondary rounded-lg transition-colors ${
|
||||
className={`card-secondary transition-colors ${
|
||||
isDragging ? "opacity-50 shadow-lg" : ""
|
||||
}`}
|
||||
>
|
||||
@@ -154,7 +154,7 @@ function SortableItem({
|
||||
|
||||
{/* Amount */}
|
||||
<div className="col-span-1">
|
||||
<div className="bg-muted/30 flex h-9 items-center rounded-md border px-3 font-medium text-emerald-600">
|
||||
<div className="bg-muted/30 text-primary flex h-9 items-center border px-3 font-medium">
|
||||
${item.amount.toFixed(2)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -166,7 +166,7 @@ function SortableItem({
|
||||
onClick={() => onRemove(index)}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-9 w-9 p-0 text-red-600 hover:bg-red-50 hover:text-red-700"
|
||||
className="text-destructive hover:bg-destructive/10 hover:text-destructive/80 h-9 w-9 p-0"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -206,7 +206,7 @@ function SortableItem({
|
||||
onClick={() => onRemove(index)}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 w-6 p-0 text-red-600 hover:bg-red-50 hover:text-red-700"
|
||||
className="text-destructive hover:bg-destructive/10 hover:text-destructive/80 h-6 w-6 p-0"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</Button>
|
||||
@@ -266,10 +266,10 @@ function SortableItem({
|
||||
</div>
|
||||
|
||||
{/* Amount */}
|
||||
<div className="bg-muted/20 rounded-md border p-3">
|
||||
<div className="bg-muted/20 border p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-muted-foreground text-sm">Total Amount:</span>
|
||||
<span className="font-mono text-lg font-bold text-emerald-600">
|
||||
<span className="text-primary font-mono text-lg font-bold">
|
||||
${item.amount.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -362,7 +362,7 @@ export function EditableInvoiceItems({
|
||||
{items.map((item, _index) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="card-secondary animate-pulse rounded-lg p-4"
|
||||
className="card-secondary animate-pulse p-4"
|
||||
>
|
||||
{/* Desktop Skeleton */}
|
||||
<div className="hidden grid-cols-12 gap-3 md:grid">
|
||||
|
||||
@@ -150,12 +150,12 @@ export function InvoiceList() {
|
||||
<CardTitle className="flex items-center justify-between">
|
||||
<span className="truncate">{invoice.invoiceNumber}</span>
|
||||
<div className="flex space-x-1">
|
||||
<Link href={`/dashboard/invoices/${invoice.id}/view`}>
|
||||
<Link href={`/dashboard/invoices/${invoice.id}`}>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href={`/dashboard/invoices/${invoice.id}`}>
|
||||
<Link href={`/dashboard/invoices/${invoice.id}/edit`}>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -171,7 +171,7 @@ export function InvoiceList() {
|
||||
</CardTitle>
|
||||
<div className="flex items-center justify-between">
|
||||
<StatusBadge status={invoice.status as StatusType} />
|
||||
<span className="text-icon-green text-lg font-bold">
|
||||
<span className="text-primary text-lg font-bold">
|
||||
{formatCurrency(invoice.totalAmount)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -148,7 +148,7 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
return (
|
||||
<div className="py-12 text-center">
|
||||
<FileText className="text-muted mx-auto mb-4 h-12 w-12" />
|
||||
<h3 className="text-accent mb-2 text-lg font-medium">
|
||||
<h3 className="text-foreground mb-2 text-lg font-medium">
|
||||
Invoice not found
|
||||
</h3>
|
||||
<p className="text-muted mb-4">
|
||||
@@ -169,9 +169,9 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
<div className="space-y-6">
|
||||
{/* Status Alert */}
|
||||
{isOverdue && (
|
||||
<Card className="border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-900/20">
|
||||
<Card className="border-destructive/20 bg-destructive/10">
|
||||
<CardContent className="p-4">
|
||||
<div className="text-error flex items-center gap-2">
|
||||
<div className="text-destructive flex items-center gap-2">
|
||||
<AlertCircle className="h-5 w-5" />
|
||||
<span className="font-medium">This invoice is overdue</span>
|
||||
</div>
|
||||
@@ -183,13 +183,13 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
{/* Main Content */}
|
||||
<div className="space-y-6 lg:col-span-2">
|
||||
{/* Invoice Header Card */}
|
||||
<Card className="card-primary">
|
||||
<Card className="bg-card border-border border">
|
||||
<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 dark:bg-emerald-900/30">
|
||||
<FileText className="h-6 w-6 text-emerald-600 dark:text-emerald-400" />
|
||||
<div className="bg-primary/10 p-2">
|
||||
<FileText className="text-primary h-6 w-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
@@ -228,7 +228,7 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
>
|
||||
<StatusIcon className="mr-1 h-3 w-3" />
|
||||
</StatusBadge>
|
||||
<div className="text-3xl font-bold text-emerald-600 dark:text-emerald-400">
|
||||
<div className="text-primary text-3xl font-bold">
|
||||
{formatCurrency(invoice.totalAmount)}
|
||||
</div>
|
||||
<Button
|
||||
@@ -239,7 +239,7 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
>
|
||||
{isExportingPDF ? (
|
||||
<>
|
||||
<div className="mr-2 h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" />
|
||||
<div className="mr-2 h-4 w-4 animate-spin border-2 border-white border-t-transparent" />
|
||||
Generating PDF...
|
||||
</>
|
||||
) : (
|
||||
@@ -255,9 +255,9 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
</Card>
|
||||
|
||||
{/* Client Information */}
|
||||
<Card className="card-primary">
|
||||
<Card className="bg-card border-border border">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-emerald-700 dark:text-emerald-400">
|
||||
<CardTitle className="text-primary flex items-center gap-2">
|
||||
<User className="h-5 w-5" />
|
||||
Bill To
|
||||
</CardTitle>
|
||||
@@ -318,31 +318,31 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
</Card>
|
||||
|
||||
{/* Invoice Items */}
|
||||
<Card className="card-primary">
|
||||
<Card className="bg-card border-border border">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-emerald-700 dark:text-emerald-400">
|
||||
<CardTitle className="text-primary flex items-center gap-2">
|
||||
<Clock className="h-5 w-5" />
|
||||
Invoice Items
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="overflow-hidden rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
<div className="border-border overflow-hidden border">
|
||||
<table className="w-full">
|
||||
<thead className="bg-gray-50 dark:bg-gray-700">
|
||||
<thead className="bg-muted">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
<th className="text-muted-foreground px-4 py-3 text-left text-sm font-semibold">
|
||||
Date
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
<th className="text-muted-foreground px-4 py-3 text-left text-sm font-semibold">
|
||||
Description
|
||||
</th>
|
||||
<th className="px-4 py-3 text-right text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
<th className="text-muted-foreground px-4 py-3 text-right text-sm font-semibold">
|
||||
Hours
|
||||
</th>
|
||||
<th className="px-4 py-3 text-right text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
<th className="text-muted-foreground px-4 py-3 text-right text-sm font-semibold">
|
||||
Rate
|
||||
</th>
|
||||
<th className="px-4 py-3 text-right text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
<th className="text-muted-foreground px-4 py-3 text-right text-sm font-semibold">
|
||||
Amount
|
||||
</th>
|
||||
</tr>
|
||||
@@ -351,21 +351,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 dark:border-gray-600 dark:hover:bg-gray-700"
|
||||
className="border-border hover:bg-muted/50 border-t"
|
||||
>
|
||||
<td className="px-4 py-3 text-sm text-gray-900 dark:text-gray-300">
|
||||
<td className="text-foreground px-4 py-3 text-sm">
|
||||
{formatDate(item.date)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-gray-900 dark:text-gray-300">
|
||||
<td className="text-foreground px-4 py-3 text-sm">
|
||||
{item.description}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right text-sm text-gray-900 dark:text-gray-300">
|
||||
<td className="text-foreground px-4 py-3 text-right text-sm">
|
||||
{item.hours}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right text-sm text-gray-900 dark:text-gray-300">
|
||||
<td className="text-foreground px-4 py-3 text-right text-sm">
|
||||
{formatCurrency(item.rate)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right text-sm font-medium text-gray-900 dark:text-gray-300">
|
||||
<td className="text-foreground px-4 py-3 text-right text-sm font-medium">
|
||||
{formatCurrency(item.amount)}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -378,11 +378,9 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
|
||||
{/* Notes */}
|
||||
{invoice.notes && (
|
||||
<Card className="card-primary">
|
||||
<Card className="bg-card border-border border">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-emerald-700 dark:text-emerald-400">
|
||||
Notes
|
||||
</CardTitle>
|
||||
<CardTitle className="text-primary">Notes</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="whitespace-pre-wrap text-gray-700 dark:text-gray-300">
|
||||
@@ -396,18 +394,16 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
{/* Sidebar */}
|
||||
<div className="space-y-6">
|
||||
{/* Status Actions */}
|
||||
<Card className="card-primary">
|
||||
<Card className="bg-card border-border border">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-emerald-700 dark:text-emerald-400">
|
||||
Status Actions
|
||||
</CardTitle>
|
||||
<CardTitle className="text-primary">Status Actions</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{invoice.status === "draft" && (
|
||||
<Button
|
||||
onClick={() => handleStatusUpdate("sent")}
|
||||
disabled={updateStatus.isPending}
|
||||
className="w-full bg-blue-600 text-white hover:bg-blue-700"
|
||||
className="bg-primary text-primary-foreground hover:bg-primary/90 w-full"
|
||||
>
|
||||
<Send className="mr-2 h-4 w-4" />
|
||||
Mark as Sent
|
||||
@@ -418,7 +414,7 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
<Button
|
||||
onClick={() => handleStatusUpdate("paid")}
|
||||
disabled={updateStatus.isPending}
|
||||
className="w-full bg-green-600 text-white hover:bg-green-700"
|
||||
className="bg-primary text-primary-foreground hover:bg-primary/90 w-full"
|
||||
>
|
||||
<DollarSign className="mr-2 h-4 w-4" />
|
||||
Mark as Paid
|
||||
@@ -429,7 +425,7 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
<Button
|
||||
onClick={() => handleStatusUpdate("paid")}
|
||||
disabled={updateStatus.isPending}
|
||||
className="w-full bg-green-600 text-white hover:bg-green-700"
|
||||
className="bg-primary text-primary-foreground hover:bg-primary/90 w-full"
|
||||
>
|
||||
<DollarSign className="mr-2 h-4 w-4" />
|
||||
Mark as Paid
|
||||
@@ -438,21 +434,17 @@ 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 dark:text-green-400" />
|
||||
<p className="font-medium text-green-600 dark:text-green-400">
|
||||
Invoice Paid
|
||||
</p>
|
||||
<DollarSign className="text-primary mx-auto mb-2 h-8 w-8" />
|
||||
<p className="text-primary font-medium">Invoice Paid</p>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Invoice Summary */}
|
||||
<Card className="card-primary">
|
||||
<Card className="bg-card border-border border">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-emerald-700 dark:text-emerald-400">
|
||||
Summary
|
||||
</CardTitle>
|
||||
<CardTitle className="text-primary">Summary</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-3">
|
||||
@@ -471,14 +463,14 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
<Separator />
|
||||
<div className="flex justify-between text-lg font-bold">
|
||||
<span className="dark:text-white">Total</span>
|
||||
<span className="text-emerald-600 dark:text-emerald-400">
|
||||
<span className="text-primary">
|
||||
{formatCurrency(invoice.totalAmount)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<div className="border-border border-t pt-4 text-center">
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{invoice.items?.length ?? 0} item
|
||||
{invoice.items?.length !== 1 ? "s" : ""}
|
||||
</p>
|
||||
@@ -487,17 +479,15 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
</Card>
|
||||
|
||||
{/* Danger Zone */}
|
||||
<Card className="card-primary border-red-200 dark:border-red-800">
|
||||
<Card className="bg-card border-border border border-destructive/20">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-red-700 dark:text-red-400">
|
||||
Danger Zone
|
||||
</CardTitle>
|
||||
<CardTitle className="text-destructive">Danger Zone</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Button
|
||||
onClick={handleDelete}
|
||||
variant="outline"
|
||||
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"
|
||||
className="border-destructive/20 text-destructive hover:bg-destructive/10 w-full"
|
||||
>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
Delete Invoice
|
||||
@@ -509,7 +499,7 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
||||
<DialogContent className="card-primary">
|
||||
<DialogContent className="bg-card border-border border">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-xl font-bold text-gray-800 dark:text-white">
|
||||
Delete Invoice
|
||||
@@ -524,7 +514,7 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setDeleteDialogOpen(false)}
|
||||
className="border-gray-300 text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
|
||||
className="border-border text-muted-foreground hover:bg-muted"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
@@ -532,7 +522,7 @@ export function InvoiceView({ invoiceId }: InvoiceViewProps) {
|
||||
variant="destructive"
|
||||
onClick={confirmDelete}
|
||||
disabled={deleteInvoice.isPending}
|
||||
className="bg-red-600 hover:bg-red-700"
|
||||
className="bg-destructive hover:bg-destructive/90"
|
||||
>
|
||||
{deleteInvoice.isPending ? "Deleting..." : "Delete Invoice"}
|
||||
</Button>
|
||||
|
||||
@@ -22,8 +22,8 @@ const variantStyles = {
|
||||
background: "bg-muted/50",
|
||||
},
|
||||
success: {
|
||||
icon: "text-status-success",
|
||||
background: "bg-status-success-muted",
|
||||
icon: "text-primary",
|
||||
background: "bg-primary/10",
|
||||
},
|
||||
warning: {
|
||||
icon: "text-status-warning",
|
||||
@@ -67,9 +67,7 @@ export function StatsCard({
|
||||
<span
|
||||
className={cn(
|
||||
"text-sm font-medium",
|
||||
trend.isPositive
|
||||
? "text-status-success"
|
||||
: "text-status-error",
|
||||
trend.isPositive ? "text-primary" : "text-destructive",
|
||||
)}
|
||||
>
|
||||
{trend.isPositive ? "+" : ""}
|
||||
@@ -82,7 +80,7 @@ export function StatsCard({
|
||||
)}
|
||||
</div>
|
||||
{Icon && (
|
||||
<div className={cn("rounded-full p-3", styles.background)}>
|
||||
<div className={cn(" p-3", styles.background)}>
|
||||
<Icon className={cn("h-6 w-6", styles.icon)} />
|
||||
</div>
|
||||
)}
|
||||
@@ -94,7 +92,7 @@ export function StatsCard({
|
||||
|
||||
export function StatsCardSkeleton() {
|
||||
return (
|
||||
<Card className="card-primary">
|
||||
<Card className="bg-card border-border border">
|
||||
<CardContent className="p-6">
|
||||
<div className="animate-pulse">
|
||||
<div className="bg-muted mb-2 h-4 w-1/2 rounded"></div>
|
||||
|
||||
@@ -19,19 +19,15 @@ interface StatusBadgeProps
|
||||
}
|
||||
|
||||
const statusClassMap: Record<StatusType, string> = {
|
||||
draft:
|
||||
"border-slate-400 bg-slate-100/90 text-slate-800 shadow-md dark:border-slate-600 dark:bg-slate-700/90 dark:text-slate-200",
|
||||
sent: "border-blue-400 bg-blue-100/90 text-blue-800 shadow-md dark:border-blue-600 dark:bg-blue-700/90 dark:text-blue-200",
|
||||
paid: "border-green-400 bg-green-100/90 text-green-800 shadow-md dark:border-green-600 dark:bg-green-700/90 dark:text-green-200",
|
||||
overdue:
|
||||
"border-red-400 bg-red-100/90 text-red-800 shadow-md dark:border-red-600 dark:bg-red-700/90 dark:text-red-200",
|
||||
success:
|
||||
"border-green-400 bg-green-100/90 text-green-800 shadow-md dark:border-green-600 dark:bg-green-700/90 dark:text-green-200",
|
||||
draft: "border-muted-foreground/40 bg-muted text-muted-foreground shadow-sm",
|
||||
sent: "border-primary/40 bg-primary/10 text-primary shadow-sm",
|
||||
paid: "border-primary/40 bg-primary/10 text-primary shadow-sm",
|
||||
overdue: "border-destructive/40 bg-destructive/10 text-destructive shadow-sm",
|
||||
success: "border-primary/40 bg-primary/10 text-primary shadow-sm",
|
||||
warning:
|
||||
"border-yellow-400 bg-yellow-100/90 text-yellow-800 shadow-md dark:border-yellow-600 dark:bg-yellow-700/90 dark:text-yellow-200",
|
||||
error:
|
||||
"border-red-400 bg-red-100/90 text-red-800 shadow-md dark:border-red-600 dark:bg-red-700/90 dark:text-red-200",
|
||||
info: "border-blue-400 bg-blue-100/90 text-blue-800 shadow-md dark:border-blue-600 dark:bg-blue-700/90 dark:text-blue-200",
|
||||
"border-muted-foreground/40 bg-muted text-muted-foreground shadow-sm",
|
||||
error: "border-destructive/40 bg-destructive/10 text-destructive shadow-sm",
|
||||
info: "border-primary/40 bg-primary/10 text-primary shadow-sm",
|
||||
};
|
||||
|
||||
const statusLabelMap: Record<StatusType, string> = {
|
||||
|
||||
Reference in New Issue
Block a user