import { Activity, ArrowUpRight, BarChart3, Calendar, Edit, Eye, FileText, Plus, Users, } from "lucide-react"; import Link from "next/link"; import { Suspense } from "react"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; import { Skeleton } from "~/components/ui/skeleton"; import { getEffectiveInvoiceStatus } from "~/lib/invoice-status"; import { auth } from "~/lib/auth"; import { headers } from "next/headers"; import { HydrateClient, api } from "~/trpc/server"; import type { StoredInvoiceStatus } from "~/types/invoice"; import { RevenueChart } from "~/app/dashboard/_components/revenue-chart"; import { InvoiceStatusChart } from "~/app/dashboard/_components/invoice-status-chart"; import { MonthlyMetricsChart } from "~/app/dashboard/_components/monthly-metrics-chart"; import { AnimatedStatsCard } from "~/app/dashboard/_components/animated-stats-card"; // Hero section with clean mono design function DashboardHero({ firstName }: { firstName: string }) { return (

Welcome back, {firstName}!

Here's what's happening with your business today

); } // Enhanced stats cards with better visuals async function DashboardStats() { const [clients, invoices] = await Promise.all([ api.clients.getAll(), api.invoices.getAll(), ]); const totalClients = clients.length; const paidInvoices = invoices.filter( (invoice) => getEffectiveInvoiceStatus( invoice.status as StoredInvoiceStatus, invoice.dueDate, ) === "paid", ); const totalRevenue = paidInvoices.reduce( (sum, invoice) => sum + invoice.totalAmount, 0, ); const pendingInvoices = invoices.filter((invoice) => { const effectiveStatus = getEffectiveInvoiceStatus( invoice.status as StoredInvoiceStatus, invoice.dueDate, ); return effectiveStatus === "sent" || effectiveStatus === "overdue"; }); const pendingAmount = pendingInvoices.reduce( (sum, invoice) => sum + invoice.totalAmount, 0, ); const overdueInvoices = invoices.filter( (invoice) => getEffectiveInvoiceStatus( invoice.status as StoredInvoiceStatus, invoice.dueDate, ) === "overdue", ); // Calculate month-over-month trends const now = new Date(); const currentMonth = new Date(now.getFullYear(), now.getMonth(), 1); const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1); // Current month data const currentMonthInvoices = invoices.filter( (invoice) => new Date(invoice.issueDate) >= currentMonth, ); const currentMonthRevenue = currentMonthInvoices .filter( (invoice) => getEffectiveInvoiceStatus( invoice.status as StoredInvoiceStatus, invoice.dueDate, ) === "paid", ) .reduce((sum, invoice) => sum + invoice.totalAmount, 0); // Last month data const lastMonthInvoices = invoices.filter((invoice) => { const date = new Date(invoice.issueDate); return date >= lastMonth && date < currentMonth; }); const lastMonthRevenue = lastMonthInvoices .filter( (invoice) => getEffectiveInvoiceStatus( invoice.status as StoredInvoiceStatus, invoice.dueDate, ) === "paid", ) .reduce((sum, invoice) => sum + invoice.totalAmount, 0); // Previous month data for clients const prevMonthClients = clients.filter( (client) => new Date(client.createdAt) < currentMonth, ).length; // Calculate trends const revenueChange = lastMonthRevenue > 0 ? ((currentMonthRevenue - lastMonthRevenue) / lastMonthRevenue) * 100 : currentMonthRevenue > 0 ? 100 : 0; const pendingChange = lastMonthInvoices.length > 0 ? ((pendingInvoices.length - lastMonthInvoices.filter((invoice) => { const status = getEffectiveInvoiceStatus( invoice.status as StoredInvoiceStatus, invoice.dueDate, ); return status === "sent" || status === "overdue"; }).length) / lastMonthInvoices.length) * 100 : pendingInvoices.length > 0 ? 100 : 0; const clientChange = totalClients - prevMonthClients; const lastMonthOverdue = lastMonthInvoices.filter( (invoice) => getEffectiveInvoiceStatus( invoice.status as StoredInvoiceStatus, invoice.dueDate, ) === "overdue", ).length; const overdueChange = overdueInvoices.length - lastMonthOverdue; const formatTrend = (value: number, isCount = false) => { if (isCount) { return value > 0 ? `+${value}` : value.toString(); } return value > 0 ? `+${value.toFixed(1)}%` : `${value.toFixed(1)}%`; }; // Debug logging to see actual values console.log("Dashboard Stats Debug:", { totalRevenue, pendingAmount, totalClients, overdueInvoices: overdueInvoices.length, revenueChange, pendingChange, clientChange, overdueChange, paidInvoicesCount: paidInvoices.length, pendingInvoicesCount: pendingInvoices.length, }); const stats = [ { title: "Total Revenue", value: `$${totalRevenue.toLocaleString("en-US", { minimumFractionDigits: 2 })}`, numericValue: totalRevenue, isCurrency: true, change: formatTrend(revenueChange), trend: revenueChange >= 0 ? ("up" as const) : ("down" as const), iconName: "DollarSign" as const, description: `From ${paidInvoices.length} paid invoices`, }, { title: "Pending Amount", value: `$${pendingAmount.toLocaleString("en-US", { minimumFractionDigits: 2 })}`, numericValue: pendingAmount, isCurrency: true, change: formatTrend(pendingChange), trend: pendingChange >= 0 ? ("up" as const) : ("down" as const), iconName: "Clock" as const, description: `${pendingInvoices.length} invoices awaiting payment`, }, { title: "Active Clients", value: totalClients.toString(), numericValue: totalClients, isCurrency: false, change: formatTrend(clientChange, true), trend: clientChange >= 0 ? ("up" as const) : ("down" as const), iconName: "Users" as const, description: "Total registered clients", }, { title: "Overdue Invoices", value: overdueInvoices.length.toString(), numericValue: overdueInvoices.length, isCurrency: false, change: formatTrend(overdueChange, true), trend: overdueChange <= 0 ? ("up" as const) : ("down" as const), iconName: "TrendingDown" as const, description: "Invoices past due date", }, ]; return (
{stats.map((stat, index) => ( ))}
); } // Charts section async function ChartsSection() { const invoices = await api.invoices.getAll(); return (
{/* Revenue Trend Chart */} Revenue Over Time {/* Invoice Status Breakdown */} Invoice Status {/* Monthly Metrics */} Monthly Metrics
); } // Enhanced Quick Actions function QuickActions() { const actions = [ { title: "Create Invoice", description: "Start a new invoice for a client", href: "/dashboard/invoices/new", icon: FileText, featured: true, }, { title: "Add Client", description: "Register a new client", href: "/dashboard/clients/new", icon: Users, featured: false, }, { title: "View All Invoices", description: "Manage your invoice pipeline", href: "/dashboard/invoices", icon: BarChart3, featured: false, }, ]; return ( Quick Actions {actions.map((action) => { const Icon = action.icon; return (

{action.title}

{action.description}

); })}
); } // Current work section with enhanced design async function CurrentWork() { const invoices = await api.invoices.getAll(); const draftInvoices = invoices.filter( (invoice) => getEffectiveInvoiceStatus( invoice.status as StoredInvoiceStatus, invoice.dueDate, ) === "draft", ); const currentInvoice = draftInvoices[0]; if (!currentInvoice) { return ( Current Work

No active drafts

Create a new invoice to get started

); } const totalHours = currentInvoice.items?.reduce((sum, item) => sum + item.hours, 0) ?? 0; return ( Current Work In Progress

#{currentInvoice.invoiceNumber}

${currentInvoice.totalAmount.toFixed(2)}
{currentInvoice.client?.name} {totalHours.toFixed(1)} hours logged
); } // Enhanced recent activity 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 getStatusStyle = (status: string) => { switch (status) { case "paid": return { backgroundColor: "oklch(var(--chart-2) / 0.1)", borderColor: "oklch(var(--chart-2) / 0.3)", color: "oklch(var(--chart-2))", }; case "sent": return { backgroundColor: "oklch(var(--chart-1) / 0.1)", borderColor: "oklch(var(--chart-1) / 0.3)", color: "oklch(var(--chart-1))", }; case "overdue": return { backgroundColor: "oklch(var(--chart-3) / 0.1)", borderColor: "oklch(var(--chart-3) / 0.3)", color: "oklch(var(--chart-3))", }; default: return { backgroundColor: "hsl(var(--muted))", borderColor: "hsl(var(--border))", color: "hsl(var(--muted-foreground))", }; } }; return ( Recent Activity {recentInvoices.length === 0 ? (

No invoices yet

Create your first invoice to get started

) : (
{recentInvoices.map((invoice, _index) => (

#{invoice.invoiceNumber}

{invoice.client?.name}

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

{new Date(invoice.issueDate).toLocaleDateString()}

))}
)}
); } // Loading skeletons function StatsSkeleton() { return (
{Array.from({ length: 4 }).map((_, i) => (
))}
); } function ChartsSkeleton() { return (
); } function CardSkeleton() { return (
); } export default async function DashboardPage() { const session = await auth.api.getSession({ headers: await headers(), }); const firstName = session?.user?.name?.split(" ")[0] ?? "User"; return (
}> }>
}>
}>
); }