Skip to content

meemekelvin/trader-app

Repository files navigation

DukaBooks — Trader Accounting App

A mobile-first Progressive Web App (PWA) that helps small-scale traders and informal vendors track cash flow, log transactions by voice, scan receipts, and visualise daily profit and loss — with full offline support.

Live demo: https://shiny-croquembouche-525f4b.netlify.app


Table of Contents

  1. Features
  2. Tech Stack
  3. Project Structure
  4. Getting Started
  5. Feature Guide
  6. API Key Setup
  7. Offline Support
  8. Running Tests
  9. Deployment
  10. Security Notes

Features

Feature Description
Voice-to-Ledger Speak a transaction naturally — "Sold 2kg of tomatoes for 200 shillings" — and the app parses it into a structured record
AI Voice (Gemini) Tap-to-record using Google Gemini for higher accuracy and multi-language support
Receipt Scanning Photograph or upload a receipt; Tesseract.js OCR extracts line items and totals automatically
Financial Dashboard Live summary cards (today's income, expenses, profit) plus a 7-day bar chart
Transaction Ledger Full history filterable by type (income / expense) and month, grouped by date
Offline Resilience All data stored in IndexedDB; service worker caches the app shell; works with no internet after first load
PWA Install Installable to the home screen on Android and iOS

Tech Stack

  • Frontend: Vanilla HTML, CSS, JavaScript — no framework, no bundler
  • Storage: IndexedDB (via a custom wrapper in lib/db.js)
  • Voice (primary): Web Speech API (Chrome / Edge)
  • Voice (AI fallback): Google Gemini API via MediaRecorder
  • OCR: Tesseract.js v4 (loaded from CDN, cached by service worker)
  • Charts: Custom Canvas API — no chart library needed
  • Offline: Service Worker with network-first strategy for app shell, cache-first for CDN assets
  • Hosting: Netlify
  • Tests: Jest + fake-indexeddb

Project Structure

trader-app/
│
├── index.html          # App shell — all four views (Dashboard, Add, Ledger, Scan)
├── app.css             # Mobile-first styles
├── app.js              # Main application logic (DB, Voice, Gemini, OCR, Charts, UI)
├── sw.js               # Service worker — offline caching
├── manifest.json       # PWA manifest — enables "Add to Home Screen"
├── netlify.toml        # Netlify deploy config with correct cache headers
├── favicon.svg         # App icon (SVG)
├── icon-192.svg        # PWA icon 192×192
├── icon-512.svg        # PWA icon 512×512
│
├── lib/                # Pure, testable modules (UMD — work in browser + Node)
│   ├── helpers.js      # todayISO, fmtDate, fmtAmount, esc, last7Days
│   ├── nlp.js          # Voice NLP parser — extracts type, amount, category
│   ├── ocr-parser.js   # Receipt text parser — finds totals and line items
│   ├── stats.js        # computeStats — income / expense / profit from a row set
│   └── db.js           # IndexedDB wrapper — open, add, getAll, remove, stats
│
├── tests/              # Jest test suite
│   ├── helpers.test.js
│   ├── nlp.test.js
│   ├── ocr-parser.test.js
│   ├── stats.test.js
│   └── db.test.js
│
├── serve.py            # Minimal local dev server (Python 3, no dependencies)
├── package.json        # Jest + fake-indexeddb dev dependencies
│
├── .env                # Gemini API key — NOT committed
├── config.js           # Exposes key to browser as window.DUKABOOKS_CONFIG — NOT committed
└── .gitignore          # Excludes .env, config.js, node_modules

Getting Started

Prerequisites

  • Python 3 (for the local dev server)
  • Node.js ≥ 18 (for running tests)
  • A modern browser — Chrome or Edge recommended for voice features

Run locally

# Clone or download the project
cd trader-app

# Start the dev server
python3 serve.py
# → http://localhost:8080

To access from a phone on the same Wi-Fi network, use your machine's local IP:

hostname -I | awk '{print $1}'
# e.g. → 10.0.0.42  →  open http://10.0.0.42:8080 on your phone

The app's core features (ledger, manual entry, charts) also work when opened directly as file:// — only the service worker (offline caching + PWA install) requires HTTP.

Install dependencies (tests only)

npm install

Feature Guide

Dashboard

The home screen shows three summary cards for today:

  • Today's Profit — income minus expenses (green = positive, red = negative)
  • Income — total money received today
  • Expenses — total money spent today

Below the cards is a 7-day bar chart (green = income, red = expenses) and a list of the five most recent transactions.


Adding a Transaction

Navigate to the Add tab. There are three ways to log a transaction:

1. Voice (Web Speech API)

Hold the 🎤 Hold to Speak button and say the transaction naturally.

Examples:

"Sold 2kg of tomatoes for 200 shillings"
"Bought 5 bags of sugar for 1500"
"Received payment of 3000 from customer"
"Paid rent 5000 shillings"
"Spent 250 on matatu fare"

The NLP parser extracts:

  • Type — income or expense (income wins if both keywords appear)
  • Amount — handles 200, 1,500, 99.50, KSh 500, 500 bob
  • Category — auto-detected from keywords (Produce, Stock, Transport, Rent, Utilities, Equipment)
  • Description — cleaned up version of the spoken text

A preview card appears. Tap Use this ✓ to load it into the form, then Save Transaction.

Note: The Web Speech API requires internet because Chrome sends audio to Google's servers for processing.

2. AI Record (Google Gemini)

Tap the 🎙 AI Record button (teal). Recording starts immediately. Tap again to stop.

The audio is sent to Google Gemini, which:

  • Supports Swahili, English, mixed language, and accents
  • Works even if Web Speech returns a "network" error
  • Returns a transcript that goes through the same NLP parser

First use will prompt for your Gemini API key (see API Key Setup).

3. Manual Entry

Fill in the form directly:

  • Toggle + Income or − Expense
  • Enter amount, description, category, and date
  • Tap Save Transaction

A confirmation sheet slides up showing the full details before saving.


Ledger

The Ledger tab shows all transactions:

  • Filter by All / Income / Expense using the tab buttons
  • Filter by month using the month picker
  • Transactions are grouped by date with a daily profit/loss summary
  • Tap × on any row to delete it
  • A small dot indicates a transaction saved while offline (pending sync)

Scanning a Receipt

Navigate to the Scan tab.

Option A — Camera:

  1. Tap 📷 Open Camera
  2. Point at the receipt
  3. Tap 📸 Capture Photo

Option B — Upload:

  1. Tap 📁 Upload from Gallery
  2. Select a photo of the receipt

The OCR engine (Tesseract.js) scans the image and:

  • If it finds a Total / Grand Total / Amount Due line, it creates one expense entry for that amount
  • Otherwise it lists all detected line items

Tap Add next to any item to load it into the Add form for review and saving.

First use: Tesseract downloads ~10 MB of language data from CDN. Subsequent uses work offline (cached by service worker).


API Key Setup

The Gemini key is used for the AI voice feature. It is never committed to git.

Local development

Create config.js in the project root:

window.DUKABOOKS_CONFIG = {
  geminiKey: 'AIzaSy...',
};

This file is loaded by index.html and listed in .gitignore.

On the hosted (Netlify) version

Because config.js is not deployed, the key is stored in the browser's localStorage on first use.

When you tap 🎙 AI Record for the first time, a prompt asks for your key. Paste it in and it is saved to localStorage on that device. You will not be prompted again on the same browser.

To update or remove the key, open the browser console and run:

localStorage.removeItem('dukabooks_gemini_key');

Offline Support

DukaBooks is designed to work in areas with poor or no connectivity.

Scenario Behaviour
Full offline (after first load) Dashboard, ledger, manual entry, and charts all work. Data saves to IndexedDB.
Offline at save time Transaction is saved locally with a synced: false flag shown as in the ledger.
Come back online The dot is informational — in a production deployment with a backend, unsynced records would be POSTed to the server here.
Tesseract OCR offline Works after first load (language data is cached by the service worker).
Web Speech API offline Fails with a friendly message. Use AI Record or manual entry instead.
Gemini AI offline Cannot reach the Gemini API — use manual entry.

The service worker uses:

  • Network-first for the app shell (index.html, app.js, app.css) so code updates are always fresh
  • Cache-first for CDN assets (Tesseract.js) since these rarely change

Running Tests

npm test

148 tests across 5 suites:

Suite Tests Coverage
tests/helpers.test.js 32 todayISO, fmtDate, fmtAmount, HTML escaping, last7Days
tests/nlp.test.js 42 Income/expense detection, amount parsing, 7 categories, edge cases
tests/ocr-parser.test.js 22 Total detection, multi-item parsing, comma/decimal amounts
tests/stats.test.js 27 Stats computation, edge cases, precision
tests/db.test.js 25 Full CRUD, all filter types, stats queries (uses fake-indexeddb)
# With coverage report
npm run test:coverage

# Watch mode during development
npm run test:watch

Deployment

The app is deployed to Netlify. To redeploy after changes:

cd trader-app
netlify deploy --dir=. --prod

First-time deploy on a new machine

npm install -g netlify-cli
netlify login
cd trader-app
netlify deploy --dir=. --prod

What gets deployed

The .netlifyignore file excludes:

.env
config.js        ← API key, never deployed
node_modules/
tests/
serve.py
package*.json

netlify.toml sets Cache-Control: no-cache on all files so updates reach users immediately, and Service-Worker-Allowed: / so the PWA service worker can intercept requests at the root scope.


Security Notes

Item Status
Gemini API key Stored in config.js (local only, git-ignored) or localStorage (per-device). Never in source code or deployed files.
IndexedDB data Stored locally in the user's browser. No data leaves the device unless Gemini AI or Tesseract OCR is used.
HTML escaping All user-supplied text is run through esc() before being inserted into the DOM, preventing XSS.
No backend The app has no server component. There is no database, no user accounts, and no data transmission beyond the Gemini API and Tesseract CDN calls.

Browser Support

Browser Voice (Web Speech) AI Record (Gemini) OCR Offline / PWA
Chrome (Android) ✅ Full PWA
Edge
Safari (iOS) ✅ (online only) ✅ Add to Home Screen
Firefox

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors