A full-stack savings tracker with goal management, deposit and withdrawal tracking, dashboard analytics, and authentication.
Live Demo: savings-tracker-rrn.up.railway.app
Savings Tracker is a full-stack application built as a Frontend Mentor challenge, extended into a complete solution with authentication, a PostgreSQL database, and a REST API. Users can create savings goals with optional deadlines, track deposits and withdrawals against each goal, and view their overall progress through a dashboard with summary stats and a monthly savings chart.
The project is structured as an npm workspaces monorepo with a React client, an Express server, and a shared package for types and validation schemas used across both.
- Register, log in, and log out with JWT-based sessions stored in HTTP-only cookies
- Forgot password flow with email-based reset tokens
- Strong password validation with real-time feedback
- Create, edit, and delete savings goals with a name, target amount, and optional deadline
- Form validation with clear error messages for missing or invalid fields
- Distinct layout for completed goals
- Add deposits and withdrawals to a goal with an amount and optional note
- View full transaction history per goal
- Withdrawal amounts validated against the current goal balance
- Summary stats for total savings, active goals, and completed goals
- Monthly savings chart showing deposit and withdrawal activity over the last 12 months
- Goal cards showing progress, amounts, and deadlines
- Empty and completed states for goals
- Filter goals by status (all, in progress, completed, not started)
- Sort by deadline, progress, amount saved, or alphabetically
- Responsive layout across mobile, tablet, and desktop
- Custom scroll areas and dropdowns built with Radix UI
- Hover and focus states for all interactive elements
- Keyboard navigable
| Category | Technology |
|---|---|
| Monorepo | npm workspaces |
| Client Framework | React + Vite |
| Language | TypeScript |
| Styling | Tailwind CSS |
| Routing | React Router |
| Data Fetching | TanStack Query, Axios |
| Forms | React Hook Form + Zod |
| Charts | MUI X Charts |
| UI Components | Radix UI |
| Server Framework | Express |
| Database | PostgreSQL |
| Authentication | Passport, JWT |
| Validation | Zod (shared between client and server) |
savings-tracker/
├── apps/
│ ├── client/ # React + Vite frontend
│ │ └── src/
│ │ ├── app/ # Router, query client setup
│ │ ├── components/ # Shared UI components
│ │ ├── features/ # Feature modules (auth, dashboard)
│ │ ├── layouts/ # Route layouts
│ │ └── pages/ # Route pages
│ └── server/ # Express backend
│ └── src/
│ ├── database/ # Schema, rollback, seeds
│ ├── includes/ # Config, db connection, passport
│ ├── middleware/ # Error handling, auth, rate limiting
│ └── modules/
│ ├── auth/ # Auth controllers, services, routes
│ └── dashboard/ # Goals and transactions
├── packages/
│ └── shared/ # Shared types and Zod schemas
└── .github/workflows/ # CI pipeline
- Node.js
v20+ - PostgreSQL
v14+
git clone https://github.com/nofuenterr/savings-tracker.git
cd savings-tracker
npm installCopy .env.example to .env in apps/server and fill in the required values:
cp apps/server/.env.example apps/server/.envNODE_ENV=development
PORT=3000
DATABASE_URL=
CLIENT_URL=http://localhost:5173
DB_HOST=localhost
DB_USER=postgres
DB_PASSWORD=
DB_NAME=savings_tracker
DB_PORT=5432
JWT_SECRET=
CSRF_SECRET=
COOKIE_SECRET=Create the database, then run migrations and seed data:
npm run migrate -w @savings-tracker/server
npm run seed -w @savings-tracker/serverTo rollback (drop all tables):
npm run rollback -w @savings-tracker/server# Run client and server together
npm run devThe client runs at http://localhost:5173 and the API at http://localhost:3000.
npm run build| Desktop | Tablet | Mobile |
|---|---|---|
- Add pagination or infinite scroll to the goals list
- Lazy load goals list items
- Add more specific error messages on the error page
- Improve empty/no-data state design for the monthly savings chart
- Add unit and integration tests, and run them in CI
- Add animations and transitions
- Add drag-to-reorder for goals list
- Add profile feature (edit username, etc.)
- Add database indexes on frequently queried columns
- Resolve cross-origin cookie issue on iOS Safari (currently blocked despite
sameSite: 'none'; considering alternatives to a custom domain or moving JWT storage off cookies)
- Create, edit, and delete savings goals with name, target amount, and optional deadline
- Form validation messages for missing or invalid fields
- Add deposits and withdrawals with amount and optional note
- View full deposit and withdrawal history for each goal
- Dashboard summary stats for total savings, active goals, and completed goals
- Monthly savings chart showing activity over time
- Goal card grid showing progress, amounts, and deadlines
- Empty and completed states for goals
- Filter goals by status and sort by deadline, progress, amount saved, or alphabetically
- Goal detail page with progress percentage, remaining amount, and progress bar
- Distinct completed layout when a goal reaches 100%
- Responsive layout across screen sizes
- Hover and focus states for interactive elements
- Full keyboard navigation
- Sign up, log in, and reset password via email
- Rate limiting and security headers
- Shared validation schemas between client and server
- Password reset emails in production: reset emails are sent via Gmail SMTP, which works in local development but is blocked by Railway's outbound network policy (SMTP ports are restricted to prevent abuse). In production, the reset link is logged to the server's output instead of being emailed. A production-ready fix would require an HTTP-based email provider (e.g., Resend, SendGrid) with a verified custom domain.
This project is a solution to a Frontend Mentor challenge. I do not own the rights to any assets used.
*Developed by RR Nofuente*