Update date picker, mobile styling

This commit is contained in:
2025-07-16 03:27:56 -04:00
parent 76711d2c10
commit c6fa9c4ac1
41 changed files with 3522 additions and 1431 deletions
+43 -61
View File
@@ -49,7 +49,7 @@ export default async function BusinessDetailPage({
variant="gradient"
>
<Link href={`/dashboard/businesses/${business.id}/edit`}>
<Button className="bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-700 hover:to-teal-700">
<Button variant="brand">
<Edit className="mr-2 h-4 w-4" />
Edit Business
</Button>
@@ -59,9 +59,9 @@ export default async function BusinessDetailPage({
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
{/* Business Information Card */}
<div className="lg:col-span-2">
<Card className="border-0 shadow-xl backdrop-blur-sm">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center space-x-2 text-green-600">
<CardTitle className="card-title-success">
<Building className="h-5 w-5" />
<span>Business Information</span>
</CardTitle>
@@ -71,50 +71,42 @@ export default async function BusinessDetailPage({
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
{business.email && (
<div className="flex items-center space-x-3">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
<Mail className="h-4 w-4 text-emerald-600" />
<div className="icon-bg-emerald">
<Mail className="text-icon-emerald h-4 w-4" />
</div>
<div>
<p className="text-muted-foreground text-sm font-medium">
Email
</p>
<p className="text-foreground text-sm">
{business.email}
</p>
<p className="text-muted text-sm font-medium">Email</p>
<p className="text-accent text-sm">{business.email}</p>
</div>
</div>
)}
{business.phone && (
<div className="flex items-center space-x-3">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
<Phone className="h-4 w-4 text-emerald-600" />
<div className="icon-bg-emerald">
<Phone className="text-icon-emerald h-4 w-4" />
</div>
<div>
<p className="text-muted-foreground text-sm font-medium">
Phone
</p>
<p className="text-foreground text-sm">
{business.phone}
</p>
<p className="text-muted text-sm font-medium">Phone</p>
<p className="text-accent text-sm">{business.phone}</p>
</div>
</div>
)}
{business.website && (
<div className="flex items-center space-x-3">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
<Globe className="h-4 w-4 text-emerald-600" />
<div className="icon-bg-emerald">
<Globe className="text-icon-emerald h-4 w-4" />
</div>
<div>
<p className="text-muted-foreground text-sm font-medium">
<p className="text-muted text-sm font-medium">
Website
</p>
<a
href={business.website}
target="_blank"
rel="noopener noreferrer"
className="text-foreground text-sm hover:text-emerald-600 hover:underline"
className="link-primary text-sm"
>
{business.website}
</a>
@@ -124,16 +116,12 @@ export default async function BusinessDetailPage({
{business.taxId && (
<div className="flex items-center space-x-3">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
<Hash className="h-4 w-4 text-emerald-600" />
<div className="icon-bg-emerald">
<Hash className="text-icon-emerald h-4 w-4" />
</div>
<div>
<p className="text-muted-foreground text-sm font-medium">
Tax ID
</p>
<p className="text-foreground text-sm">
{business.taxId}
</p>
<p className="text-muted text-sm font-medium">Tax ID</p>
<p className="text-accent text-sm">{business.taxId}</p>
</div>
</div>
)}
@@ -143,19 +131,21 @@ export default async function BusinessDetailPage({
{(business.addressLine1 ?? business.city ?? business.state) && (
<div className="space-y-4">
<div className="flex items-center space-x-3">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
<MapPin className="h-4 w-4 text-emerald-600" />
<div className="icon-bg-emerald">
<MapPin className="text-icon-emerald h-4 w-4" />
</div>
<div>
<p className="text-muted-foreground text-sm font-medium">
<p className="text-muted text-sm font-medium">
Address
</p>
</div>
</div>
<div className="text-foreground ml-11 space-y-1 text-sm">
<div className="text-accent ml-11 space-y-1 text-sm">
{business.addressLine1 && <p>{business.addressLine1}</p>}
{business.addressLine2 && <p>{business.addressLine2}</p>}
{(business.city ?? business.state ?? business.postalCode) && (
{(business.city ??
business.state ??
business.postalCode) && (
<p>
{[business.city, business.state, business.postalCode]
.filter(Boolean)
@@ -169,14 +159,14 @@ export default async function BusinessDetailPage({
{/* Business Since */}
<div className="flex items-center space-x-3">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
<Calendar className="h-4 w-4 text-emerald-600" />
<div className="icon-bg-emerald">
<Calendar className="text-icon-emerald h-4 w-4" />
</div>
<div>
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
<p className="text-muted text-sm font-medium">
Business Added
</p>
<p className="text-sm dark:text-gray-300">
<p className="text-secondary text-sm">
{formatDate(business.createdAt)}
</p>
</div>
@@ -185,16 +175,12 @@ export default async function BusinessDetailPage({
{/* Default Business Badge */}
{business.isDefault && (
<div className="flex items-center space-x-3">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
<Building className="h-4 w-4 text-emerald-600" />
<div className="icon-bg-emerald">
<Building className="text-icon-emerald h-4 w-4" />
</div>
<div>
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
Status
</p>
<Badge className="bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400">
Default Business
</Badge>
<p className="text-muted text-sm font-medium">Status</p>
<Badge className="badge-success">Default Business</Badge>
</div>
</div>
)}
@@ -204,23 +190,19 @@ export default async function BusinessDetailPage({
{/* Settings & Actions Card */}
<div className="space-y-6">
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
<Card className="card-secondary">
<CardHeader>
<CardTitle className="flex items-center space-x-2 text-emerald-700 dark:text-emerald-400">
<CardTitle className="card-title-primary">
<Building className="h-5 w-5" />
<span>Business Settings</span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="text-center">
<p className="text-sm text-gray-500 dark:text-gray-400">
Default Business
</p>
<p className="text-muted text-sm">Default Business</p>
<p className="text-lg font-semibold">
{business.isDefault ? (
<Badge className="bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400">
Yes
</Badge>
<Badge className="badge-success">Yes</Badge>
) : (
<Badge variant="outline">No</Badge>
)}
@@ -228,7 +210,7 @@ export default async function BusinessDetailPage({
</div>
<div className="space-y-3">
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
<p className="text-muted text-sm font-medium">
Quick Actions
</p>
<div className="space-y-2">
@@ -258,20 +240,20 @@ export default async function BusinessDetailPage({
</Card>
{/* Information Card */}
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
<Card className="card-secondary">
<CardHeader>
<CardTitle className="text-lg dark:text-white">
<CardTitle className="text-accent text-lg">
About This Business
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3 text-sm text-gray-600 dark:text-gray-300">
<div className="text-secondary space-y-3 text-sm">
<p>
This business profile is used for generating invoices and
represents your company information to clients.
</p>
{business.isDefault && (
<p className="text-emerald-600 dark:text-emerald-400">
<p className="text-icon-emerald">
This is your default business and will be automatically
selected when creating new invoices.
</p>
+36 -59
View File
@@ -72,9 +72,9 @@ export default async function ClientDetailPage({
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
{/* Client Information Card */}
<div className="lg:col-span-2">
<Card className="border-0 shadow-xl backdrop-blur-sm">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center space-x-2 text-green-600">
<CardTitle className="client-section-title">
<Building className="h-5 w-5" />
<span>Contact Information</span>
</CardTitle>
@@ -83,33 +83,25 @@ export default async function ClientDetailPage({
{/* Basic Info */}
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
{client.email && (
<div className="flex items-center space-x-3">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
<Mail className="h-4 w-4 text-emerald-600" />
<div className="client-info-item">
<div className="client-info-icon">
<Mail className="client-info-icon-emerald" />
</div>
<div>
<p className="text-muted-foreground text-sm font-medium">
Email
</p>
<p className="text-foreground text-sm">
{client.email}
</p>
<p className="client-info-label">Email</p>
<p className="client-info-value">{client.email}</p>
</div>
</div>
)}
{client.phone && (
<div className="flex items-center space-x-3">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
<Phone className="h-4 w-4 text-emerald-600" />
<div className="client-info-item">
<div className="client-info-icon">
<Phone className="client-info-icon-emerald" />
</div>
<div>
<p className="text-muted-foreground text-sm font-medium">
Phone
</p>
<p className="text-foreground text-sm">
{client.phone}
</p>
<p className="client-info-label">Phone</p>
<p className="client-info-value">{client.phone}</p>
</div>
</div>
)}
@@ -118,17 +110,15 @@ export default async function ClientDetailPage({
{/* Address */}
{(client.addressLine1 ?? client.city ?? client.state) && (
<div className="space-y-4">
<div className="flex items-center space-x-3">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
<MapPin className="h-4 w-4 text-emerald-600" />
<div className="client-info-item">
<div className="client-info-icon">
<MapPin className="client-info-icon-emerald" />
</div>
<div>
<p className="text-muted-foreground text-sm font-medium">
Address
</p>
<p className="client-info-label">Address</p>
</div>
</div>
<div className="text-foreground ml-11 space-y-1 text-sm">
<div className="client-address-content">
{client.addressLine1 && <p>{client.addressLine1}</p>}
{client.addressLine2 && <p>{client.addressLine2}</p>}
{(client.city ?? client.state ?? client.postalCode) && (
@@ -144,15 +134,13 @@ export default async function ClientDetailPage({
)}
{/* Client Since */}
<div className="flex items-center space-x-3">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/30">
<Calendar className="h-4 w-4 text-emerald-600" />
<div className="client-info-item">
<div className="client-info-icon">
<Calendar className="client-info-icon-emerald" />
</div>
<div>
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">
Client Since
</p>
<p className="text-sm dark:text-gray-300">
<p className="client-info-label">Client Since</p>
<p className="client-info-value">
{formatDate(client.createdAt)}
</p>
</div>
@@ -163,39 +151,31 @@ export default async function ClientDetailPage({
{/* Stats Card */}
<div className="space-y-6">
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center space-x-2 text-emerald-700 dark:text-emerald-400">
<CardTitle className="client-stats-title">
<DollarSign className="h-5 w-5" />
<span>Invoice Summary</span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="text-center">
<p className="text-2xl font-bold text-emerald-600">
<p className="client-total-amount">
{formatCurrency(totalInvoiced)}
</p>
<p className="text-sm text-gray-500 dark:text-gray-400">
Total Invoiced
</p>
<p className="client-total-label">Total Invoiced</p>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="client-stats-grid">
<div className="text-center">
<p className="text-lg font-semibold text-green-600">
{paidInvoices}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
Paid
</p>
<p className="client-stat-value-paid">{paidInvoices}</p>
<p className="client-stat-label">Paid</p>
</div>
<div className="text-center">
<p className="text-lg font-semibold text-orange-600">
<p className="client-stat-value-pending">
{pendingInvoices}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
Pending
</p>
<p className="client-stat-label">Pending</p>
</div>
</div>
</CardContent>
@@ -203,7 +183,7 @@ export default async function ClientDetailPage({
{/* Recent Invoices */}
{client.invoices && client.invoices.length > 0 && (
<Card className="border-0 bg-white/80 shadow-xl backdrop-blur-sm dark:bg-gray-800/80">
<Card className="card-primary">
<CardHeader>
<CardTitle className="text-lg dark:text-white">
Recent Invoices
@@ -212,20 +192,17 @@ export default async function ClientDetailPage({
<CardContent>
<div className="space-y-3">
{client.invoices.slice(0, 3).map((invoice) => (
<div
key={invoice.id}
className="flex items-center justify-between rounded-lg bg-gray-50 p-3 dark:bg-gray-700"
>
<div key={invoice.id} className="invoice-item">
<div>
<p className="text-sm font-medium dark:text-white">
<p className="invoice-item-title">
{invoice.invoiceNumber}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
<p className="invoice-item-date">
{formatDate(invoice.issueDate)}
</p>
</div>
<div className="text-right">
<p className="text-sm font-medium dark:text-white">
<p className="invoice-item-amount">
{formatCurrency(invoice.totalAmount)}
</p>
<Badge
@@ -1,64 +0,0 @@
"use client";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu";
import { Button } from "~/components/ui/button";
import {
MoreHorizontal,
Edit,
Copy,
Send,
Trash2,
} from "lucide-react";
interface InvoiceActionsDropdownProps {
invoiceId: string;
}
export function InvoiceActionsDropdown({ invoiceId }: InvoiceActionsDropdownProps) {
const handleSendClick = () => {
const sendButton = document.querySelector(
"[data-testid='send-invoice-button']",
) as HTMLButtonElement;
sendButton?.click();
};
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="icon"
className="border-0 shadow-sm"
>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuItem>
<Edit className="mr-2 h-4 w-4" />
Edit Invoice
</DropdownMenuItem>
<DropdownMenuItem>
<Copy className="mr-2 h-4 w-4" />
Duplicate
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleSendClick}>
<Send className="mr-2 h-4 w-4" />
Send to Client
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="text-red-600">
<Trash2 className="mr-2 h-4 w-4" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
@@ -66,7 +66,7 @@ const columns: ColumnDef<InvoiceItem>[] = [
accessorKey: "amount",
header: "Amount",
cell: ({ row }) => (
<div className="text-right font-medium text-emerald-600">
<div className="text-icon-emerald text-right font-medium">
{formatCurrency(row.getValue("amount"))}
</div>
),
+33 -30
View File
@@ -85,7 +85,7 @@ async function InvoiceContent({ invoiceId }: { invoiceId: string }) {
{/* Left Column */}
<div className="space-y-6 lg:col-span-2">
{/* Invoice Header */}
<Card className="shadow-sm">
<Card className="card-primary">
<CardContent className="p-4 sm:p-6">
<div className="space-y-4">
<div className="flex items-start justify-between gap-6">
@@ -120,7 +120,7 @@ async function InvoiceContent({ invoiceId }: { invoiceId: string }) {
{/* Overdue Alert */}
{isOverdue && (
<Card className="border-destructive/20 bg-destructive/5 shadow-sm">
<Card className="border-destructive/20 bg-destructive/5 card-secondary">
<CardContent className="p-4">
<div className="text-destructive flex items-center gap-3">
<AlertTriangle className="h-5 w-5 flex-shrink-0" />
@@ -143,7 +143,7 @@ async function InvoiceContent({ invoiceId }: { invoiceId: string }) {
{/* Client & Business Info */}
<div className="grid gap-4 sm:grid-cols-2">
{/* Client Information */}
<Card className="shadow-sm">
<Card className="card-primary">
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2">
<User className="h-5 w-5" />
@@ -215,7 +215,7 @@ async function InvoiceContent({ invoiceId }: { invoiceId: string }) {
{/* Business Information */}
{invoice.business && (
<Card className="shadow-sm">
<Card className="card-primary">
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2">
<Building className="h-5 w-5" />
@@ -258,7 +258,7 @@ async function InvoiceContent({ invoiceId }: { invoiceId: string }) {
</div>
{/* Invoice Items */}
<Card className="shadow-sm">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<FileText className="h-5 w-5" />
@@ -267,31 +267,34 @@ async function InvoiceContent({ invoiceId }: { invoiceId: string }) {
</CardHeader>
<CardContent className="space-y-4">
{invoice.items.map((item) => (
<div key={item.id} className="space-y-3 rounded-lg border p-4">
<div className="flex items-start justify-between gap-4">
<div className="min-w-0 flex-1">
<p className="text-foreground mb-2 text-base font-medium">
{item.description}
</p>
<div className="text-muted-foreground space-y-1 text-sm sm:space-y-0">
<span className="sm:inline">
{formatDate(item.date)}
</span>
<span className="block sm:inline sm:before:content-['_•_']">
{item.hours} hours
</span>
<span className="block sm:inline sm:before:content-['_•_']">
@ ${item.rate}/hr
</span>
<Card key={item.id} className="card-secondary">
<CardContent className="py-2">
<div className="flex items-start justify-between gap-4">
<div className="min-w-0 flex-1">
<p className="text-foreground mb-2 text-base font-medium">
{item.description}
</p>
<div className="text-muted-foreground text-sm">
<span className="inline whitespace-nowrap">
{formatDate(item.date).replace(/ /g, "\u00A0")}
</span>
<span className="inline whitespace-nowrap before:mx-2 before:content-['_|_']">
{item.hours.toString().replace(/ /g, "\u00A0")}
&nbsp;hours
</span>
<span className="inline whitespace-nowrap before:mx-2 before:content-['_|_']">
@&nbsp;${item.rate}/hr
</span>
</div>
</div>
<div className="flex-shrink-0 text-right">
<p className="text-primary text-lg font-semibold">
{formatCurrency(item.amount)}
</p>
</div>
</div>
<div className="flex-shrink-0 text-right">
<p className="text-primary text-lg font-semibold">
{formatCurrency(item.amount)}
</p>
</div>
</div>
</div>
</CardContent>
</Card>
))}
{/* Totals */}
@@ -327,7 +330,7 @@ async function InvoiceContent({ invoiceId }: { invoiceId: string }) {
{/* Notes */}
{invoice.notes && (
<Card className="shadow-sm">
<Card className="card-primary">
<CardHeader>
<CardTitle>Notes</CardTitle>
</CardHeader>
@@ -342,7 +345,7 @@ async function InvoiceContent({ invoiceId }: { invoiceId: string }) {
{/* Right Column - Actions */}
<div className="space-y-6">
<Card className="sticky top-6 shadow-sm">
<Card className="card-primary sticky top-6">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Check className="h-5 w-5" />
+41 -54
View File
@@ -64,7 +64,7 @@ function ImportStats() {
return (
<Card
key={stat.title}
className="border-0 shadow-md transition-shadow hover:shadow-lg"
className="card-primary transition-shadow hover:shadow-lg"
>
<CardContent className="p-6">
<div className="flex items-center justify-between">
@@ -92,19 +92,19 @@ function ImportStats() {
// File Upload Component
function FileUploadArea() {
return (
<Card className="border-0 shadow-lg">
<Card className="card-primary">
<CardHeader className="border-b">
<CardTitle className="flex items-center gap-2 text-lg">
<Upload className="h-5 w-5 text-emerald-600" />
<CardTitle className="card-title-secondary">
<Upload className="text-icon-emerald h-5 w-5" />
Upload CSV File
</CardTitle>
</CardHeader>
<CardContent className="p-8">
<div className="mx-auto max-w-xl">
{/* Drop Zone */}
<div className="rounded-lg border-2 border-dashed border-emerald-300 bg-emerald-50/50 p-12 text-center transition-colors hover:border-emerald-400 hover:bg-emerald-50 dark:border-emerald-700 dark:bg-emerald-900/10 dark:hover:bg-emerald-900/20">
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-emerald-100 dark:bg-emerald-900/30">
<Upload className="h-8 w-8 text-emerald-600" />
<div className="bg-upload-zone">
<div className="bg-brand-muted mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full">
<Upload className="text-icon-emerald h-8 w-8" />
</div>
<h3 className="mb-2 text-lg font-semibold">
Drop your CSV file here
@@ -112,10 +112,7 @@ function FileUploadArea() {
<p className="text-muted-foreground mb-4">
or click to browse and select a file
</p>
<Button
type="button"
className="bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-700 hover:to-teal-700"
>
<Button type="button" className="btn-brand-primary">
<Upload className="mr-2 h-4 w-4" />
Choose File
</Button>
@@ -128,11 +125,11 @@ function FileUploadArea() {
<div className="mt-6 hidden">
<div className="mb-2 flex items-center justify-between">
<span className="text-sm font-medium">Uploading...</span>
<span className="text-sm text-emerald-600">75%</span>
<span className="text-icon-emerald text-sm">75%</span>
</div>
<div className="h-2 overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700">
<div className="bg-progress-track">
<div
className="h-full bg-gradient-to-r from-emerald-600 to-teal-600 transition-all duration-300"
className="bg-brand-gradient h-full transition-all duration-300"
style={{ width: "75%" }}
></div>
</div>
@@ -148,16 +145,16 @@ function FormatInstructions() {
return (
<div className="grid gap-6 lg:grid-cols-2">
{/* Required Format */}
<Card className="border-0 shadow-lg">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<FileText className="h-5 w-5 text-blue-600" />
<CardTitle className="card-title-info">
<FileText className="text-icon-blue h-5 w-5" />
Required CSV Format
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="rounded-lg bg-gray-50 p-4 dark:bg-gray-800/50">
<p className="font-mono text-sm text-gray-700 dark:text-gray-300">
<div className="bg-muted-subtle rounded-lg p-4">
<p className="text-secondary font-mono text-sm">
client_name,client_email,invoice_number,issue_date,due_date,description,hours,rate,tax_rate
</p>
</div>
@@ -176,9 +173,7 @@ function FormatInstructions() {
{ field: "rate", desc: "Hourly rate (decimal)" },
].map((col) => (
<div key={col.field} className="flex items-start gap-3">
<Badge variant="outline" className="text-xs">
{col.field}
</Badge>
<Badge className="badge-outline text-xs">{col.field}</Badge>
<span className="text-muted-foreground text-sm">
{col.desc}
</span>
@@ -190,25 +185,19 @@ function FormatInstructions() {
<div className="pt-2">
<h4 className="mb-2 font-semibold">Optional Columns:</h4>
<div className="flex flex-wrap gap-2">
<Badge variant="secondary" className="text-xs">
tax_rate
</Badge>
<Badge variant="secondary" className="text-xs">
notes
</Badge>
<Badge variant="secondary" className="text-xs">
client_phone
</Badge>
<Badge className="badge-secondary text-xs">tax_rate</Badge>
<Badge className="badge-secondary text-xs">notes</Badge>
<Badge className="badge-secondary text-xs">client_phone</Badge>
</div>
</div>
</CardContent>
</Card>
{/* Sample Data & Download */}
<Card className="border-0 shadow-lg">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<Download className="h-5 w-5 text-green-600" />
<CardTitle className="card-title-secondary">
<Download className="text-icon-green h-5 w-5" />
Sample Template
</CardTitle>
</CardHeader>
@@ -218,14 +207,12 @@ function FormatInstructions() {
for importing invoices.
</p>
<div className="rounded-lg bg-green-50 p-4 dark:bg-green-900/20">
<div className="bg-green-subtle rounded-lg p-4">
<div className="flex items-start gap-3">
<Info className="mt-0.5 h-5 w-5 text-green-600" />
<Info className="text-icon-green mt-0.5 h-5 w-5" />
<div>
<p className="text-sm font-medium text-green-800 dark:text-green-400">
Pro Tip
</p>
<p className="text-sm text-green-700 dark:text-green-300">
<p className="text-success text-sm font-medium">Pro Tip</p>
<p className="text-success text-sm">
The template includes sample data and formatting examples to
help you get started quickly.
</p>
@@ -249,8 +236,8 @@ function FormatInstructions() {
<div className="space-y-2">
<h4 className="text-sm font-semibold">Sample Row:</h4>
<div className="rounded-lg bg-gray-50 p-3 dark:bg-gray-800/50">
<p className="font-mono text-xs break-all text-gray-600 dark:text-gray-400">
<div className="bg-muted-subtle rounded-lg p-3">
<p className="text-muted font-mono text-xs break-all">
"Acme
Corp","john@acme.com","INV-001","2024-01-15","2024-02-14","Web
development work","40","75.00","8.5"
@@ -266,10 +253,10 @@ function FormatInstructions() {
// Important Notes Section
function ImportantNotes() {
return (
<Card className="border-0 border-l-4 border-l-amber-500 shadow-lg">
<Card className="card-primary border-l-4 border-l-amber-500">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<AlertCircle className="h-5 w-5 text-amber-600" />
<CardTitle className="card-title-warning">
<AlertCircle className="text-icon-amber h-5 w-5" />
Important Notes
</CardTitle>
</CardHeader>
@@ -331,7 +318,7 @@ function ImportHistory() {
const getStatusBadge = (status: string) => {
if (status === "completed") {
return (
<Badge className="bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400">
<Badge className="badge-success">
<CheckCircle className="mr-1 h-3 w-3" />
Completed
</Badge>
@@ -339,7 +326,7 @@ function ImportHistory() {
}
if (status === "processing") {
return (
<Badge className="bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400">
<Badge className="badge-features">
<RefreshCw className="mr-1 h-3 w-3" />
Processing
</Badge>
@@ -354,10 +341,10 @@ function ImportHistory() {
};
return (
<Card className="border-0 shadow-lg">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<FileText className="h-5 w-5 text-purple-600" />
<CardTitle className="card-title-purple">
<FileText className="text-icon-purple h-5 w-5" />
Recent Imports
</CardTitle>
</CardHeader>
@@ -382,8 +369,8 @@ function ImportHistory() {
>
<td className="p-4">
<div className="flex items-center gap-3">
<div className="rounded-lg bg-purple-100 p-2 dark:bg-purple-900/30">
<FileSpreadsheet className="h-4 w-4 text-purple-600" />
<div className="icon-bg-purple-muted">
<FileSpreadsheet className="text-icon-purple h-4 w-4" />
</div>
<span className="font-medium">{item.filename}</span>
</div>
@@ -397,7 +384,7 @@ function ImportHistory() {
</td>
<td className="p-4 text-right">
{item.errors > 0 ? (
<span className="text-red-600">{item.errors}</span>
<span className="status-text-error">{item.errors}</span>
) : (
<span className="text-muted-foreground">0</span>
)}
@@ -443,7 +430,7 @@ export default async function ImportPage() {
fallback={
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{[...Array(4)].map((_, i) => (
<Card key={i} className="border-0 shadow-md">
<Card key={i} className="card-primary">
<CardContent className="p-6">
<div className="animate-pulse">
<div className="bg-muted mb-2 h-4 w-1/2 rounded"></div>
+60 -58
View File
@@ -94,7 +94,7 @@ function InvoiceItemCard({
};
return (
<Card className="border-border/50 border p-3 shadow-sm">
<Card className="card-secondary">
<div className="space-y-3">
{/* Header with item number and delete */}
<div className="flex items-center justify-between">
@@ -106,7 +106,7 @@ function InvoiceItemCard({
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0 text-red-500 hover:text-red-700"
className="text-icon-red hover:text-error h-6 w-6 p-0"
>
<Trash2 className="h-3 w-3" />
</Button>
@@ -123,7 +123,7 @@ function InvoiceItemCard({
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={() => onDelete(index)}
className="bg-red-600 hover:bg-red-700"
className="btn-danger"
>
Delete
</AlertDialogAction>
@@ -142,7 +142,7 @@ function InvoiceItemCard({
/>
{/* Date, Hours, Rate, Amount in compact grid */}
<div className="grid grid-cols-2 gap-2 text-sm sm:grid-cols-4">
<div className="grid grid-cols-2 gap-2 text-sm md:grid-cols-4">
<div className="space-y-1">
<Label className="text-xs font-medium">Date</Label>
<DatePicker
@@ -150,7 +150,8 @@ function InvoiceItemCard({
onDateChange={(date) =>
handleFieldChange("date", date ?? new Date())
}
className="[&>button]:h-8 [&>button]:text-xs"
size="sm"
className="w-full"
/>
</div>
<div className="space-y-1">
@@ -161,7 +162,6 @@ function InvoiceItemCard({
min={0}
step={0.25}
placeholder="0"
className="text-xs"
/>
</div>
<div className="space-y-1">
@@ -173,13 +173,12 @@ function InvoiceItemCard({
step={0.25}
placeholder="0.00"
prefix="$"
className="text-xs"
/>
</div>
<div className="space-y-1">
<Label className="text-xs font-medium">Amount</Label>
<div className="bg-muted/30 flex h-8 items-center rounded-md border px-2">
<span className="font-mono text-xs font-medium text-emerald-600">
<span className="amount-primary">
${(item.hours * item.rate).toFixed(2)}
</span>
</div>
@@ -377,9 +376,9 @@ export default function NewInvoicePage() {
description="Loading form data..."
variant="gradient"
/>
<Card className="shadow-xl">
<Card className="card-primary">
<CardContent className="flex items-center justify-center p-8">
<Loader2 className="h-8 w-8 animate-spin text-emerald-600" />
<Loader2 className="text-icon-emerald h-8 w-8 animate-spin" />
</CardContent>
</Card>
</div>
@@ -394,25 +393,25 @@ export default function NewInvoicePage() {
variant="gradient"
>
<Link href="/dashboard/invoices">
<Button variant="outline" size="sm" className="w-full sm:w-auto">
<Button variant="outline" size="sm" className="w-full md:w-auto">
<ArrowLeft className="mr-2 h-4 w-4" />
<span className="hidden sm:inline">Back to Invoices</span>
<span className="sm:hidden">Back</span>
<span className="hidden md:inline">Back to Invoices</span>
<span className="md:hidden">Back</span>
</Button>
</Link>
</PageHeader>
<div className="space-y-6">
{/* Invoice Header */}
<Card className="shadow-lg">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<FileText className="h-5 w-5 text-emerald-600" />
<CardTitle className="card-title-secondary">
<FileText className="text-icon-emerald h-5 w-5" />
Invoice Details
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
<div className="space-y-2">
<Label className="text-sm font-medium">Invoice Number</Label>
<div className="bg-muted/30 flex h-10 items-center rounded-md border px-3">
@@ -453,15 +452,15 @@ export default function NewInvoicePage() {
</Card>
{/* Business & Client */}
<Card className="shadow-lg">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Building className="h-5 w-5 text-emerald-600" />
<CardTitle className="card-title-secondary">
<Building className="text-icon-emerald h-5 w-5" />
Business & Client
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-2">
<Label className="text-sm font-medium">From Business</Label>
<div className="relative">
@@ -484,7 +483,7 @@ export default function NewInvoicePage() {
<div className="flex items-center gap-2">
<span>{business.name}</span>
{business.isDefault && (
<Badge variant="secondary" className="text-xs">
<Badge className="badge-secondary text-xs">
Default
</Badge>
)}
@@ -495,11 +494,11 @@ export default function NewInvoicePage() {
</Select>
</div>
{(!businesses || businesses.length === 0) && (
<p className="text-sm text-red-600">
<p className="text-icon-red text-sm">
No businesses found.{" "}
<Link
href="/dashboard/businesses/new"
className="underline hover:text-red-700"
className="link-secondary"
>
Create one first
</Link>
@@ -551,7 +550,7 @@ export default function NewInvoicePage() {
</Card>
{/* Line Items */}
<Card className="shadow-lg">
<Card className="card-primary">
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle className="flex items-center gap-2">
@@ -565,8 +564,8 @@ export default function NewInvoicePage() {
size="sm"
className="shrink-0"
>
<Plus className="h-4 w-4 sm:mr-2" />
<span className="hidden sm:inline">Add Item</span>
<Plus className="h-4 w-4 md:mr-2" />
<span className="hidden md:inline">Add Item</span>
</Button>
</div>
</CardHeader>
@@ -585,7 +584,7 @@ export default function NewInvoicePage() {
</Card>
{/* Tax & Totals */}
<Card className="shadow-lg">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<DollarSign className="h-5 w-5 text-emerald-600" />
@@ -595,22 +594,25 @@ export default function NewInvoicePage() {
<CardContent className="space-y-6">
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
<div className="space-y-4">
<div className="space-y-2">
<Label className="text-sm font-medium">Tax Rate (%)</Label>
<NumberInput
value={formData.taxRate}
onChange={(value) =>
setFormData({
...formData,
taxRate: value,
})
}
min={0}
max={100}
step={0.01}
placeholder="0.00"
suffix="%"
/>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="space-y-2 md:col-span-1">
<Label className="text-sm font-medium">Tax Rate (%)</Label>
<NumberInput
value={formData.taxRate}
onChange={(value) =>
setFormData({
...formData,
taxRate: value,
})
}
min={0}
max={100}
step={0.01}
placeholder="0.00"
suffix="%"
width="full"
/>
</div>
</div>
<div className="space-y-2">
@@ -659,21 +661,21 @@ export default function NewInvoicePage() {
{/* Action Buttons */}
<div
ref={footerRef}
className="flex flex-col gap-3 border-t pt-6 sm:flex-row sm:justify-between"
className="flex flex-col gap-3 border-t pt-6 md:flex-row md:justify-between"
>
<Link href="/dashboard/invoices">
<Button variant="outline" className="w-full sm:w-auto">
<Button variant="outline" className="w-full md:w-auto">
<ArrowLeft className="mr-2 h-4 w-4" />
Cancel
</Button>
</Link>
<div className="flex flex-col gap-3 sm:flex-row sm:items-center">
<div className="flex flex-col gap-3 md:flex-row md:items-center">
<Button
onClick={handleSaveDraft}
disabled={isLoading || !isFormValid()}
variant="outline"
className="w-full sm:w-auto"
className="w-full md:w-auto"
>
{isLoading ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
@@ -685,7 +687,7 @@ export default function NewInvoicePage() {
<Button
onClick={handleCreateInvoice}
disabled={isLoading || !isFormValid()}
className="w-full bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-700 hover:to-teal-700 sm:w-auto"
className="btn-brand-primary w-full md:w-auto"
>
{isLoading ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
@@ -708,8 +710,8 @@ export default function NewInvoicePage() {
className="border-border/40 hover:bg-accent/50"
size="sm"
>
<ArrowLeft className="h-4 w-4 sm:mr-2" />
<span className="hidden sm:inline">Cancel</span>
<ArrowLeft className="h-4 w-4 md:mr-2" />
<span className="hidden md:inline">Cancel</span>
</Button>
</Link>
<Button
@@ -720,24 +722,24 @@ export default function NewInvoicePage() {
size="sm"
>
{isLoading ? (
<Loader2 className="h-4 w-4 animate-spin sm:mr-2" />
<Loader2 className="h-4 w-4 animate-spin md:mr-2" />
) : (
<Save className="h-4 w-4 sm:mr-2" />
<Save className="h-4 w-4 md:mr-2" />
)}
<span className="hidden sm:inline">Save Draft</span>
<span className="hidden md:inline">Save Draft</span>
</Button>
<Button
onClick={handleCreateInvoice}
disabled={isLoading || !isFormValid()}
className="bg-gradient-to-r from-emerald-600 to-teal-600 shadow-md transition-all duration-200 hover:from-emerald-700 hover:to-teal-700 hover:shadow-lg"
className="btn-brand-primary shadow-md"
size="sm"
>
{isLoading ? (
<Loader2 className="h-4 w-4 animate-spin sm:mr-2" />
<Loader2 className="h-4 w-4 animate-spin md:mr-2" />
) : (
<Send className="h-4 w-4 sm:mr-2" />
<Send className="h-4 w-4 md:mr-2" />
)}
<span className="hidden sm:inline">Create Invoice</span>
<span className="hidden md:inline">Create Invoice</span>
</Button>
</FloatingActionBar>
</div>
+1 -4
View File
@@ -28,10 +28,7 @@ export default async function InvoicesPage() {
<span>Import CSV</span>
</Link>
</Button>
<Button
asChild
className="bg-gradient-to-r from-emerald-600 to-teal-600 shadow-md transition-all duration-200 hover:from-emerald-700 hover:to-teal-700 hover:shadow-lg"
>
<Button asChild className="btn-brand-primary shadow-md">
<Link href="/dashboard/invoices/new">
<Plus className="mr-2 h-5 w-5" />
<span>Create Invoice</span>
+1 -1
View File
@@ -8,7 +8,7 @@ export default function DashboardLayout({
children: React.ReactNode;
}) {
return (
<div className="bg-cosmic-gradient bg-nebula-overlay relative min-h-screen">
<div className="floating-orbs relative min-h-screen">
<Navbar />
<Sidebar />
{/* Mobile layout - no left margin */}
+55 -71
View File
@@ -42,50 +42,46 @@ async function DashboardStats() {
title: "Total Clients",
value: totalClients.toString(),
icon: Users,
color: "text-blue-600 dark:text-blue-400",
bgColor: "bg-blue-100 dark:bg-blue-900/20",
color: "text-icon-blue",
bgColor: "bg-brand-muted-blue",
},
{
title: "Total Invoices",
value: totalInvoices.toString(),
icon: FileText,
color: "text-emerald-600 dark:text-emerald-400",
bgColor: "bg-emerald-100 dark:bg-emerald-900/20",
color: "text-icon-emerald",
bgColor: "bg-brand-muted",
},
{
title: "Total Revenue",
value: `$${totalRevenue.toLocaleString("en-US", { minimumFractionDigits: 2 })}`,
icon: DollarSign,
color: "text-teal-600 dark:text-teal-400",
bgColor: "bg-teal-100 dark:bg-teal-900/20",
color: "text-icon-teal",
bgColor: "bg-brand-muted-teal",
},
{
title: "Pending Invoices",
value: pendingInvoices.toString(),
icon: Calendar,
color: "text-amber-600 dark:text-amber-400",
bgColor: "bg-amber-100 dark:bg-amber-900/20",
color: "text-icon-amber",
bgColor: "bg-brand-muted-amber",
},
];
return (
<Card className="mb-4 border-0 shadow-sm">
<Card className="card-primary mb-4">
<CardContent className="p-4 py-0">
<div className="grid grid-cols-2 gap-4 md:grid-cols-4">
<div className="stats-grid">
{stats.map((stat) => {
const Icon = stat.icon;
return (
<div key={stat.title} className="flex items-center space-x-3">
<div className={`rounded-lg p-2 ${stat.bgColor}`}>
<div key={stat.title} className="stats-item">
<div className={`icon-bg-small ${stat.bgColor}`}>
<Icon className={`h-4 w-4 ${stat.color}`} />
</div>
<div className="min-w-0">
<p className="text-muted-foreground text-xs font-medium">
{stat.title}
</p>
<p className={`text-lg font-bold ${stat.color}`}>
{stat.value}
</p>
<p className="stats-label">{stat.title}</p>
<p className={`stats-value ${stat.color}`}>{stat.value}</p>
</div>
</div>
);
@@ -99,38 +95,27 @@ async function DashboardStats() {
// Quick Actions Component
function QuickActions() {
return (
<Card className="border-0 shadow-sm">
<Card className="card-secondary">
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2 text-lg">
<Plus className="h-5 w-5 text-emerald-600" />
<CardTitle className="quick-action-title">
<Plus className="quick-action-icon" />
Quick Actions
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<Button
asChild
className="w-full bg-gradient-to-r from-emerald-600 to-teal-600 text-white shadow-sm hover:from-emerald-700 hover:to-teal-700"
>
<Button asChild className="btn-brand-primary w-full shadow-sm">
<Link href="/dashboard/invoices/new">
<FileText className="mr-2 h-4 w-4" />
Create Invoice
</Link>
</Button>
<Button
asChild
variant="outline"
className="w-full border-0 shadow-sm"
>
<Button asChild variant="outline" className="w-full shadow-sm">
<Link href="/dashboard/clients/new">
<Users className="mr-2 h-4 w-4" />
Add Client
</Link>
</Button>
<Button
asChild
variant="outline"
className="w-full border-0 shadow-sm"
>
<Button asChild variant="outline" className="w-full shadow-sm">
<Link href="/dashboard/businesses/new">
<TrendingUp className="mr-2 h-4 w-4" />
Add Business
@@ -153,7 +138,7 @@ async function RecentActivity() {
if (recentInvoices.length === 0) {
return (
<Card className="border-0 shadow-sm">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Calendar className="h-5 w-5" />
@@ -161,15 +146,12 @@ async function RecentActivity() {
</CardTitle>
</CardHeader>
<CardContent>
<div className="py-8 text-center">
<FileText className="text-muted-foreground mx-auto mb-4 h-12 w-12" />
<p className="text-muted-foreground">
<div className="recent-activity-empty">
<FileText className="recent-activity-icon" />
<p className="recent-activity-text">
No invoices yet. Create your first invoice to get started!
</p>
<Button
asChild
className="mt-4 bg-gradient-to-r from-emerald-600 to-teal-600 text-white hover:from-emerald-700 hover:to-teal-700"
>
<Button asChild className="btn-brand-primary mt-4">
<Link href="/dashboard/invoices/new">
<Plus className="mr-2 h-4 w-4" />
Create Invoice
@@ -182,10 +164,10 @@ async function RecentActivity() {
}
return (
<Card className="border-0 shadow-sm">
<Card className="card-primary">
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="flex items-center gap-2">
<Calendar className="text-muted-foreground h-5 w-5" />
<Calendar className="text-muted h-5 w-5" />
Recent Activity
</CardTitle>
<Button variant="outline" size="sm" asChild>
@@ -196,34 +178,36 @@ async function RecentActivity() {
</Button>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="space-y-3">
{recentInvoices.map((invoice) => (
<div
key={invoice.id}
className="hover:bg-muted/50 flex items-center justify-between rounded-lg border p-4 transition-colors"
>
<div className="flex items-center space-x-4">
<div className="rounded-lg bg-emerald-100 p-2 dark:bg-emerald-900/20">
<FileText className="h-4 w-4 text-emerald-600 dark:text-emerald-400" />
<Card key={invoice.id} className="card-secondary">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<div className="activity-icon">
<FileText className="text-icon-emerald h-4 w-4" />
</div>
<div>
<p className="font-medium">
Invoice #{invoice.invoiceNumber}
</p>
<p className="text-muted text-sm">
{invoice.client?.name} $
{invoice.totalAmount.toFixed(2)}
</p>
</div>
</div>
<div className="flex items-center space-x-3">
<StatusBadge status={invoice.status as StatusType} />
<Button variant="ghost" size="sm" asChild>
<Link href={`/dashboard/invoices/${invoice.id}`}>
<Eye className="h-4 w-4" />
</Link>
</Button>
</div>
</div>
<div>
<p className="font-medium">
Invoice #{invoice.invoiceNumber}
</p>
<p className="text-muted-foreground text-sm">
{invoice.client?.name} ${invoice.totalAmount.toFixed(2)}
</p>
</div>
</div>
<div className="flex items-center space-x-3">
<StatusBadge status={invoice.status as StatusType} />
<Button variant="ghost" size="sm" asChild>
<Link href={`/dashboard/invoices/${invoice.id}`}>
<Eye className="h-4 w-4" />
</Link>
</Button>
</div>
</div>
</CardContent>
</Card>
))}
</div>
</CardContent>
@@ -262,11 +262,11 @@ export function SettingsContent() {
{/* Profile & Account Overview */}
<div className="grid gap-6 lg:grid-cols-2">
{/* Profile Section */}
<Card className="shadow-lg">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<div className="rounded-lg bg-emerald-100 p-2">
<User className="h-5 w-5 text-emerald-600" />
<div className="icon-bg-emerald">
<User className="text-icon-emerald h-5 w-5" />
</div>
Profile Information
</CardTitle>
@@ -301,7 +301,7 @@ export function SettingsContent() {
<Button
type="submit"
disabled={updateProfileMutation.isPending}
className="bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-700 hover:to-teal-700"
className="btn-brand-primary"
>
{updateProfileMutation.isPending
? "Updating..."
@@ -312,11 +312,11 @@ export function SettingsContent() {
</Card>
{/* Data Overview */}
<Card className="shadow-lg">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<div className="rounded-lg bg-blue-100 p-2">
<Database className="h-5 w-5 text-blue-600" />
<div className="icon-bg-info">
<Database className="text-icon-blue h-5 w-5" />
</div>
Account Data
</CardTitle>
@@ -329,23 +329,24 @@ export function SettingsContent() {
{dataStatItems.map((item) => {
const Icon = item.icon;
return (
<div
key={item.label}
className="flex items-center justify-between rounded-lg border p-3"
>
<div className="flex items-center gap-3">
<div className={`rounded-lg p-2 ${item.bgColor}`}>
<Icon className={`h-4 w-4 ${item.color}`} />
<Card key={item.label} className="card-secondary">
<CardContent className="py-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className={`rounded-lg p-2 ${item.bgColor}`}>
<Icon className={`h-4 w-4 ${item.color}`} />
</div>
<span className="font-medium">{item.label}</span>
</div>
<Badge
variant="secondary"
className="text-lg font-semibold"
>
{item.value}
</Badge>
</div>
<span className="font-medium">{item.label}</span>
</div>
<Badge
variant="secondary"
className="text-lg font-semibold"
>
{item.value}
</Badge>
</div>
</CardContent>
</Card>
);
})}
</div>
@@ -354,11 +355,11 @@ export function SettingsContent() {
</div>
{/* Data Management */}
<Card className="shadow-lg">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<div className="rounded-lg bg-indigo-100 p-2">
<Shield className="h-5 w-5 text-indigo-600" />
<div className="bg-indigo-subtle rounded-lg p-2">
<Shield className="text-icon-indigo h-5 w-5" />
</div>
Data Management
</CardTitle>
@@ -418,7 +419,7 @@ export function SettingsContent() {
disabled={
!importData.trim() || importDataMutation.isPending
}
className="bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700"
className="btn-brand-primary"
>
{importDataMutation.isPending
? "Importing..."
@@ -444,11 +445,11 @@ export function SettingsContent() {
</Card>
{/* Danger Zone */}
<Card className="shadow-lg">
<Card className="card-primary">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<div className="rounded-lg bg-red-100 p-2">
<AlertTriangle className="h-5 w-5 text-red-600" />
<div className="icon-bg-error">
<AlertTriangle className="text-icon-red h-5 w-5" />
</div>
Data Management
</CardTitle>
@@ -515,7 +516,7 @@ export function SettingsContent() {
deleteConfirmText !== "delete all my data" ||
deleteDataMutation.isPending
}
className="bg-red-600 hover:bg-red-700"
className="btn-danger"
>
{deleteDataMutation.isPending
? "Deleting..."