Scope: This README replaces prior selected overview docs
Budget Calendar is a personal financial forecasting app for checking-balance planning. It combines Monarch-derived balances and recurring transactions, manual transactions, projection logic, and Google Calendar sync so upcoming bills, income, and projected balance changes stay in one workspace.
- App URL:
https://budget.theespeys.com - Frontend hosting: Netlify
- Backend: Firebase Auth, Firestore, and Cloud Functions
- Monitoring:
- Netlify build watch:
npm run deploy:watch - Firebase logs:
firebase functions:log --only <functionName> - GitHub Actions:
.github/workflows/ - Weekly backup status logging:
.github/workflows/backup.yml
- Netlify build watch:
- Frontend: React 18, Vite, TypeScript, Tailwind CSS
- Backend: Firebase Functions v1, Firestore, Firebase Auth
- Forecasting data: Monarch Money unofficial API
- Calendar sync: Google Calendar API via Firebase Functions
- Automation: GitHub Actions + Netlify deploy pipeline
git clone https://github.com/bradyespey/budget-calendar.git
cd BudgetCalendar
npm install
npm run devCopy .env.example to .env and fill in values. See .env.example for all required variables.
Variable reference:
VITE_FIREBASE_API_KEY=YOUR_FIREBASE_API_KEY
VITE_FIREBASE_AUTH_DOMAIN=YOUR_FIREBASE_AUTH_DOMAIN
VITE_FIREBASE_PROJECT_ID=YOUR_FIREBASE_PROJECT_ID
VITE_FIREBASE_STORAGE_BUCKET=YOUR_FIREBASE_STORAGE_BUCKET
VITE_FIREBASE_MESSAGING_SENDER_ID=YOUR_FIREBASE_MESSAGING_SENDER_ID
VITE_FIREBASE_APP_ID=YOUR_FIREBASE_APP_ID
VITE_ALLOWED_EMAILS=YOUR_ALLOWED_EMAILS
VITE_SITE_URL=YOUR_SITE_URL
VITE_DEBUG_MODE=true
VITE_FIREBASE_FUNCTIONS_BASE_URL=http://127.0.0.1:5001/budgetcalendar-e6538/us-central1Important notes:
- Do not expose paid provider keys through
VITE_* refreshAccounts,updateBalance, andrefreshTransactionsuse Firebase runtime config, not browser env varsrefreshAccountscalls Monarch directly, requests checking/savings refresh, and waits until those configured accounts are done syncing before returning success- Localhost calls deployed Firebase Functions by default so dev and production use the same backend
VITE_FIREBASE_FUNCTIONS_BASE_URLis optional and only needed for explicit emulator testing
Firebase runtime config still required for live Functions:
firebase experiments:enable legacyRuntimeConfigCommands
firebase functions:config:set \
monarch.token="YOUR_MONARCH_TOKEN" \
monarch.checking_id="YOUR_CHECKING_ACCOUNT_ID" \
monarch.savings_id="YOUR_SAVINGS_ACCOUNT_ID"Google Calendar functions also require runtime config for:
google.service_account_json- dev/prod bills calendar IDs
- dev/prod balance calendar IDs
GitHub Actions secrets required for weekly encrypted backups:
FIREBASE_SERVICE_ACCOUNTBACKUP_ENCRYPTION_KEYBACKUP_LOG_KEY
- Dev app:
npm run dev - Local functions emulator:
cd functions && npm run serve - Type check:
npm run type-check - Production build:
npm run build
Development environment loading:
npm run devruns plainvite— Vite loads.envautomatically from the project root
Local function testing:
- Create
functions/.runtimeconfig.jsonfrom Firebase runtime config before starting the emulator - Keep
functions/.runtimeconfig.jsonlocal only; it contains secrets and is gitignored - Set
VITE_FIREBASE_FUNCTIONS_BASE_URL=http://127.0.0.1:5001/budgetcalendar-e6538/us-central1only when intentionally testing the local emulator
- Dev:
npm run dev - Build:
npm run build - Type check:
npm run type-check - Automation check:
npm run check:automation - Trigger nightly workflow:
npm run trigger:nightly - Watch Netlify deploy after push:
npm run deploy:watch
Key Functions:
refreshAccounts: calls Monarch directly to refresh configured checking/savings accounts and waits for those syncs to completeupdateBalance: pulls checking, savings, and credit-card totals from Monarch and stores account snapshotsrefreshTransactions: refreshes recurring Monarch streams into Firestore; maps Monarch account types to Checking/Credit Card; Unknown account type + negative amount is reclassified as Credit Card (CC charges without a real account don't affect balance projection)budgetProjection: calculates projected checking balances with business-day adjustments; excludes Credit Card and unknown-account-type expenses from balance (those are covered by CC payment bills); writes monthly cash flow summary (category averages and bills/income by frequency) tomonthlyCashFlow/currentsyncCalendar: syncs bills and projected balances to Google Calendar as all-day events with event reminders disabledclearCalendars: clears future events from configured calendarsrunAll: orchestrates the nightly automation flow; it stops ifrefreshAccountsdoes not complete, so balance/transaction updates only run after checking/savings refresh finishes
Automation:
.github/workflows/backup.yml: runs the encrypted Firestore backup weekly or manually, commits changed backup data, and posts the run status to AdminPanel
- Frontend deploys from GitHub to Netlify
- Functions deploy separately through Firebase
npx firebase deploy --only functionsBuild monitoring:
npm run deploy:watchImportant:
deploy:watchmust be read to completion- treat any
Build failedoutput or error lines as a failed deploy even if the watcher prints a success-style summary
/dashboard: balances, alerts, forecast summary, savings trend; category averages (client-side, all bills, clickable → filtered transactions); bills/income summary by frequency (daily/weekly/biweekly/semimonthly/monthly/yearly/one-time)/transactions: Recurring Bills & Income — Monarch entries are read-only (categories and details sync from Monarch); manual transactions can be created and edited freely/upcoming: 7-day projection view, transaction search, balance-impact vs excluded display; CC charges shown as excluded (covered by CC payment bill)/settings: projection settings, quick actions, maintenance actions, calendar controls/login: restricted sign-in and demo access
BudgetCalendar/
├── src/
│ ├── components/
│ ├── hooks/
│ ├── pages/
│ ├── api/
│ ├── utils/
│ └── types/
├── functions/
│ └── src/functions/
├── scripts/
├── .github/workflows/
├── docs/
├── netlify.toml
└── package.json
- Monarch
401on balance/transaction refresh:- update Firebase runtime config
monarch.token - verify checking/savings account IDs
- update Firebase runtime config
- Calendar actions report auth failures:
- verify
google.service_account_json - verify calendar IDs and calendar sharing with the service account
- verify
- Projection looks off around dates:
- re-run
budgetProjection - confirm recurring transaction dates and checking-impact rules
- re-run
- Netlify deploy watcher finishes but site is broken:
- read full watcher output and look for
Build failed
- read full watcher output and look for
Read this README first, then scan src/, functions/src/functions/, and scripts/. Keep paid keys out of browser env vars, preserve the current Firebase runtime config pattern for Monarch and Google Calendar, and prefer minimal changes over new abstractions.