Case study · Progressive web app

Masraf

A personal money tracker that lives on a phone's home screen. It answers one question first: how much can I actually spend this month, after cards, instalments and subscriptions are paid?

  • Next.js 16
  • React 19
  • Supabase · Postgres
  • PWA
  • Claude API
Overview screen: this month's free-to-spend amount on a teal card, upcoming payments, notes
In-app assistant answering where the month's money went
Twelve-month calendar of large yearly payments
1 userbuilt around one person's real month
1,600+unit tests, no network needed
400database tests on Postgres 16
0store listings. It installs from the browser
Why I built it

Spreadsheets could not answer the question I kept asking

Most money apps tell you what you spent. I wanted to know what I can still spend, in a household with two credit cards, two overdraft accounts, a loan, instalment purchases and subscriptions that renew on different days. A spreadsheet needed hand-editing every week and never knew a card's due date.

So I made the app around that one question and let everything else (accounts, instalments, reminders, charts) serve it. It runs on my own phone every day, which keeps the feature list honest.

Overview

One number before anything else

The top card shows what is left to spend this month. It starts from cash plus any savings that earn interest every day (money that can be moved to pay a bill when needed) and subtracts the card bills due this month, the loan instalments and the subscriptions still to be charged. It also shows the figure you would have if you only paid the card minimums.

  • Upcoming card payments and subscriptions sit directly under the number
  • Amounts are hidden every time the app opens; one tap reveals them
  • The month card compares spending with the same days of last month, so a half-finished month is never compared with a whole one

The app is in Turkish. "Bu ay serbest" means "free this month".

Overview with free-to-spend card
Monthly spending card with category breakdown and recent transactions
Accounts

Cards, overdrafts, a loan and savings in one list

Each account type behaves the way its bank does. Credit cards track debt and remaining limit, overdraft accounts spend cash first and debt second, loans count down their instalments, and savings that earn daily interest count as spendable money while pension and fund balances stay out of it.

  • Statement amount, minimum payment and due date per card
  • Weekend and public-holiday rules move due dates the way banks do
  • Tap a row to open details in a bottom sheet
Accounts grouped into cards, deposit and overdraft, with limits and due dates
Analysis

Where did it go?

Pick a month and see its spending sorted by size, with each category's share and how it compares with the previous month. A toggle switches between the month a purchase was made and the month its card payment lands, which matters as soon as instalments exist.

Analysis screen with month chips and a category breakdown
Add

Four ways in, one thumb

Type an amount, scan a receipt, record a payment between accounts, or log a fuel stop. Accounts and categories are large tap targets, the last account is remembered, and a warning appears if the amount is more than the card's remaining limit.

  • Receipt photos are read by a vision model; a human confirms before saving
  • Fuel entries keep litres, price per litre and odometer, and feed the vehicle screen
  • Every control is at least 44 px tall
Add screen with amount field, account grid and category choice
Subscriptions

The yearly bills that arrive without warning

Monthly or yearly, charged to cards or accounts. Yearly items appear 30 days before renewal with a "renew?" prompt, and a twelve-month calendar adds up insurance, annual fees and vehicle tax so the month they hit is no surprise.

  • It suggests how much to set aside per month for the big ones
  • Amounts that are not known yet are shown as unknown, never guessed
Subscriptions list with monthly load and upcoming renewals
Twelve-month calendar of large payments with a monthly set-aside suggestion
Assistant

Ask in plain language, get numbers you can trust

Kâtip, the in-app assistant, answers questions about the user's own accounts: spending, statements, upcoming payments, net worth. It is rule-based first. The language model only helps classify a sentence it cannot parse, and it never computes a figure.

  • The server calculates every number from the database
  • Read-only: it can draft an entry, but only the user saves it
  • A daily cap keeps the language-model cost predictable
  • Sentences it fails to understand are logged anonymously, so the rules can be extended by hand. It does not learn on its own
Assistant listing the month's spending by category
Assistant showing a credit card's statement, minimum payment and a note on what it cannot see
Themes

Three moods, none of them pitch black

A light paper theme, a warm mid-dark theme and a plum theme. Colours are defined once as tokens, a lint rule rejects raw colour values, and animations play once and respect the reduced-motion setting.

Light theme
Light
Warm dark theme
Warm dark
Plum theme
Plum
Lessons from real bugs

What went wrong, and what it changed

Money input

A comma that silently changed the amount

A number field dropped the Turkish decimal comma, so 12.345,67 was saved as 12,35. It was caught during real use. Amount fields are now text inputs with their own parser, and a test covers every format.

Proof, not trust

Matching the bank to the kuruş

Daily savings interest was reproduced from a formula and checked against seven days of real bank statements until the totals matched exactly. When the bank changed the rate, the difference showed up the same week and was corrected.

Silent failure

Pop-ups that had no background

A styling class that was never defined produced no CSS and no error, so dialogs had no background. The fix was a check that fails the build when a class has no style behind it.

Cost

Twelve requests where one was needed

The notes card fired a dozen parallel AI requests while data was still loading. Looking at the request log, not the code, found it. It now waits for the data and shares one request. Lesson: count calls before you pay per call.

Under the hood

Engineering decisions that matter more than the screens

Money logic lives in the database

Every balance change goes through a Postgres function in a single transaction. The browser never calculates a balance, and an audit function can prove the stored totals match the ledger.

Security on the server

Row-level security on every table, no anonymous access, email one-time-code sign-in, a hashed on-device PIN that signs out after five misses, and security headers. Hiding something on screen is never the protection.

Tested without a network

Pure helpers for dates, instalments, statements and the assistant's sentences are covered by unit tests, and the SQL has its own suite that CI runs against Postgres 16 on every push.

Careful with AI and privacy

Bank statement PDFs go only to Claude, never to a free-tier model that may be reviewed by people. The assistant sends the user's sentence, not their figures. Card numbers, expiry dates and CVVs are deliberately never stored.

Backups with a restore path

A weekly encrypted backup is emailed to the owner, and a restore screen opens it with the passphrase. A backup that cannot be restored is not a backup.

Built for a phone first

Designed at 390 px wide: no sideways scroll, tables turn into cards, large tap targets, and a home-screen install with an offline shell.

Timeline

Eighty-plus releases in a few weeks

  1. September 2026First version. Moving it to a clean base: money logic into the database, tests and continuous integration added.
  2. Early OctoberVersion 2.0: the large page file split up, instalments and card statements calculated on the server, security hardening, email sign-in and PIN.
  3. Mid OctoberLoans, savings interest, income and salary handling, a new identity with a warmer palette, three themes.
  4. Late OctoberEncrypted weekly backups, the in-app assistant, notes and reminders, a morning summary email.
  5. End of OctoberYearly subscriptions and the twelve-month calendar of large payments; the assistant's log for extending its rules by hand.
  6. Early NovemberThe code base reorganised by feature, with a check that fails the build when a file grows past 500 lines; secret and workflow-security scans added to continuous integration; the spendable figure now counts savings that earn daily interest.
About this project

Built with Claude. The product decisions, the real-world money rules and the testing on a real phone are mine. The code was written with Claude as a pair programmer, in short branches with tests and a changelog for every release.

Screens use invented data. Account names, amounts and the vehicle shown here are made up for this page. The app's interface is in Turkish because it was built for a Turkish household and Turkish banks.