Invoices List
blockfinanceInvoice history page: header with a New invoice action, a three-card summary strip (total outstanding, paid this month, overdue), and an invoice table pairing monospace invoice numbers with avatar clients, issue/due dates, tabular-nums amounts, color-coded status badges, and a per-row View/Download/Mark-as-paid dropdown, closed by a pagination hint. Data is stubbed inline; swap for your source.
Installation
$pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/invoices-list.json$npx shadcn-vue@latest add https://uipkge.dev/r/vue/invoices-list.json$yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/invoices-list.json$bunx shadcn-vue@latest add https://uipkge.dev/r/vue/invoices-list.jsonNamed registry:
npx shadcn-vue@latest add @uipkge/invoices-listInstalls to:app/components/blocks/Variants
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
class | HTMLAttributes['class'] | — | optional |
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
Invoiceinterface Invoice {
id: string
number: string
client: string
issued: string
due: string
amount: string
status: InvoiceStatus
}npm dependencies
Files installed (1)
app/components/blocks/InvoicesList.vue6.9 kB
<script setup lang="ts"> import type { HTMLAttributes } from 'vue' import { Check, Download, Eye, MoreHorizontal, Plus } from 'lucide-vue-next' import { cn } from '@/lib/utils' import { Avatar, AvatarFallback } from '@/components/ui/avatar' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card, CardContent } from '@/components/ui/card' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' type InvoiceStatus = 'paid' | 'pending' | 'overdue' | 'draft' interface Invoice { id: string number: string client: string issued: string due: string amount: string status: InvoiceStatus } const props = defineProps<{ class?: HTMLAttributes['class'] }>() const summary = [ { label: 'Total outstanding', value: '$12,480.00', note: '5 unpaid invoices', tone: '' }, { label: 'Paid this month', value: '$28,930.00', note: '+12% vs last month', tone: '' }, { label: 'Overdue', value: '$3,120.00', note: '2 invoices past due', tone: 'text-destructive', }, ] const statusClass: Record<InvoiceStatus, string> = { paid: 'bg-success/10 text-success', pending: 'bg-warning/10 text-warning', overdue: 'bg-destructive/10 text-destructive', draft: '', } const invoices: Invoice[] = [ { id: 'i1', number: 'INV-2026-0142', client: 'Acme Corp', issued: 'Aug 18, 2026', due: 'Sep 1, 2026', amount: '$4,800.00', status: 'pending', }, { id: 'i2', number: 'INV-2026-0141', client: 'Northwind Traders', issued: 'Aug 15, 2026', due: 'Aug 30, 2026', amount: '$2,350.00', status: 'overdue', }, { id: 'i3', number: 'INV-2026-0140', client: 'Globex Industries', issued: 'Aug 12, 2026', due: 'Sep 10, 2026', amount: '$1,200.00', status: 'draft', }, { id: 'i4', number: 'INV-2026-0139', client: 'Helios Labs', issued: 'Aug 8, 2026', due: 'Aug 22, 2026', amount: '$6,750.00', status: 'paid', }, { id: 'i5', number: 'INV-2026-0138', client: 'Acme Corp', issued: 'Aug 5, 2026', due: 'Aug 19, 2026', amount: '$770.00', status: 'paid', }, { id: 'i6', number: 'INV-2026-0137', client: 'Vertex Media', issued: 'Jul 30, 2026', due: 'Aug 13, 2026', amount: '$3,120.00', status: 'overdue', }, { id: 'i7', number: 'INV-2026-0136', client: 'Initech Systems', issued: 'Jul 24, 2026', due: 'Aug 7, 2026', amount: '$5,400.00', status: 'paid', }, ] function initials(name: string) { return name .split(' ') .map((part) => part[0]) .slice(0, 2) .join('') .toUpperCase() } </script> <template> <div data-slot="invoices-list" :class="cn('w-full space-y-6', props.class)"> <div class="flex flex-wrap items-center justify-between gap-4"> <div> <h2 class="text-2xl font-bold tracking-tight">Invoices</h2> <p class="text-muted-foreground mt-1 text-sm">Manage billing for all of your clients.</p> </div> <Button> <Plus aria-hidden="true" /> New invoice </Button> </div> <div class="grid grid-cols-1 gap-4 sm:grid-cols-3"> <Card v-for="item in summary" :key="item.label" class="shadow-xs"> <CardContent class="space-y-1"> <p class="text-muted-foreground text-sm">{{ item.label }}</p> <p class="text-2xl font-bold tracking-tight tabular-nums" :class="item.tone">{{ item.value }}</p> <p class="text-muted-foreground text-xs">{{ item.note }}</p> </CardContent> </Card> </div> <div class="bg-card overflow-x-auto rounded-lg border shadow-xs"> <Table> <TableHeader> <TableRow> <TableHead>Invoice</TableHead> <TableHead>Client</TableHead> <TableHead>Issued</TableHead> <TableHead>Due</TableHead> <TableHead class="text-right">Amount</TableHead> <TableHead>Status</TableHead> <TableHead class="w-12"><span class="sr-only">Actions</span></TableHead> </TableRow> </TableHeader> <TableBody> <TableRow v-for="invoice in invoices" :key="invoice.id"> <TableCell class="font-mono text-xs font-medium">{{ invoice.number }}</TableCell> <TableCell> <div class="flex items-center gap-2"> <Avatar class="size-7"> <AvatarFallback class="text-xs">{{ initials(invoice.client) }}</AvatarFallback> </Avatar> <span class="truncate text-sm font-medium">{{ invoice.client }}</span> </div> </TableCell> <TableCell class="text-muted-foreground text-sm">{{ invoice.issued }}</TableCell> <TableCell class="text-muted-foreground text-sm">{{ invoice.due }}</TableCell> <TableCell class="text-right font-medium tabular-nums">{{ invoice.amount }}</TableCell> <TableCell> <Badge :variant="invoice.status === 'draft' ? 'secondary' : undefined" :class="cn('capitalize', statusClass[invoice.status])" > {{ invoice.status }} </Badge> </TableCell> <TableCell> <DropdownMenu> <DropdownMenuTrigger as-child> <Button variant="ghost" size="icon-sm" class="text-muted-foreground"> <MoreHorizontal aria-hidden="true" /> <span class="sr-only">Open actions for {{ invoice.number }}</span> </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end"> <DropdownMenuItem> <Eye aria-hidden="true" /> View </DropdownMenuItem> <DropdownMenuItem> <Download aria-hidden="true" /> Download </DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem :disabled="invoice.status === 'paid' || invoice.status === 'draft'"> <Check aria-hidden="true" /> Mark as paid </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </TableCell> </TableRow> </TableBody> </Table> </div> <div class="flex items-center justify-between"> <p class="text-muted-foreground text-sm">Showing {{ invoices.length }} of 42 invoices</p> <div class="flex items-center gap-2"> <Button variant="outline" size="sm" disabled>Previous</Button> <Button variant="outline" size="sm">Next</Button> </div> </div> </div> </template>
Raw manifest:https://uipkge.dev/r/vue/invoices-list.json