How it’s built
For anyone who wants to see how this was made: who built it, why, and what is under it.
Who built it
I’m Ahmed Malik, a computer science graduate from UET Lahore. I built CommitteeKeeper as the fourth of ten portfolio projects, aiming for work as a developer, business analyst or project manager.
I wrote the requirements, the business rules, the database design and the test cases first, then built the product against them one issue at a time, each with its own tests.
The story
A kameti (also called a BC) is a savings committee. Everyone puts in the same amount every month and one member takes the whole pot, until every member has had a turn. It runs on trust, a notebook and a WhatsApp group.
Before writing any code I talked to an organiser who runs three or four committees at once and is a member in all of them. Her hassles became the requirements: chasing late payers, recalculating when two members swap turns, and working out what someone is owed when they leave and are replaced.
The problem
One person keeps the whole record by hand: who has paid, who is late, whose turn it is and what was agreed. When two people remember it differently, there is nothing to settle it with, and the organiser carries the blame.
CommitteeKeeper gives the organiser one place for all of it, and a history that nobody can quietly change. It only keeps the record: it never holds or moves money.
- Set up a committee with a written agreement, members and slots, and decide turns by hand or by a draw.
- Record payments with up to two receipt images each; see who is late; hand over each pot, with the split worked out when a slot changed hands.
- A permanent history: corrections are new entries, and a chain check shows if anything was altered.
- A daily summary and ready-to-send WhatsApp messages for whoever is due or late.
- A private link for each member, and an optional view-only account, so members can answer their own questions.
The stack
- Interface
- Next.js 16 (App Router, Server Components and Server Actions), React 19, TypeScript
- Database and sign-in
- Supabase: Postgres with row-level security on every table, Auth, private file storage
- Hosting
- Vercel, in the Mumbai region, close to the database
- Tests
- Vitest for the rules, Playwright for the screens
- Fonts
- Red Hat Display and Red Hat Mono
How it fits together
The rules live in small pure TypeScript files with no screens and no database in them (who is late, how a pot is split, what a history entry says), so they can be tested and explained on their own.
Every change to money or turns goes through a database function that checks the rules again and writes the history entry in the same step, so the app cannot forget to log something. Members never read tables: they can only call functions that return their own information and nothing else.
Receipts sit in a private bucket and are opened through links that stop working after a minute. A hash chain links every history entry to the one before it, and the History page re-checks it each time it opens.
How it is tested
Unit tests cover the rules. System tests drive the real screens: every test in desktop Chrome, the core flows again in desktop Edge, and the layout, touch and accessibility checks in a 375 px phone view. Test data is made through the database so the suite stays fast, and a short smoke set is run against the live site after each deploy. A security script acts as each role (signed out, another organiser, a member) and checks what each can and cannot read or change.
Defects found by testing were fixed in their own commits. The history of what was found and fixed is in the repository.
As of 19 October 2026: 189 unit tests and 226 system tests (runs counted across desktop Chrome, desktop Edge for the core flows, and a 375 px phone view for layout, touch and accessibility).
Links
Credits
Built with Next.js, React, Supabase, Vitest and Playwright, and qrcode.react (ISC licence) for the QR codes. Fonts: Red Hat Display and Red Hat Mono, licensed under the SIL Open Font License. This project is released under the MIT licence.