Skip to content
All Taste components

Dashboards

Invoicing dashboard

A small-business money overview: what’s owed, what’s overdue, monthly collections and the latest invoices.

sidebar · header · 4 KPIs · bar chart · due list · invoice table · 4 statuses

Overview

Good morning, Asha

Here’s where your money stands for October.

Outstanding
₹4,82,300
23 open invoices
Overdue
₹96,400
6 invoices · oldest 21 days
Collected in October
₹7,14,950
↑ 12.4% vs September
Average days to pay
18 days
↓ 3 days faster

Invoiced vs collected

InvoicedCollected
0₹4L₹8L₹12LNDJFMAMJJASO

Due in 7 days

₹1,37,500
Nilgiri Traders
Fri 16
₹38,500
Sharma Hardware
Sat 17
₹12,800
Coastal Spice Co.
Mon 19
₹64,200
Bluefern Studio
Tue 20
₹22,000

Recent invoices

InvoiceCustomerIssuedDueAmountStatus
INV-1048Nilgiri Traders12 Oct26 Oct₹38,500Sent
INV-1047Coastal Spice Co.10 Oct24 Oct₹64,200Viewed
INV-1046Sharma Hardware02 Oct09 Oct₹12,800Overdue
INV-1045Bluefern Studio28 Sep12 Oct₹22,000Paid
import { Bell, ChevronDown, CreditCard, Download, FileText, Home, LineChart, Plus, Search, Tag, Users } from "lucide-react";
import { cn } from "@/lib/utils";

const nav = [
  { label: "Overview", icon: Home, href: "/", current: true },
  { label: "Invoices", icon: FileText, href: "/invoices", count: 12 },
  { label: "Customers", icon: Users, href: "/customers" },
  { label: "Payments", icon: CreditCard, href: "/payments" },
  { label: "Expenses", icon: Tag, href: "/expenses" },
  { label: "Reports", icon: LineChart, href: "/reports" },
];

const kpis = [
  { label: "Outstanding", value: "₹4,82,300", note: "23 open invoices", tone: "flat" },
  { label: "Overdue", value: "₹96,400", note: "6 invoices · oldest 21 days", tone: "down" },
  { label: "Collected in October", value: "₹7,14,950", note: "↑ 12.4% vs September", tone: "up" },
  { label: "Average days to pay", value: "18 days", note: "↓ 3 days faster", tone: "up" },
] as const;

// Lakh rupees per month, November → October.
const months = ["Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct"];
const invoiced = [6.2, 5.8, 7.1, 6.6, 7.9, 8.4, 7.2, 8.8, 9.6, 9.1, 10.4, 9.8];
const collected = [5.6, 5.5, 6.4, 6.1, 7.0, 7.9, 6.8, 8.0, 8.7, 8.6, 9.2, 7.1];
const max = 12;

const dueThisWeek = [
  { customer: "Nilgiri Traders", day: "Fri 16", amount: "₹38,500" },
  { customer: "Sharma Hardware", day: "Sat 17", amount: "₹12,800" },
  { customer: "Coastal Spice Co.", day: "Mon 19", amount: "₹64,200" },
  { customer: "Bluefern Studio", day: "Tue 20", amount: "₹22,000" },
];

const invoices = [
  { id: "INV-1048", customer: "Nilgiri Traders", issued: "12 Oct", due: "26 Oct", amount: "₹38,500", status: "Sent" },
  { id: "INV-1047", customer: "Coastal Spice Co.", issued: "10 Oct", due: "24 Oct", amount: "₹64,200", status: "Viewed" },
  { id: "INV-1046", customer: "Sharma Hardware", issued: "02 Oct", due: "09 Oct", amount: "₹12,800", status: "Overdue" },
  { id: "INV-1045", customer: "Bluefern Studio", issued: "28 Sep", due: "12 Oct", amount: "₹22,000", status: "Paid" },
] as const;

const statusTone = {
  Sent: "bg-surface-2 text-fg-muted",
  Viewed: "bg-info-soft text-info-fg",
  Overdue: "bg-danger-soft text-danger-fg",
  Paid: "bg-success-soft text-success-fg",
};

export default function DashboardPage() {
  return (
    <div className="flex min-h-screen bg-bg font-sans text-sm text-fg">
      <aside aria-label="Main" className="hidden w-56 shrink-0 flex-col gap-0.5 border-r border-border bg-surface p-3 lg:flex">
        <button type="button" className="mb-2 flex h-8 items-center gap-2 rounded px-1.5 font-medium hover:bg-surface-2">
          <span className="grid size-6 place-items-center rounded-sm bg-primary text-xs font-semibold text-primary-fg">K</span>
          Kaveri Foods
          <ChevronDown className="ml-auto size-3.5 text-fg-subtle" aria-hidden />
        </button>
        <nav className="grid gap-0.5">
          {nav.map(({ label, icon: Icon, href, current, count }) => (
            <a
              key={label}
              href={href}
              aria-current={current ? "page" : undefined}
              className={cn(
                "flex h-8 items-center gap-2.5 rounded px-2 font-medium text-fg-muted transition hover:bg-surface-2 hover:text-fg",
                current && "bg-bg text-fg shadow-sm ring-1 ring-border",
              )}
            >
              <Icon className="size-4" aria-hidden />
              {label}
              {count && <span className="ml-auto text-xs tabular-nums text-fg-subtle">{count}</span>}
            </a>
          ))}
        </nav>
        <div className="mt-auto flex items-center gap-2 border-t border-border px-1 pt-3">
          <span className="grid size-7 place-items-center rounded-full bg-primary-soft text-[11px] font-medium text-primary-soft-fg">AR</span>
          <div className="leading-tight"><p className="font-medium">Asha Rao</p><p className="text-xs text-fg-subtle">Owner</p></div>
        </div>
      </aside>

      <div className="flex min-w-0 flex-1 flex-col">
        <header className="flex h-14 items-center gap-3 border-b border-border px-4 sm:px-6">
          <p className="font-medium">Overview</p>
          <label className="ml-auto hidden h-9 w-64 items-center gap-2 rounded border border-border bg-surface px-2.5 text-fg-subtle md:flex">
            <Search className="size-4" aria-hidden />
            <input placeholder="Search invoices, customers" className="w-full bg-transparent text-[13px] text-fg outline-none placeholder:text-fg-subtle" />
          </label>
          <button type="button" aria-label="Notifications" className="relative grid size-9 place-items-center rounded text-fg-muted hover:bg-surface-2 hover:text-fg">
            <Bell className="size-4" aria-hidden />
            <span className="absolute right-2 top-2 size-1.5 rounded-full bg-danger ring-2 ring-bg" />
          </button>
          <button type="button" className="inline-flex h-9 items-center gap-1.5 rounded bg-primary px-3 font-medium text-primary-fg hover:bg-primary-hover">
            <Plus className="size-4" aria-hidden /> New invoice
          </button>
        </header>

        <main className="grid gap-4 p-4 sm:p-6">
          <div className="flex flex-wrap items-end justify-between gap-3">
            <div>
              <h1 className="text-xl font-medium tracking-tight">Good morning, Asha</h1>
              <p className="text-fg-muted">Here’s where your money stands for October.</p>
            </div>
            <button type="button" className="inline-flex h-9 items-center gap-1.5 rounded border border-border-strong bg-bg px-3 font-medium hover:bg-surface">
              <Download className="size-4" aria-hidden /> Export
            </button>
          </div>

          <section aria-label="Key figures" className="grid grid-cols-2 gap-3 xl:grid-cols-4">
            {kpis.map((k) => (
              <div key={k.label} className="rounded-lg border border-border p-4">
                <p className="text-xs text-fg-muted">{k.label}</p>
                <p className="mt-1 text-2xl font-medium tracking-tight tabular-nums">{k.value}</p>
                <p className={cn("mt-1 text-xs font-medium tabular-nums",
                  k.tone === "up" ? "text-success-fg" : k.tone === "down" ? "text-danger-fg" : "text-fg-subtle")}>{k.note}</p>
              </div>
            ))}
          </section>

          <div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_288px]">
            <section aria-labelledby="chart-title" className="rounded-lg border border-border">
              <div className="flex items-center gap-3 border-b border-border px-4 py-3">
                <h2 id="chart-title" className="font-medium">Invoiced vs collected</h2>
                <div className="ml-auto flex gap-3 text-xs text-fg-muted">
                  <span className="flex items-center gap-1.5"><i className="size-2 rounded-sm bg-primary/30" />Invoiced</span>
                  <span className="flex items-center gap-1.5"><i className="size-2 rounded-sm bg-primary" />Collected</span>
                </div>
              </div>
              <div className="flex h-48 items-end gap-2 px-4 pb-3 pt-4" role="img" aria-label="Monthly invoiced and collected amounts">
                {months.map((m, i) => (
                  <div key={m} className="flex flex-1 flex-col items-center gap-1.5">
                    <div className="flex h-36 w-full items-end justify-center gap-0.5">
                      <span className="w-2.5 rounded-sm bg-primary/30" style={{ height: (invoiced[i] / max) * 100 + "%" }} />
                      <span className="w-2.5 rounded-sm bg-primary" style={{ height: (collected[i] / max) * 100 + "%" }} />
                    </div>
                    <span className="text-[11px] text-fg-subtle">{m}</span>
                  </div>
                ))}
              </div>
            </section>

            <section aria-labelledby="due-title" className="rounded-lg border border-border">
              <div className="flex items-center justify-between border-b border-border px-4 py-3">
                <h2 id="due-title" className="font-medium">Due in 7 days</h2>
                <span className="rounded-full bg-surface-2 px-2 py-0.5 text-xs font-medium tabular-nums text-fg-muted">₹1,37,500</span>
              </div>
              <ul className="divide-y divide-border">
                {dueThisWeek.map((d) => (
                  <li key={d.customer} className="flex items-center gap-3 px-4 py-2.5">
                    <div className="min-w-0"><p className="truncate font-medium">{d.customer}</p><p className="text-xs text-fg-subtle">{d.day}</p></div>
                    <span className="ml-auto font-medium tabular-nums">{d.amount}</span>
                  </li>
                ))}
              </ul>
            </section>
          </div>

          <section aria-labelledby="recent-title" className="overflow-hidden rounded-lg border border-border">
            <div className="flex items-center justify-between border-b border-border px-4 py-3">
              <h2 id="recent-title" className="font-medium">Recent invoices</h2>
              <a href="/invoices" className="text-[13px] font-medium text-primary hover:underline">View all</a>
            </div>
            <div className="overflow-x-auto">
              <table className="w-full text-[13px]">
                <thead className="bg-surface text-left text-xs text-fg-subtle">
                  <tr>
                    <th className="px-4 py-2 font-medium">Invoice</th><th className="px-4 py-2 font-medium">Customer</th>
                    <th className="px-4 py-2 font-medium">Issued</th><th className="px-4 py-2 font-medium">Due</th>
                    <th className="px-4 py-2 text-right font-medium">Amount</th><th className="px-4 py-2 font-medium">Status</th>
                  </tr>
                </thead>
                <tbody className="divide-y divide-border">
                  {invoices.map((inv) => (
                    <tr key={inv.id} className="whitespace-nowrap hover:bg-surface">
                      <td className="px-4 py-2.5 tabular-nums text-fg-muted">{inv.id}</td>
                      <td className="px-4 py-2.5 font-medium">{inv.customer}</td>
                      <td className="px-4 py-2.5 text-fg-muted">{inv.issued}</td>
                      <td className="px-4 py-2.5 text-fg-muted">{inv.due}</td>
                      <td className="px-4 py-2.5 text-right font-medium tabular-nums">{inv.amount}</td>
                      <td className="px-4 py-2.5">
                        <span className={cn("inline-flex h-5 items-center gap-1.5 rounded-full px-2 text-xs font-medium", statusTone[inv.status])}>
                          <span className="size-1.5 rounded-full bg-current" />{inv.status}
                        </span>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </section>
        </main>
      </div>
    </div>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.