What changed after launch
Month-end reporting dropped from two days to under an hour for the pilot agencies.
- Next.js
- TypeScript
- PostgreSQL
- tRPC
- Playwright
Key figures
- Lighthouse
- 98
- Load time
- −60%
- Active users
- 2k
Dashboard
A finance dashboard for small agencies that turns invoices, time tracking and bank feeds into one live cash-flow view.

The agencies we interviewed kept invoices, timesheets and bank exports in three different tools. Month-end reporting took two days of spreadsheet work and was outdated the moment it was finished.
The new product had to import all three sources, reconcile them automatically and stay fast on the low-end laptops most account managers use.
How it was built
Six phases from the first conversation to the release. The bar shows how the time was split.
Interviews with agency owners and a look at their month-end spreadsheets showed where the two days went.
Data model, rendering strategy and API contracts were agreed before the first screen was drawn.
A component inventory and responsive layouts, built entirely from design tokens.
Features went live in small slices behind flags, with typed boundaries between client and server.
Unit, end-to-end and accessibility checks ran on every pull request.
Every branch got a preview environment; the production rollout was a single click.
Month-end reporting dropped from two days to under an hour for the pilot agencies.
Outcome

Month-end reporting dropped from two days to under an hour for the pilot agencies.
Key figures
Every report is rendered on the server from materialised views, so the first paint never waits for client JavaScript. Only the filters, the date range picker and the inline editors hydrate.
The reconciliation engine runs as a queue worker. It scores candidate matches by amount, date distance and reference similarity, and only asks a human when two candidates score within five points of each other.
export function score(tx: Tx, inv: Invoice) {
const amount = tx.amount === inv.total ? 50 : 0;
const days = Math.abs(daysBetween(tx.date, inv.due));
const date = Math.max(0, 30 - days);
const ref = similarity(tx.reference, inv.number) * 20;
return amount + date + ref;
}Architecture
React Server Components render every report; client islands handle filters and inline edits with optimistic updates.

tRPC procedures over PostgreSQL with row-level security. Materialised views refresh after each import.

Each pull request gets its own database branch and preview URL; production deploys are atomic and reversible.

Next project
Keep exploring