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 uipkge-ng@latest add invoices-list$npx uipkge-ng@latest add invoices-list$yarn dlx uipkge-ng@latest add invoices-list$bunx uipkge-ng@latest add invoices-listManifest:
https://uipkge.dev/r/angular/invoices-list.jsonInstalls to:src/app/components/blocks/invoices-list/Variants
Loading interactive previews…
Loading interactive previews…
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 (2)
src/app/components/blocks/invoices-list/invoices-list.component.ts9.3 kB
import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { Check, Download, Eye, LucideAngularModule, MoreHorizontal, Plus } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component' import { UiDropdownMenuComponent, UiDropdownMenuContentComponent, UiDropdownMenuItemComponent, UiDropdownMenuSeparatorComponent, UiDropdownMenuTriggerComponent, } from '@/ui/dropdown-menu/dropdown-menu.component' import { UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, } from '@/ui/table/table.component' export type InvoiceStatus = 'paid' | 'pending' | 'overdue' | 'draft' export interface Invoice { id: string number: string client: string issued: string due: string amount: string status: InvoiceStatus } 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', }, ] /** * InvoicesList -- the React block, 1:1: 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. */ @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-invoices-list, [ui-invoices-list]', standalone: true, // React renders the root <div> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiAvatarComponent, UiAvatarFallbackComponent, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiDropdownMenuComponent, UiDropdownMenuContentComponent, UiDropdownMenuItemComponent, UiDropdownMenuSeparatorComponent, UiDropdownMenuTriggerComponent, UiTableComponent, UiTableBodyComponent, UiTableCellComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, ], template: ` <div data-slot="invoices-list" [class]="rootClass"> <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 ui-button> <lucide-icon [img]="Plus" /> New invoice </button> </div> <div class="grid grid-cols-1 gap-4 sm:grid-cols-3"> @for (item of summary; track item.label) { <div ui-card class="shadow-xs"> <div ui-card-content 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> </div> </div> } </div> <div class="bg-card overflow-x-auto rounded-lg border shadow-xs"> <table ui-table> <thead ui-table-header> <tr ui-table-row> <th ui-table-head>Invoice</th> <th ui-table-head>Client</th> <th ui-table-head>Issued</th> <th ui-table-head>Due</th> <th ui-table-head class="text-right">Amount</th> <th ui-table-head>Status</th> <th ui-table-head class="w-12"><span class="sr-only">Actions</span></th> </tr> </thead> <tbody ui-table-body> @for (invoice of invoices; track invoice.id) { <tr ui-table-row> <td ui-table-cell class="font-mono text-xs font-medium">{{ invoice.number }}</td> <td ui-table-cell> <div class="flex items-center gap-2"> <ui-avatar class="size-7"> <ui-avatar-fallback class="text-xs">{{ initials(invoice.client) }}</ui-avatar-fallback> </ui-avatar> <span class="truncate text-sm font-medium">{{ invoice.client }}</span> </div> </td> <td ui-table-cell class="text-muted-foreground text-sm">{{ invoice.issued }}</td> <td ui-table-cell class="text-muted-foreground text-sm">{{ invoice.due }}</td> <td ui-table-cell class="text-right font-medium tabular-nums">{{ invoice.amount }}</td> <td ui-table-cell> <span ui-badge [variant]="invoice.status === 'draft' ? 'secondary' : 'default'" [class]="badgeClass(invoice.status)" > {{ invoice.status }} </span> </td> <td ui-table-cell> <ui-dropdown-menu> <button ui-button ui-dropdown-menu-trigger variant="ghost" size="icon-sm" class="text-muted-foreground" > <lucide-icon [img]="MoreHorizontal" /> <span class="sr-only">Open actions for {{ invoice.number }}</span> </button> <ui-dropdown-menu-content align="end"> <ui-dropdown-menu-item> <lucide-icon [img]="Eye" /> <span>View</span> </ui-dropdown-menu-item> <ui-dropdown-menu-item> <lucide-icon [img]="Download" /> <span>Download</span> </ui-dropdown-menu-item> <ui-dropdown-menu-separator /> <ui-dropdown-menu-item [disabled]="invoice.status === 'paid' || invoice.status === 'draft'"> <lucide-icon [img]="Check" /> <span>Mark as paid</span> </ui-dropdown-menu-item> </ui-dropdown-menu-content> </ui-dropdown-menu> </td> </tr> } </tbody> </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 ui-button variant="outline" size="sm" disabled>Previous</button> <button ui-button variant="outline" size="sm">Next</button> </div> </div> </div> `, }) export class UiInvoicesListComponent { protected readonly Check = Check protected readonly Download = Download protected readonly Eye = Eye protected readonly MoreHorizontal = MoreHorizontal protected readonly Plus = Plus protected readonly summary = summary protected readonly invoices = invoices @Input('class') className?: string get rootClass(): string { return cn('w-full space-y-6', this.className) } badgeClass(status: InvoiceStatus): string { return cn('capitalize', statusClass[status]) } initials(name: string): string { return name .split(' ') .map((part) => part[0]) .slice(0, 2) .join('') .toUpperCase() } }src/app/components/blocks/invoices-list/index.ts0.1 kB
export { UiInvoicesListComponent, type Invoice, type InvoiceStatus } from './invoices-list.component'
Raw manifest:https://uipkge.dev/r/angular/invoices-list.json