A complete personal finance planning + management web application for Bangladesh users, built as a monolithic Next.js app (App Router) with TypeScript, Tailwind CSS, and Turso (libSQL/SQLite).
Live URL: https://personal-budget-manager-beta.vercel.app/
- Export
TURSO_DATABASE_URL,TURSO_AUTH_TOKEN,AUTH_SECRET, andNEXT_PUBLIC_APP_URL(or use.env.local) before any commands. - Wipe old data:
npm run db:cleanclears the connected Turso DB; run withTURSO_DATABASE_URL= TURSO_AUTH_TOKEN=to clean the local.data/local.db. - Apply schema:
npm run db:migrate. - (Optional) Seed lookup data:
npm run db:seed. - Build for deploy:
npm run build; start withnpm run start.
- Enable users to log daily income & expenses quickly.
- Provide tracking (daily/weekly/monthly) with charts and summaries.
- Provide bank + investment management (manual, no integrations).
- Provide savings goals + loan tracking.
- Provide Sharia profit sharing + FIRE calculator.
- Provide a modern analytics dashboard.
- Secure login system with per-user data isolation.
- Fully responsive UI for desktop dashboard + mobile view.
- No bank APIs, SMS, email notifications, or third-party financial integrations.
- No multi-language support (English only).
- No shared accounts or multi-user collaboration per account.
- Frontend + Backend: Next.js (App Router), React, TypeScript
- Styling: Tailwind CSS
- Database: Turso (libSQL / SQLite)
- Auth: Session-based auth (recommended: Lucia / NextAuth credentials / custom)
- Charts: Recharts (recommended) or Chart.js
- Forms/Validation: React Hook Form + Zod (recommended)
Single repo, single Next.js application contains:
- UI pages (routes)
- API endpoints (
app/api/*) - Database access layer (
src/server/db/*) - Business logic/services (
src/server/services/*) - Shared types and schemas (
src/shared/*)
- Strict per-user isolation: every query filtered by
userId. - Server-first where possible (data fetching on server components).
- Type-safe boundaries: Zod schemas for API inputs, shared TS types.
- Auditability: track changes in transactions (optional, recommended).
- Register: email + password
- Login/Logout
- Password reset (optional but recommended)
- Session management via secure cookies
- Route protection middleware for authenticated areas
- User cannot access any private pages without login.
- User cannot query other users’ data (API must enforce).
- Password stored hashed (bcrypt/argon2).
- Date, type (INCOME/EXPENSE), amount, category, optional account/investment link, description, tags (optional)
-
“Add Transaction” must be < 15 seconds on mobile.
-
Default date is “today”
-
Category selector with search
-
Quick amount input (numeric keyboard on mobile)
-
Transaction list with filters:
- Date range
- Type
- Category
- Account
- Search (description)
- Daily view: grouped by day
- Weekly view: grouped by week (Mon–Sun or Sun–Sat; choose one and standardize)
- Monthly view: calendar-like summary + list
- Creating/editing/deleting transaction updates analytics immediately.
- Amount validation: positive numbers; type defines direction.
- Currency display:
BDTwith৳formatting.
- Create bank accounts (name, type, opening balance, current balance)
- Optional linking of transactions to accounts
- Account details view with transaction list filtered to that account
- Manual balance adjustments (optional but recommended)
-
Account balances can be derived either:
- stored current balance + adjustment entries, OR
- computed from opening balance + linked transactions
-
Pick one approach and keep consistent across product.
Recommended approach: store openingBalance and compute current balance from transactions (less chance of mismatch) + allow “Adjustment” transactions for reconciliation.
- Investment items (name, type, current value, notes)
- Investment value history updates (manual)
- Investment dashboard summary and performance chart (based on history)
- User can update value; history is stored and shown over time.
- Create goal: title, target amount, target date (optional)
- Add contributions (manual) OR link to specific “Savings” category transactions
- Progress: amount saved, % complete, projected completion (optional)
- Goal progress visible on dashboard and goals page.
- Completed goals are archived.
- Create loans: lender name, principal, interest rate, start date, term (optional)
- Record payments: date, amount
- Outstanding balance tracking
- Payoff progress bar and summary
-
Payments reduce balance.
-
Interest calculation: Version 1: optional/simple (document clearly)
- Option A: user manually updates interest via adjustment
- Option B: app calculates monthly simple interest
-
Choose one and document in UI.
- Initial capital
- Monthly contribution
- Expected annual profit rate
- Investor profit share (%)
- Profit handling (reinvest or withdraw)
- Duration (years/months)
- Projected value
- Total contributions
- Total profit earned (investor share)
- Growth chart + year-by-year table
- Uses a profit-sharing model (no interest).
- Clear disclaimer: “Profit is not guaranteed.”
- Current age (optional)
- Current net worth (prefilled from app)
- Monthly contribution
- Expected annual return
- Annual expense (or monthly expense * 12)
- Withdrawal rate (default 4%)
- Inflation (optional)
- FIRE number (expense / withdrawal rate)
- Years to FIRE + estimated year/age
- Projection chart
- Calculator can run entirely client-side
- Clear disclaimer: “This is estimation, not financial advice.”
- Income vs Expense (monthly bars for last 6–12 months)
- Expense breakdown by category (donut)
- Net worth trend (line)
- Savings goal progress (cards/progress bars)
- Top categories and top merchants/notes (optional)
- This month: total income, total expense, savings (income - expense)
- Average daily spending (last 30 days)
- Largest expense transaction (current month)
- Dashboard loads fast (<2s typical on good network).
- Mobile: charts stack and remain readable.
/Landing (optional)/auth/login/auth/register/auth/forgot-password(optional)
/appDashboard/app/expenses/app/expenses/new/app/accounts/app/accounts/[id]/app/investments/app/investments/[id]/app/goals/app/goals/[id]/app/loans/app/loans/[id]/app/tools/sharia-profit/app/tools/fire/app/settings(profile, data export, theme, etc.)
All endpoints are authenticated, return JSON, and are scoped by
userId.
GET /api/expenses?from=YYYY-MM-DD&to=YYYY-MM-DD&type=&categoryId=&accountId=&q=POST /api/expensesPATCH /api/expenses/:idDELETE /api/expenses/:id
POST body
{
"date": "2025-12-17",
"type": "EXPENSE",
"amount": 1200,
"categoryId": "cat_food",
"accountId": "acc_1",
"note": "Lunch"
}GET /api/accountsPOST /api/accountsPATCH /api/accounts/:idDELETE /api/accounts/:id
GET /api/investmentsPOST /api/investmentsPATCH /api/investments/:idPOST /api/investments/:id/value(creates history entry)
GET /api/goalsPOST /api/goalsPOST /api/goals/:id/contributionsPATCH /api/goals/:idDELETE /api/goals/:id
GET /api/loansPOST /api/loansPOST /api/loans/:id/paymentsPATCH /api/loans/:idDELETE /api/loans/:id
GET /api/analytics/summary?period=this_month|last_month|ytd|custom&from=&to=GET /api/analytics/trends?range=12mGET /api/analytics/categories?from=&to=
userscategoriesaccountstransactionsinvestmentsinvestment_valuesgoalsgoal_contributionsloansloan_payments
- All user data tables include
user_id. - Use
TEXTUUIDs for IDs (stable across SQLite). - Store money as integer minor units (recommended): e.g., store amount in paisa as
amount_paisato avoid floating issues.
- Clean layout, 8px spacing system
- Light mode first
- Typography: simple (Inter or system)
- Consistent input sizes & button hierarchy
-
App Shell: topbar + responsive sidebar (desktop), drawer/bottom nav (mobile)
-
Reusable:
Card,StatCard,DataTable,ChartCardDateRangePicker,CategorySelect,AmountInputConfirmDialog,Toast,EmptyState
- Every list has an empty state + “Add” CTA.
- Every destructive action requires confirmation.
- Loading states: skeletons
- Errors: clear message + retry
- Password hashing
- Secure session cookies (HttpOnly, SameSite)
- CSRF protection if needed (depending auth strategy)
- Input validation on server using Zod
- Rate limit login endpoints (basic)
- Dashboard: optimized queries (pre-aggregations in SQL)
- Use indexes on
(user_id, date)intransactions - Paginate transaction list
- Avoid heavy client rendering for large lists (virtualization optional)
- Next.js app deployed on Vercel (recommended)
- Turso production DB with credentials as env vars
- Separate staging environment (optional but recommended)
Proposed structure for Next.js App Router monolith.
budget-manager/
├─ README.md
├─ package.json
├─ tsconfig.json
├─ next.config.js
├─ tailwind.config.ts
├─ postcss.config.js
├─ .env.example
├─ .gitignore
│
├─ src/
│ ├─ app/
│ │ ├─ (public)/
│ │ │ ├─ page.tsx # Landing (optional)
│ │ │ └─ auth/
│ │ │ ├─ login/page.tsx
│ │ │ ├─ register/page.tsx
│ │ │ └─ forgot-password/page.tsx # optional
│ │ │
│ │ ├─ (protected)/
│ │ │ ├─ layout.tsx # App shell (sidebar/topbar)
│ │ │ ├─ app/page.tsx # Dashboard
│ │ │ ├─ app/expenses/page.tsx
│ │ │ ├─ app/expenses/new/page.tsx
│ │ │ ├─ app/accounts/page.tsx
│ │ │ ├─ app/accounts/[id]/page.tsx
│ │ │ ├─ app/investments/page.tsx
│ │ │ ├─ app/investments/[id]/page.tsx
│ │ │ ├─ app/goals/page.tsx
│ │ │ ├─ app/goals/[id]/page.tsx
│ │ │ ├─ app/loans/page.tsx
│ │ │ ├─ app/loans/[id]/page.tsx
│ │ │ ├─ app/tools/sharia-profit/page.tsx
│ │ │ ├─ app/tools/fire/page.tsx
│ │ │ └─ app/settings/page.tsx
│ │ │
│ │ ├─ api/
│ │ │ ├─ auth/
│ │ │ │ ├─ login/route.ts
│ │ │ │ ├─ register/route.ts
│ │ │ │ └─ logout/route.ts
│ │ │ ├─ expenses/route.ts
│ │ │ ├─ expenses/[id]/route.ts
│ │ │ ├─ accounts/route.ts
│ │ │ ├─ accounts/[id]/route.ts
│ │ │ ├─ investments/route.ts
│ │ │ ├─ investments/[id]/route.ts
│ │ │ ├─ investments/[id]/value/route.ts
│ │ │ ├─ goals/route.ts
│ │ │ ├─ goals/[id]/route.ts
│ │ │ ├─ goals/[id]/contributions/route.ts
│ │ │ ├─ loans/route.ts
│ │ │ ├─ loans/[id]/route.ts
│ │ │ ├─ loans/[id]/payments/route.ts
│ │ │ └─ analytics/
│ │ │ ├─ summary/route.ts
│ │ │ ├─ trends/route.ts
│ │ │ └─ categories/route.ts
│ │ │
│ │ ├─ layout.tsx # Root layout
│ │ ├─ globals.css
│ │ └─ middleware.ts # Auth guard (if used in app router)
│ │
│ ├─ components/
│ │ ├─ ui/ # Generic UI components
│ │ │ ├─ button.tsx
│ │ │ ├─ input.tsx
│ │ │ ├─ modal.tsx
│ │ │ ├─ card.tsx
│ │ │ └─ ...
│ │ ├─ layout/
│ │ │ ├─ AppShell.tsx
│ │ │ ├─ Sidebar.tsx
│ │ │ ├─ Topbar.tsx
│ │ │ └─ MobileNav.tsx
│ │ ├─ charts/
│ │ │ ├─ IncomeExpenseBar.tsx
│ │ │ ├─ CategoryDonut.tsx
│ │ │ └─ NetWorthLine.tsx
│ │ └─ forms/
│ │ ├─ TransactionForm.tsx
│ │ ├─ AccountForm.tsx
│ │ ├─ GoalForm.tsx
│ │ └─ LoanForm.tsx
│ │
│ ├─ server/
│ │ ├─ db/
│ │ │ ├─ client.ts # Turso client
│ │ │ ├─ schema.sql # base schema (or migrations)
│ │ │ ├─ migrations/
│ │ │ │ ├─ 0001_init.sql
│ │ │ │ └─ ...
│ │ │ └─ repositories/
│ │ │ ├─ expenses.repo.ts
│ │ │ ├─ accounts.repo.ts
│ │ │ ├─ investments.repo.ts
│ │ │ ├─ goals.repo.ts
│ │ │ ├─ loans.repo.ts
│ │ │ └─ analytics.repo.ts
│ │ ├─ services/
│ │ │ ├─ auth.service.ts
│ │ │ ├─ analytics.service.ts
│ │ │ ├─ calculators/
│ │ │ │ ├─ sharia-profit.ts
│ │ │ │ └─ fire.ts
│ │ │ └─ money.ts # formatting + minor units helpers
│ │ └─ auth/
│ │ ├─ session.ts # cookie/session helpers
│ │ └─ guards.ts # requireUser()
│ │
│ ├─ shared/
│ │ ├─ types/
│ │ │ ├─ models.ts
│ │ │ └─ api.ts
│ │ ├─ validators/
│ │ │ ├─ transaction.schema.ts
│ │ │ ├─ account.schema.ts
│ │ │ ├─ goal.schema.ts
│ │ │ └─ loan.schema.ts
│ │ └─ constants/
│ │ ├─ categories.ts
│ │ └─ ui.ts
│ │
│ ├─ lib/
│ │ ├─ date.ts # week/month helpers
│ │ ├─ format.ts # currency/date formatting
│ │ └─ fetcher.ts # API fetch helper (SWR/ReactQuery)
│ │
│ └─ styles/
│ └─ theme.ts
│
└─ scripts/
├─ migrate.ts # run SQL migrations
└─ seed.ts # seed categories, demo user (optional)Create .env.local from .env.example.
.env.example
# Turso
TURSO_DATABASE_URL=
TURSO_AUTH_TOKEN=
# Cron (optional)
CRON_SECRET=
# Auth
AUTH_SECRET= # random string
APP_URL=http://localhost:3000pnpm installpnpm dev- Create a feature branch from
main. - Keep PRs focused; split UI vs API changes when it helps review.
- Update docs when adding env vars, scripts, or routes.
- Before pushing:
pnpm lint
pnpm typecheck
pnpm db:migrate- Use
pnpmfor scripts to keep lockfile behavior consistent. - Keep secrets in
.env.localand do not commit them. - Prefer feature flags or small increments over large refactors.
- Keep server logic in
src/server/*; keep UI insrc/app/*. - Use parameterized SQL and scope all queries by
user_id. - Store timestamps as ISO UTC strings (
YYYY-MM-DDTHH:mm:ss.sssZ). - Add indexes when new filters or sorts are introduced.
- Add a numbered migration in
src/server/db/migrations(next sequence). - Update
src/server/db/schema.sqlto match the latest schema snapshot. - Keep all queries scoped by
user_idfor data isolation.
- Return
401on unauthenticated access; validate all inputs. - Prefer
NextResponse.jsonwith a clear{ ok: boolean }shape. - Sanitize user input with shared helpers in
src/shared/security/*.
- New users get
data_expires_atset to 4 days in the future. - Keep
CRON_SECRETconfigured and schedule/api/cron/purge-expired-users.
- Describe the change and risk/impact in the PR.
- Include migration notes (if any).
- Add screenshots for UI changes.
- Use Turso dev database for dev (recommended)
- Store SQL migrations under
src/server/db/migrations
pnpm db:migrateRecommended scripts:
dev– start Next.js dev serverbuild– build productionstart– run production buildlint– linttypecheck– tsc typecheckdb:migrate– apply migrations to Tursodb:seed– seed categoriesdb:clean– wipe all data (preserve schema)db:purge-expired-users– delete users pastdata_expires_at
- ✅ Login/register works
- ✅ All CRUD endpoints validate inputs + enforce userId scoping
- ✅ Dashboard analytics correct for selected period
- ✅ Mobile UX: add transaction is smooth
- ✅ Transactions filter + pagination works
- ✅ Calculators return correct values for test cases
- ✅ No third-party integrations present
