Warning
This project is still in production and actively evolving. Expect ongoing changes, incomplete areas, and occasional breaking updates.
It was also built heavily with AI assistance as a personal learning tool, with the goal of accelerating exploration and understanding of modern full-stack patterns.
A personal budget tracking app built with React and Supabase. Features full offline support, recurring transactions, split expense tracking, CSV import/export, multi-account net worth tracking, and a plan-vs-actual reports page.
- Transactions — Full CRUD with filtering, soft deletes, and per-account tracking
- Budgets — Monthly budget plans with category allocations and plan-vs-actual comparisons
- Reports — Category charts, trend analysis, annual actuals table, category drill-down
- Accounts — Checking, savings, credit cards, retirement, brokerage, loans, mortgages; net worth chart
- Recurring Transactions — Template-based with grouping (e.g. grouped paycheck splits), auto-projection 30 days out, weekly/biweekly/semi-monthly/monthly/quarterly/yearly schedules
- Split Expenses — Partner-style shared expense tracking with paid-by attribution, split methods, and settlement visibility
- CSV Import — Column mapping, duplicate detection, account & category assignment, up to 5,000 rows
- Export — Excel workbook with transactions, budgets, and accounts sheets
- Categories — Custom categories with drag-and-drop sort, type groups (income/needs/wants/savings/transfer)
- Offline Support — Full offline-first with Dexie.js (IndexedDB) sync queue and last-write-wins conflict resolution
- Auth — Supabase email/password auth with per-user Row-Level Security on all data
- Theme — Persistent dark/light mode; dark is the default
| Layer | Technology |
|---|---|
| Frontend | React 19.2 + Vite 7 |
| Styling | Tailwind CSS 4.2 |
| Database | Supabase (PostgreSQL + RLS) |
| Offline DB | Dexie.js (IndexedDB) |
| Charts | Recharts 3.7 |
| Export | ExcelJS 4.4 |
| Drag & Drop | dnd-kit |
| PWA | vite-plugin-pwa |
| Date utils | date-fns 4.1 |
src/
├── App.jsx ← App routes, auth guard, recurring + sync init
├── main.jsx ← React app bootstrap
├── App.css
├── index.css
├── assets/
│ └── react.svg
├── components/
│ ├── accounts/ ← Account CRUD, NetWorthChart, NetWorthSummary
│ ├── budgets/ ← BudgetForm, CategoryList, AnnualBudgetTable, BudgetImportModal
│ ├── reports/ ← CategoryChart, PlanVsActual, Trends, CategoryDrillDown, AnnualActualsTable
│ ├── splits/ ← Shared expense split setup, tracking, and settlement views
│ ├── transactions/ ← Transaction list, form, filters, recurring forms
│ └── common/ ← Modal, TopBar, LoginForm, SignupForm, MonthYearSelector, SyncStatus, ExportData
├── constants/
│ └── pages.jsx ← Page name registry
├── contexts/ ← MonthYear, SafeMode, Theme providers + memoized values
├── hooks/ ← Month/year, theme, safe mode, session, sync, swipe, threshold, and transaction hooks
├── pages/ ← ReportsPage, TransactionsPage, BudgetPage, SplitExpensesPage, AccountsPage, CategoriesPage, SettingsPage, AuthPage
├── services/
│ ├── supabase.js ← Client init
│ ├── transactions.js ← CRUD + pagination
│ ├── budgets.js ← Plans, items, plan-vs-actual
│ ├── accounts.js ← Account CRUD + balance calc
│ ├── categories.js ← CRUD + drag-and-drop sort
│ ├── recurring.js ← Template CRUD + projection engine
│ ├── splitExpenses.js ← Split expense CRUD + partner balance logic
│ ├── partnerships.js ← Partner invite/accept/dissolve lifecycle
│ ├── import.js ← CSV parsing + dedup
│ ├── export.js ← Excel workbook generation
│ ├── analytics.js ← Multi-month trend analysis
│ ├── offlineAware.js ← Offline-first wrappers around core services
│ ├── offlineDb.js ← Dexie schema + helpers
│ └── sync.js ← Sync queue + conflict resolution
├── utils/
│ ├── helpers.js ← Currency/date formatting
│ ├── budgetCalculations.js
│ ├── recurringCalculations.js
│ ├── csvParser.js
│ └── syncQueue.js
- Node.js v18+
- Supabase account (free tier works)
# 1. Install dependencies
npm install
# 2. Set up environment variables
cp .env.example .env
# Edit .env and fill in VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY
# 3. Apply database schema
# Run sql_scripts/supabase_schema_create.sql in the Supabase SQL Editor
# 4. Start the dev server
npm run devThe app will be available at http://localhost:5173.
See docs/SETUP_GUIDE.md for detailed Supabase setup instructions.
docs/
├── CONTEXT_GUIDE.md ← How to work with AI effectively on this project
├── DATA_MODEL.md ← Complete database schema (reference while coding)
├── SECURITY.md ← Security model and invariants
├── SECURITY_CHECKLIST.md ← Pre-merge security review checklist
├── SETUP_GUIDE.md ← Supabase setup walkthrough
├── STYLE_GUIDE.md ← Design system, component patterns, dark mode
├── QUICK_REFERENCE.md ← Navigation index for all docs
└── archive/ ← Completed phase build guides (historical)
This project includes a dev container configuration for a portable, reproducible development environment using Node 25.
- Copy
.env.exampleto.envand fill in your Supabase credentials:VITE_SUPABASE_URL=your_supabase_project_url VITE_SUPABASE_ANON_KEY=your_supabase_anon_key - Open the
budget-app/folder in VS Code. - When prompted, click Reopen in Container (or run
Dev Containers: Reopen in Containerfrom the Command Palette). - Dependencies install automatically via
npm install. Once done, start the dev server:The app will be available atcd budget-app && npm run dev
http://localhost:5173.
The container automatically installs ESLint, Prettier, Tailwind CSS IntelliSense, GitLens, and React snippet extensions.
The app ships as a lightweight Docker image (~30 MB) — a multi-stage build compiles the React SPA and serves it with Nginx.
- Docker (built into Unraid, or Docker Desktop on any machine)
- A Supabase project with the database schema applied (see
sql_scripts/) - Your Supabase Project URL and anon/public key
# 1. Clone the repo
git clone https://github.com/SamGunvalson/budget-app.git
cd budget-app
# 2. Create your .env file with Supabase credentials
cp .env.example .env
# Edit .env and fill in VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY
# 3. Build and start the container
docker compose up -d --build
# 4. Verify it's running
docker ps | grep budget-appThe app will be available at http://<your-server-ip>:8085.
If your Unraid server is on your Tailscale network, access the app from any device at:
http://<unraid-tailscale-ip>:8085
Tailscale encrypts traffic with WireGuard, so TLS/HTTPS is not required.
If you prefer the Unraid Docker UI instead of Compose:
- Build the image on your Unraid server:
cd /path/to/Thewarguy/budget-app docker build \ --build-arg VITE_SUPABASE_URL=https://your-project.supabase.co \ --build-arg VITE_SUPABASE_ANON_KEY=your-anon-key \ -t budget-app .
- In the Unraid Docker UI, click Add Container:
- Repository:
budget-app(local image) - Port mapping: Host
8085→ Container80 - Restart policy: Unless stopped
- Repository:
- Start the container.
After pulling new code changes:
cd /path/to/Thewarguy/budget-app
git pull
docker compose up -d --buildThe old container is automatically replaced.
Edit docker-compose.yml and change the host port:
ports:
- "8085:80" # change 8085 to any free portThen rebuild: docker compose up -d --build