From c2fdcabac8493936b87b9f4c6c60e6296d9c7eb1 Mon Sep 17 00:00:00 2001 From: Sean O'Connor Date: Wed, 16 Jul 2025 03:49:05 -0400 Subject: [PATCH] Dark theme tuning --- src/app/dashboard/page.tsx | 509 ++++++++++++++------ src/components/forms/invoice-line-items.tsx | 2 +- src/components/ui/skeleton.tsx | 366 +++----------- src/styles/globals.css | 44 +- 4 files changed, 475 insertions(+), 446 deletions(-) diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 627024d..bd93b81 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -1,26 +1,44 @@ import { Suspense } from "react"; import { HydrateClient, api } from "~/trpc/server"; -import { PageHeader } from "~/components/layout/page-header"; -import { Button } from "~/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; - -import { StatusBadge, type StatusType } from "~/components/data/status-badge"; -import { DataTableSkeleton } from "~/components/data/data-table"; -import { CurrentOpenInvoiceCard } from "~/components/data/current-open-invoice-card"; +import { Button } from "~/components/ui/button"; +import { Badge } from "~/components/ui/badge"; +import { Skeleton } from "~/components/ui/skeleton"; import { auth } from "~/server/auth"; import Link from "next/link"; import { Users, FileText, - TrendingUp, DollarSign, + TrendingUp, Plus, - Eye, - Calendar, ArrowUpRight, + Calendar, + Clock, + Eye, + Edit, + Activity, + BarChart3, } from "lucide-react"; -// Stats Cards Component +// Modern gradient background component +function DashboardHero({ firstName }: { firstName: string }) { + return ( +
+
+
+

Welcome back, {firstName}!

+

+ Ready to manage your invoicing business +

+
+
+
+
+ ); +} + +// Enhanced stats cards with better visual hierarchy async function DashboardStats() { const [clients, invoices] = await Promise.all([ api.clients.getAll(), @@ -29,129 +47,178 @@ async function DashboardStats() { const totalClients = clients.length; const totalInvoices = invoices.length; - const totalRevenue = invoices.reduce( - (sum, invoice) => sum + invoice.totalAmount, - 0, - ); - const pendingInvoices = invoices.filter( - (invoice) => invoice.status === "sent" || invoice.status === "draft", - ).length; + const totalRevenue = invoices + .filter((invoice) => invoice.status === "paid") + .reduce((sum, invoice) => sum + invoice.totalAmount, 0); + const pendingAmount = invoices + .filter((invoice) => invoice.status === "sent") + .reduce((sum, invoice) => sum + invoice.totalAmount, 0); const stats = [ { - title: "Total Clients", + title: "Total Revenue", + value: `$${totalRevenue.toLocaleString("en-US", { minimumFractionDigits: 2 })}`, + change: "+12.5%", + icon: DollarSign, + color: "", + bgColor: "bg-green-50", + changeColor: "", + }, + { + title: "Pending Amount", + value: `$${pendingAmount.toLocaleString("en-US", { minimumFractionDigits: 2 })}`, + change: "+8.2%", + icon: Clock, + color: "", + bgColor: "bg-amber-50", + changeColor: "", + }, + { + title: "Active Clients", value: totalClients.toString(), + change: "+3", icon: Users, - color: "text-icon-blue", - bgColor: "bg-brand-muted-blue", + color: "", + bgColor: "bg-blue-50", + changeColor: "", }, { title: "Total Invoices", value: totalInvoices.toString(), + change: "+15", icon: FileText, - color: "text-icon-emerald", - bgColor: "bg-brand-muted", - }, - { - title: "Total Revenue", - value: `$${totalRevenue.toLocaleString("en-US", { minimumFractionDigits: 2 })}`, - icon: DollarSign, - color: "text-icon-teal", - bgColor: "bg-brand-muted-teal", - }, - { - title: "Pending Invoices", - value: pendingInvoices.toString(), - icon: Calendar, - color: "text-icon-amber", - bgColor: "bg-brand-muted-amber", + color: "", + bgColor: "bg-purple-50", + changeColor: "", }, ]; return ( - - -
- {stats.map((stat) => { - const Icon = stat.icon; - return ( -
-
- -
-
-

{stat.title}

-

{stat.value}

+
+ {stats.map((stat) => { + const Icon = stat.icon; + return ( + + +
+
+
+ + {stat.change} +
- ); - })} -
- - +
+

+ {stat.value} +

+

+ {stat.title} +

+
+ + + ); + })} +
); } -// Quick Actions Component +// Quick Actions with better visual design function QuickActions() { + const actions = [ + { + title: "Create Invoice", + description: "Start a new invoice", + href: "/dashboard/invoices/new", + icon: FileText, + primary: true, + }, + { + title: "Add Client", + description: "Add a new client", + href: "/dashboard/clients/new", + icon: Users, + primary: false, + }, + { + title: "View Reports", + description: "Business analytics", + href: "/dashboard/reports", + icon: BarChart3, + primary: false, + }, + ]; + return ( - + - - + + Quick Actions - - - - + + {actions.map((action) => { + const Icon = action.icon; + return ( + + ); + })} ); } -// Recent Activity Component -async function RecentActivity() { +// Current work in progress +async function CurrentWork() { const invoices = await api.invoices.getAll(); - const recentInvoices = invoices - .sort( - (a, b) => - new Date(b.issueDate).getTime() - new Date(a.issueDate).getTime(), - ) - .slice(0, 5); + const draftInvoices = invoices.filter( + (invoice) => invoice.status === "draft", + ); + const currentInvoice = draftInvoices[0]; - if (recentInvoices.length === 0) { + if (!currentInvoice) { return ( - + - - - Recent Activity + + + Current Work -
- -

- No invoices yet. Create your first invoice to get started! +

+ +

+ No draft invoices found

- + +
+
+
+
+ ); +} + +// Recent activity with enhanced design +async function RecentActivity() { + const invoices = await api.invoices.getAll(); + const recentInvoices = invoices + .sort( + (a, b) => + new Date(b.issueDate).getTime() - new Date(a.issueDate).getTime(), + ) + .slice(0, 5); + + const getStatusColor = (status: string) => { + switch (status) { + case "paid": + return "bg-green-50 border-green-200"; + case "sent": + return "bg-blue-50 border-blue-200"; + case "overdue": + return "bg-red-50 border-red-200"; + default: + return "bg-gray-50 border-gray-200"; + } + }; + + return ( + + + + Recent Activity - + + {recentInvoices.length === 0 ? ( +
+ +

+ No invoices yet +

+ +
+ ) : ( +
+ {recentInvoices.map((invoice) => ( + + + +
+
+
+ +
+
+

+ #{invoice.invoiceNumber} +

+

+ {invoice.client?.name} •{" "} + {new Date(invoice.issueDate).toLocaleDateString()} +

+
+
+
+ + {invoice.status} + +

+ ${invoice.totalAmount.toFixed(2)} +

+
+ +
+
+
+
+
+ + ))} +
+ )} +
+
+ ); +} + +// Loading skeletons +function StatsSkeleton() { + return ( +
+ {Array.from({ length: 4 }).map((_, i) => ( + + +
+ + +
+ + +
+
+ ))} +
+ ); +} + +function CardSkeleton() { + return ( + + + +
- {recentInvoices.map((invoice) => ( - - -
-
-
- -
-
-

- Invoice #{invoice.invoiceNumber} -

-

- {invoice.client?.name} • $ - {invoice.totalAmount.toFixed(2)} -

-
-
-
- - -
-
-
-
- ))} + + +
@@ -220,31 +430,30 @@ export default async function DashboardPage() { const firstName = session?.user?.name?.split(" ")[0] ?? "User"; return ( - <> - +
+ -
+ + }> + + + + +
- }> - + }> + -
- - -
- - - }> - - - +
- + + + }> + + + +
); } diff --git a/src/components/forms/invoice-line-items.tsx b/src/components/forms/invoice-line-items.tsx index fd7a623..6c69eef 100644 --- a/src/components/forms/invoice-line-items.tsx +++ b/src/components/forms/invoice-line-items.tsx @@ -206,7 +206,7 @@ function MobileLineItem({
{/* Bottom section with controls, item name, and total */} -
+