Founding price: $35, then $79.99 after 23:59 UTC on 4 November, then $79.99
Get ProPro starter app. Needs a tealmac UI Pro licence. Set it up once on /pro/login, see Install Pro.
Ledger console is a back-office app for a payments company, built from tealmac UI components and the shadcn primitives. Look around the live preview first; every page works, and the data is generated locally so it renders the same everywhere.
| Page | Route | What it shows |
|---|---|---|
| Overview | /templates/ledger-console | KPIs with sparklines for 7, 30 or 90 days, a volume chart against the previous period, payment methods, alerts, recent payments, top customers |
| Transactions | …/transactions | A TanStack Table data grid with 336 rows: search, faceted filters, sorting, column picker, page sizes, row selection, a bulk action bar, a detail drawer, refunds and CSV export |
| Customers | …/customers | Segment tabs, search, sorting. A stacked list on phones, a table on wider screens |
| Customer | …/customers/cus-1001 | Lifetime figures, monthly chart, payments, profile and team notes. Also shown with a Vietnamese name, a Japanese name and a customer with no payments |
| Reports | …/reports | A date range with presets, daily or weekly grouping, volume, refunds, payment counts, method split, countries, a retention heat table and CSV export |
| Audit log | …/audit-log | Events grouped by day, filters, expandable before and after diffs and raw payloads, load more |
| Access | …/access | A role and permission checkbox matrix, members with role selects, invites and a forbidden state (?as=support) |
| Settings | …/settings | Organisation, security switches, alert preferences, API keys and webhooks |
The shell has a collapsible sidebar, an organisation switcher, a command menu (⌘ K or Ctrl K), a Live and Test mode switch, an alerts menu and a drawer for phones.
Every list page can be put into its other states with a query string:
?state=empty, ?state=loading or ?state=error. Searching for something
that does not exist shows the filtered empty state with a Clear filters
button.
Add your licence key and the Pro registry once per project, as described in Install Pro:
TEALMAC_UI_LICENSE_KEY=your-licence-key{
"registries": {
"@tealmac-pro": {
"url": "https://ui.tealmac.com/r/pro/{name}.json",
"headers": {
"Authorization": "Bearer ${TEALMAC_UI_LICENSE_KEY}"
}
}
}
}Then install the app:
The CLI also installs the shadcn primitives and tealmac UI components the app
uses, and the @tanstack/react-table, recharts, sonner and lucide-react
packages.
components/tealmac-pro/ledger-console/
ledger-console-shell.tsx sidebar, top bar, command menu, shared helpers
ledger-console-data.ts generated mock data and formatters
ledger-console-transactions.tsx the data grid
ledger-console-*.tsx one file per page
app/templates/ledger-console/
page.tsx overview
transactions/page.tsx
customers/page.tsx
customers/cus-1001/page.tsx … and the other customer pages
reports/page.tsx
audit-log/page.tsx
access/page.tsx
settings/page.tsxMove the routes wherever you like. Navigation links are built in one place,
the ledgerHref object in ledger-console-shell.tsx; change its BASE
constant if you mount the app somewhere other than /templates/ledger-console.
The shell mounts its own toast container, so you don't need to add one.
Every page imports from ledger-console-data.ts. The page components only
rely on the exported types (Transaction, Customer, AuditEvent, Member,
and so on), so you can swap the arrays for your own data.
For the 336-row grid the data lives in the browser, which is fine up to a few thousand rows. For larger tables, switch the grid to server-side work with TanStack Table's manual modes and keep the state you already have:
const table = useTable({
features,
data: page.rows, // one page from your API
columns,
rowCount: page.total, // total matching rows
manualPagination: true,
manualSorting: true,
getRowId: (row) => row.id,
state: { sorting, columnVisibility, rowSelection, pagination },
onSortingChange: setSorting,
onPaginationChange: setPagination,
onRowSelectionChange: setRowSelection,
})Send sorting, pagination, the search text and the filter values to your
API (as query parameters or a server action), and render the response. Keep
getRowId stable so that row selection survives paging. The StateView
around the table already handles loading, error and empty results; drive its
status from your request instead of from ?state=.
The usePreviewState hook in the shell only powers the ?state= preview.
Delete it once the pages use real requests.
CSV export builds the file in the browser from the filtered rows. For very large exports, call a route handler that streams the file instead.
The app uses semantic tokens only: background, card, border, primary,
muted, accent, destructive, ring and chart-1 to chart-5. It follows
whichever theme preset and light or dark mode your project uses, with no
colours of its own. Status colours are small dots and tints; labels always
keep the foreground colour so contrast holds in every preset.
h1, a main landmark, a skip link and labelled
navigation. The current page is marked with aria-current.aria-sort. Every row checkbox is named after its payment and customer.prefers-reduced-motion. The charts do not animate.