Personal finance tracker built as a Progressive Web App. Track daily expenses, direct debits, salary, and India (INR) expenses with sync to Google Sheets.
| Layer | Tool |
|---|---|
| Language | TypeScript (strict) |
| Bundler | Vite 5 |
| Charts | Chart.js 4 |
| Storage | localStorage + Google Sheets |
| PWA | vite-plugin-pwa (Workbox) |
| Tests | Vitest |
npm install
cp .env.example .env.local # add your Google Script URL
npm run dev| Command | Description |
|---|---|
npm run dev |
Start dev server |
npm run build |
TypeScript check + production build |
npm run test |
Run all unit + integration tests |
npm run test:watch |
Run tests in watch mode |
npm run lint |
TypeScript type check |
src/
├── main.ts Entry point, PIN lock, inactivity timer
├── style.css Global styles
├── env.d.ts Vite env type declarations
│
├── operations/ Business logic layer (no DOM)
│ ├── index.ts Barrel export
│ ├── types.ts All TypeScript interfaces
│ ├── config.ts Environment config + constants
│ ├── constants.ts Category/payment/source definitions
│ ├── storage.ts localStorage CRUD + cloud sync
│ ├── google-api.ts Google Apps Script API calls
│ ├── filters.ts Date utils, sorting, monthly reports
│ ├── filters.unit.ts Unit tests (co-located)
│ ├── utils.ts escapeHtml, extractMonth, generateId
│ ├── utils.unit.ts Unit tests (co-located)
│ └── pin.ts PIN auth (PBKDF2, attempt limiting)
│
└── ui/ Presentation layer (DOM manipulation)
├── app.ts Main app logic, form handlers, rendering
├── charts.ts Chart.js wrapper functions
├── export.ts CSV report generation
└── helpers.ts DOM helpers ($, showToast)
tests/
└── integration/
└── monthly-report.test.ts Integration tests
google-apps-script.js Server-side (deploy to Google)
index.html HTML shell
vite.config.ts Vite + PWA + Vitest config
The codebase follows a two-layer pattern:
src/operations/— Pure business logic. No DOM dependencies. Handles storage, API calls, validation, data transformations. Fully unit-testable.src/ui/— Presentation layer. Renders DOM, handles events, manages form state. Imports everything from../operations.tests/integration/— Integration tests that test multiple modules together.
Unit tests are co-located with their source files using the .unit.ts suffix (same pattern as the BT infrastructure repo).
- Create a Spreadsheet with sheets:
DirectDebits,DailyExpenses,Salary,IndiaExpenses,IndiaTransfers - Add headers matching the TypeScript interfaces
- Paste
google-apps-script.jsinto Apps Script - Deploy as Web App → copy URL to
.env.local
- PIN hashed with PBKDF2 (100k iterations, random salt)
- Auto-lock after 5 min inactivity
- Lockout after 5 failed attempts (60s cooldown)
- All HTML output escaped (XSS-safe)
- Google Script URL in environment variables
User Action → localStorage (instant) → Google Sheets (async)
Cloud Sync → Fetch → Merge by ID → Save merged result
Merge-based sync preserves offline additions.