App, invoice list
Install
npx shadcn add @klarhimmel/app-billing-invoicesUsage
import { Theme } from '@/components/klarhimmel/theme/style';
import { BlockSurface } from '@/components/klarhimmel/blocks/surface';
import AppBillingInvoices from '@/components/klarhimmel/blocks/app-billing-invoices';
export default function Section() {
return (
<Theme theme="brand">
<BlockSurface>
<AppBillingInvoices props={{
"eyebrow": "Billing",
"title": "Invoices",
"description": "Every invoice for Exempelhotellet. Download one as a PDF.",
"caption": "Invoices, newest first",
"columns": {
"number": "Invoice",
"date": "Date",
"amount": "Amount",
"status": "Status"
},
"rows": [
{
"number": "INV-1042",
"date": "1 Jun 2026",
"amount": "490 kr",
"status": "due",
"href": "#inv-1042"
},
{
"number": "INV-1031",
"date": "1 May 2026",
"amount": "490 kr",
"status": "paid",
"href": "#inv-1031"
},
{
"number": "INV-1019",
"date": "1 Apr 2026",
"amount": "490 kr",
"status": "paid",
"href": "#inv-1019"
},
{
"number": "INV-1007",
"date": "1 Mar 2026",
"amount": "490 kr",
"status": "failed",
"href": "#inv-1007"
}
],
"statusLabels": {
"paid": "Paid",
"due": "Due",
"failed": "Failed"
},
"download": "Download PDF",
"scrollHint": "Scroll sideways for more columns",
"recovery": {
"payAgain": "Pay again",
"updateCard": {
"label": "Update card",
"href": "#card"
},
"processing": "Processing",
"said": "Payment attempt started. We will email you the result."
},
"state": "filled",
"messages": {
"emptyTitle": "No invoices yet",
"emptyBody": "Your first invoice appears here at the end of the first billing month."
}
}} />
</BlockSurface>
</Theme>
);
}Install
Import
import Block from '@/components/klarhimmel/blocks/app-billing-invoices';- app/billing-invoices
- About
- A list of invoices as a table: number, date, amount, a status badge (paid, due, failed: always a word) and a download link named for its invoice that stays in view while the other columns scroll, and a failed invoice that can be paid…
- Category
- Interface sections for signed-in products.
Notes
When to use it
A table of past invoices with status and a download link per row, for a billing page. For the current plan and usage use `app-billing-plan`; for a generic table of other data use `app-data-table`.
Writing for it
- Rows take 1 to 8 entries. Each has a number, date and amount of 30 characters at most, a status of paid, due or failed, and an `href` for the download file. - Set `download` (30) to a short verb like Download; the invoice number is appended for screen readers so the link is not just the same word eight times. - Use `recovery` only for failed rows: `payAgain` and an `updateCard` link appear there, and `said` (80) is the announcement after a retry.
Watch out for
Eight rows is the ceiling, so a long history needs `app-data-pagination` or a link elsewhere. In the empty state `messages.emptyTitle` is required, and the Pay again button only changes the row to processing locally; it does not charge anything.
Pairs well with
Usage
The block in a page, with its default content (long lists shortened). Every prop is in the table below.
import { Theme } from '@/components/klarhimmel/theme/style';
import { BlockSurface } from '@/components/klarhimmel/blocks/surface';
import AppBillingInvoices from '@/components/klarhimmel/blocks/app-billing-invoices';
export default function Section() {
return (
<Theme theme="brand">
<BlockSurface>
<AppBillingInvoices props={{
"eyebrow": "Billing",
"title": "Invoices",
"description": "Every invoice for Exempelhotellet. Download one as a PDF.",
"caption": "Invoices, newest first",
"columns": {
"number": "Invoice",
"date": "Date",
"amount": "Amount",
"status": "Status"
},
"rows": [
{
"number": "INV-1042",
"date": "1 Jun 2026",
"amount": "490 kr",
"status": "due",
"href": "#inv-1042"
}
],
"statusLabels": {
"paid": "Paid",
"due": "Due",
"failed": "Failed"
},
"download": "Download PDF",
"scrollHint": "Scroll sideways for more columns",
"recovery": {
"payAgain": "Pay again",
"updateCard": {
"label": "Update card",
"href": "#card"
},
"processing": "Processing",
"said": "Payment attempt started. We will email you the result."
},
"state": "filled",
"messages": {
"emptyTitle": "No invoices yet",
"emptyBody": "Your first invoice appears here at the end of the first billing month."
}
}} />
</BlockSurface>
</Theme>
);
}Props
The type, default and text limit of every prop. On a real page, over-limit text is kept and checkSections flags it (Installation, Check a page before you ship); this live preview shows the default until it fits. The panel above edits all props.
| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | 'Billing' | Text, up to 60 characters. Optional; empty is not rendered. |
title | string | 'Invoices' | Text, up to 120 characters. |
description | string | 'Every invoice for Exempelhotellet. …' | Text, up to 300 characters. Optional; empty is not rendered. |
caption | string | 'Invoices, newest first' | Text, up to 80 characters. |
columns | { number, date, amount, status } | {…} | Fields: number, date, amount, status. |
rows | { number, date, amount, status, href }[] | 4 items | List of 1 to 8. Each item: number, date, amount, status ('paid' | 'due' | 'failed'), href. |
statusLabels | { paid, due, failed } | {…} | Fields: paid, due, failed. |
download | string | 'Download PDF' | Text, up to 30 characters. |
scrollHint | string | 'Scroll sideways for more columns' | Text, up to 40 characters. |
recovery | { payAgain, updateCard, processing, said } | {…} | Fields: payAgain, updateCard, processing, said. |
state | 'filled' | 'empty' | 'filled' | One of 'filled', 'empty'. |
messages | { emptyTitle, emptyBody } | {…} | Fields: emptyTitle, emptyBody. |
headingLevel | '1' | '2' | - | One of '1', '2'. Optional; empty is not rendered. |