Skip to content

ethanwithnoe/Tabula

Repository files navigation

HerLedger

Donation intake and inventory intelligence system for WellSpring Women's Center, Sacramento.

What it does

  • Intake — staff scan barcodes or manually enter donated items on their phone
  • Classification — Gemini 2.5 Flash automatically sorts items into categories
  • Review — low-confidence items go to a human review queue
  • Dashboard — category breakdowns and weekly trends
  • Give Out — record items distributed to guests
  • Ask — natural-language questions answered from live inventory data

Stack

Layer Technology
Frontend React + Vite + TailwindCSS → Cloudflare Pages
Backend AWS Lambda (Node 20) behind API Gateway → Serverless Framework v3
Database & Auth Supabase (Postgres + RLS + Auth)
AI Gemini 2.5 Flash (classification + Q&A)
Barcode scanning ZXing WASM (in-browser)

Environment variables

Frontend (apps/web/.env)

VITE_SUPABASE_URL=https://<project>.supabase.co
VITE_SUPABASE_ANON_KEY=<anon key>
VITE_API_BASE_URL=https://<api-gateway-id>.execute-api.us-west-2.amazonaws.com/dev

Backend (set in PowerShell before deploying)

SUPABASE_URL
SUPABASE_SERVICE_ROLE_KEY
GEMINI_API_KEY
OLLAMA_HOST   (default: http://localhost:11434)

Local development

pnpm install
pnpm --filter web dev        # frontend on http://localhost:5173

Deploying the backend (API)

Run these in PowerShell from the repo root every time you change anything in apps/api/:

$env:SUPABASE_URL = "https://<project>.supabase.co"
$env:SUPABASE_SERVICE_ROLE_KEY = "<service role key>"
$env:GEMINI_API_KEY = "<gemini key>"
$env:OLLAMA_HOST = "http://localhost:11434"

cd apps/api
pnpm build
serverless deploy

pnpm build runs esbuild and outputs a single dist/index.js. serverless deploy packages and uploads it to AWS Lambda.

After a successful deploy, Serverless prints the new API Gateway URL. If it changed, update VITE_API_BASE_URL in apps/web/.env and redeploy the frontend.


Deploying the frontend

Run this from the repo root every time you change anything in apps/web/:

cd C:\Users\USER\Documents\GitHub\HerLedger
pnpm --filter web build
wrangler pages deploy apps/web/dist --project-name herledger --commit-dirty=true

pnpm --filter web build compiles the React app into apps/web/dist/. wrangler pages deploy uploads it to Cloudflare Pages.


Database migrations

Migrations live in packages/db/migrations/. They are numbered and applied in order. Never edit an already-applied migration — create a new one instead.

To apply a new migration, paste its contents into the Supabase SQL Editor and run it.

Current migrations:

File What it does
0001_initial_schema.sql Core tables: users, items, categories, item_classifications, daily_snapshots, audit_log
0002_rls_policies.sql Row Level Security policies
0003_functions.sql refresh_daily_snapshots, forecast_days_remaining, retrieve_chunks
0004_user_profile_trigger.sql Auto-creates a public.users row on Supabase Auth signup
0005_classification_runs.sql Tracks each classification run (status, items processed)
0006_distributions.sql Records items given out to guests

Seed data (run once after initial setup):

packages/db/seed/categories.sql   — inserts all 30 item categories

Classification

  1. Staff scan items in Intake — each item is saved as pending
  2. Press Run Classification on the Dashboard
  3. Lambda calls Gemini 2.5 Flash for each pending item
  4. Items classified with confidence ≥ 75% → classified
  5. Items below threshold → needs_review (appear in Review queue)
  6. After all items are processed, refresh_daily_snapshots() updates the dashboard

Gemini free tier: 20 requests/day for gemini-2.5-flash. Enable billing at aistudio.google.com for production use.


Troubleshooting

"0 items classified" after pressing Run Classification Items are not in pending status. Run in Supabase SQL Editor:

UPDATE items SET status = 'pending'
WHERE id NOT IN (SELECT DISTINCT item_id FROM item_classifications);

Classification keeps looping / never finishes Items stuck as needs_review with no classification record. Same fix as above.

Dashboard shows wrong counts Refresh the snapshot manually:

SELECT refresh_daily_snapshots();

429 Too Many Requests from Gemini Free tier quota exhausted (20 req/day). Wait until tomorrow or enable billing.

API returns 500 Check AWS CloudWatch: Console → CloudWatch → Log groups → /aws/lambda/herledger-api-dev-api → latest log stream.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors