Files
app/invoice-list-card/page.tsx
import {
  ArrowDownToLine,
  CheckCircle2,
  Clock,
  Download,
  RotateCcw,
  TriangleAlert,
} from "lucide-react"

import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

type InvoiceStatus = "paid" | "pending" | "overdue" | "refunded"

type Invoice = {
  id: string
  description: string
  date: string
  amount: number
  status: InvoiceStatus
}

const invoices: Invoice[] = [
  {
    id: "INV-2026-0612",
    description: "Pro plan · Monthly",
    date: "Jun 1, 2026",
    amount: 49,
    status: "paid",
  },
  {
    id: "INV-2026-0598",
    description: "Additional seats · 3 members",
    date: "Jun 1, 2026",
    amount: 36,
    status: "pending",
  },
  {
    id: "INV-2026-0540",
    description: "Pro plan · Monthly",
    date: "May 1, 2026",
    amount: 49,
    status: "overdue",
  },
  {
    id: "INV-2026-0489",
    description: "Usage overage · April",
    date: "Apr 30, 2026",
    amount: 12.4,
    status: "refunded",
  },
  {
    id: "INV-2026-0455",
    description: "Pro plan · Monthly",
    date: "Apr 1, 2026",
    amount: 49,
    status: "paid",
  },
]

const statusConfig: Record<
  InvoiceStatus,
  {
    label: string
    icon: typeof CheckCircle2
    badgeClass: string
    dotClass: string
  }
> = {
  paid: {
    label: "Paid",
    icon: CheckCircle2,
    badgeClass:
      "border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-900/60 dark:bg-emerald-950/40 dark:text-emerald-300",
    dotClass: "bg-emerald-500",
  },
  pending: {
    label: "Pending",
    icon: Clock,
    badgeClass:
      "border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/60 dark:bg-amber-950/40 dark:text-amber-300",
    dotClass: "bg-amber-500",
  },
  overdue: {
    label: "Overdue",
    icon: TriangleAlert,
    badgeClass:
      "border-rose-200 bg-rose-50 text-rose-700 dark:border-rose-900/60 dark:bg-rose-950/40 dark:text-rose-300",
    dotClass: "bg-rose-500",
  },
  refunded: {
    label: "Refunded",
    icon: RotateCcw,
    badgeClass:
      "border-slate-200 bg-slate-50 text-slate-600 dark:border-slate-700 dark:bg-slate-800/60 dark:text-slate-300",
    dotClass: "bg-slate-400 dark:bg-slate-500",
  },
}

const currency = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
})

export default function InvoiceListCard() {
  const outstanding = invoices
    .filter((i) => i.status === "pending" || i.status === "overdue")
    .reduce((sum, i) => sum + i.amount, 0)

  return (
    <Card
      className="w-full max-w-xl gap-0 overflow-hidden rounded-2xl border py-0 shadow-sm"
      role="region"
      aria-label="Billing invoices"
    >
      <CardHeader className="flex flex-row items-start justify-between gap-4 space-y-0 border-b px-5 py-5 sm:px-6">
        <div className="space-y-1">
          <CardTitle className="text-lg font-semibold tracking-tight">
            Invoices
          </CardTitle>
          <CardDescription>
            {outstanding > 0 ? (
              <>
                <span className="font-medium text-rose-600 dark:text-rose-400">
                  {currency.format(outstanding)}
                </span>{" "}
                outstanding across {invoices.length} invoices
              </>
            ) : (
              <>All {invoices.length} invoices settled</>
            )}
          </CardDescription>
        </div>
        <Button
          variant="outline"
          size="sm"
          className="shrink-0 gap-1.5"
          aria-label="Download all invoices"
        >
          <ArrowDownToLine className="size-4" aria-hidden="true" />
          <span className="hidden sm:inline">Export all</span>
        </Button>
      </CardHeader>

      <CardContent className="px-0 py-0">
        <ul role="list" className="divide-y">
          {invoices.map((invoice) => {
            const config = statusConfig[invoice.status]
            const StatusIcon = config.icon
            return (
              <li
                key={invoice.id}
                className="hover:bg-muted/50 flex items-center gap-3 px-5 py-4 transition-colors sm:gap-4 sm:px-6"
              >
                <div
                  className="bg-muted text-muted-foreground flex size-10 shrink-0 items-center justify-center rounded-xl"
                  aria-hidden="true"
                >
                  <StatusIcon className="size-5" />
                </div>

                <div className="min-w-0 flex-1">
                  <div className="flex items-center gap-2">
                    <p className="text-foreground truncate text-sm font-medium">
                      {invoice.id}
                    </p>
                    <Badge
                      variant="outline"
                      className={`hidden gap-1 rounded-full px-2 py-0 text-[11px] font-medium capitalize sm:inline-flex ${config.badgeClass}`}
                    >
                      <span
                        className={`size-1.5 rounded-full ${config.dotClass}`}
                        aria-hidden="true"
                      />
                      {config.label}
                    </Badge>
                  </div>
                  <p className="text-muted-foreground mt-0.5 truncate text-xs">
                    {invoice.description} · {invoice.date}
                  </p>
                </div>

                <div className="flex shrink-0 flex-col items-end gap-1">
                  <span className="text-foreground text-sm font-semibold tabular-nums">
                    {currency.format(invoice.amount)}
                  </span>
                  <Badge
                    variant="outline"
                    className={`gap-1 rounded-full px-2 py-0 text-[11px] font-medium capitalize sm:hidden ${config.badgeClass}`}
                  >
                    {config.label}
                  </Badge>
                </div>

                <Button
                  variant="ghost"
                  size="icon"
                  className="text-muted-foreground hover:text-foreground size-8 shrink-0"
                  aria-label={`Download invoice ${invoice.id}`}
                >
                  <Download className="size-4" aria-hidden="true" />
                </Button>
              </li>
            )
          })}
        </ul>
      </CardContent>

      <CardFooter className="text-muted-foreground justify-between border-t px-5 py-4 text-xs sm:px-6">
        <span>Showing {invoices.length} of 38 invoices</span>
        <button
          type="button"
          className="text-foreground font-medium underline-offset-4 hover:underline"
        >
          View all
        </button>
      </CardFooter>
    </Card>
  )
}
Invoice list card with status badges, amounts, and download buttons
invoice-list-card-01