Skip to content

About

Financial-banking-analytics portfolio

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

26 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

BankDash - Personal Finance Management Platform

Next.js TypeScript React Query Tailwind CSS

A modern, full-stack personal finance management application with comprehensive financial analytics, budgeting tools, and real-time data synchronization.

Features β€’ Tech Stack β€’ Getting Started β€’ Architecture β€’ API Documentation


πŸ“‹ Table of Contents


Overview

BankDash is a comprehensive personal finance management platform that helps users track expenses, manage budgets, set financial goals, and gain insights into their spending habits. Built with Next.js 14 (App Router) and TypeScript, it features a beautiful, responsive UI and integrates with a robust NestJS backend.

🎯 Key Highlights

  • Full-Stack Integration: Seamlessly connected with NestJS backend API
  • Real-Time Sync: Automatic data synchronization with PostgreSQL (Supabase)
  • Modern Architecture: Server Components, Server Actions, and API Routes
  • Type-Safe: End-to-end TypeScript with shared DTOs
  • Production-Ready: Optimized for performance and scalability

✨ Features

πŸ’° Financial Management

  • Multi-Account Support: Manage checking, savings, credit cards, and investment accounts
  • Transaction Tracking: Create, edit, and categorize transactions with advanced filtering
  • Budget Management: Set monthly budgets per category with real-time progress tracking
  • Bill Tracking: Never miss a payment with bill reminders and payment history
  • Financial Goals: Set savings goals and track progress with automatic calculations
  • Analytics Dashboard: Visualize spending patterns with interactive charts

πŸ“Š Analytics & Insights

  • Spending Analysis: Breakdown by category, merchant, and time period
  • Trends: Identify spending patterns and seasonal variations
  • Cash Flow: Monitor income vs expenses with predictive analytics
  • Category Insights: Deep dive into spending habits per category
  • Comparative Reports: Month-over-month and year-over-year comparisons
  • Financial Health Score: AI-powered insights (coming soon)

πŸ” Security & Auth

  • JWT Authentication: Secure token-based authentication
  • Automatic Token Refresh: Seamless session management
  • HTTP-Only Cookies: Enhanced security for sensitive data
  • Protected Routes: Server-side and client-side route protection
  • Password Reset: Secure password recovery flow

🎨 User Experience

  • Dark Mode: Full dark mode support with system preference detection
  • Responsive Design: Mobile-first approach, works on all devices
  • Animations: Smooth transitions with Framer Motion
  • Virtual Scrolling: Optimized performance for large transaction lists
  • Loading States: Skeleton loaders and suspense boundaries
  • Error Handling: Comprehensive error boundaries and toast notifications

πŸ›  Tech Stack

Frontend Core

Data Fetching & State

  • @tanstack/react-query 5.28 - Server state management
    • Automatic caching and background refetching
    • Optimistic updates
    • Pagination and infinite scroll support
  • Zustand 5.0 - Lightweight client state
  • Axios 1.6 - HTTP client with interceptors

UI & Visualization

Forms & Validation

Date & Utilities

Development & Testing


πŸš€ Getting Started

Prerequisites

  • Node.js: Version 18.0 or higher
  • npm: Version 9.0 or higher
  • Backend: NestJS API running on port 3000 (Backend Repo)

Installation

  1. Clone the repository
git clone <repository-url>
cd Financial-banking-analytics
  1. Install dependencies
npm install
  1. Environment Setup

Create a .env.local file in the root directory:

# API Configuration - NestJS Backend
NEXT_PUBLIC_API_URL=http://localhost:3000/api/v1
API_URL=http://localhost:3000/api/v1
NEXT_PUBLIC_API_TIMEOUT=30000

# App Configuration
NEXT_PUBLIC_APP_URL=http://localhost:3001
NEXT_PUBLIC_APP_NAME=BankDash

# Feature Flags
NEXT_PUBLIC_ENABLE_AI_INSIGHTS=false
NEXT_PUBLIC_ENABLE_BANK_SYNC=false
NEXT_PUBLIC_ENABLE_MULTI_CURRENCY=false
NEXT_PUBLIC_ENABLE_REAL_TIME=false

# Development
NODE_ENV=development
  1. Start the development server
npm run dev

The application will be available at http://localhost:3001

Test Credentials

Email: demo@example.com
Password: Demo@12345

Available Scripts

# Development
npm run dev              # Start development server on port 3001
npm run build            # Build for production
npm start                # Start production server
npm run lint             # Run ESLint
npm run type-check       # Check TypeScript types

# Testing
npm test                 # Run tests
npm run test:ui          # Run tests with UI
npm run test:coverage    # Generate coverage report

πŸ“ Project Structure

Financial-banking-analytics/
β”œβ”€β”€ app/                          # Next.js App Router
β”‚   β”œβ”€β”€ (auth)/                   # Authentication routes (route group)
β”‚   β”‚   β”œβ”€β”€ login/
β”‚   β”‚   β”‚   └── page.tsx          # Login page
β”‚   β”‚   β”œβ”€β”€ register/
β”‚   β”‚   β”‚   └── page.tsx          # Registration page
β”‚   β”‚   β”œβ”€β”€ forgot-password/
β”‚   β”‚   β”‚   └── page.tsx          # Password recovery
β”‚   β”‚   └── layout.tsx            # Auth layout
β”‚   β”‚
β”‚   β”œβ”€β”€ (dashboard)/              # Protected dashboard routes
β”‚   β”‚   β”œβ”€β”€ dashboard/            # Main dashboard
β”‚   β”‚   β”œβ”€β”€ transactions/         # Transaction management
β”‚   β”‚   β”œβ”€β”€ accounts/             # Account management
β”‚   β”‚   β”œβ”€β”€ budgets/              # Budget planning
β”‚   β”‚   β”œβ”€β”€ bills/                # Bill tracking
β”‚   β”‚   β”œβ”€β”€ goals/                # Financial goals
β”‚   β”‚   β”œβ”€β”€ analytics/            # Analytics & insights
β”‚   β”‚   β”œβ”€β”€ reports/              # Report generation
β”‚   β”‚   β”œβ”€β”€ planning/             # Financial planning tools
β”‚   β”‚   └── layout.tsx            # Dashboard layout (Sidebar, Header)
β”‚   β”‚
β”‚   β”œβ”€β”€ actions/                  # Server Actions
β”‚   β”‚   └── auth.ts               # Authentication actions
β”‚   β”‚
β”‚   β”œβ”€β”€ api/                      # API Routes
β”‚   β”‚   └── auth/
β”‚   β”‚       β”œβ”€β”€ login/route.ts
β”‚   β”‚       β”œβ”€β”€ register/route.ts
β”‚   β”‚       └── logout/route.ts
β”‚   β”‚
β”‚   β”œβ”€β”€ globals.css               # Global styles
β”‚   β”œβ”€β”€ layout.tsx                # Root layout
β”‚   └── page.tsx                  # Home page (redirects to dashboard)
β”‚
β”œβ”€β”€ components/                   # React components
β”‚   β”œβ”€β”€ ui/                       # Reusable UI components
β”‚   β”‚   β”œβ”€β”€ Button.tsx
β”‚   β”‚   β”œβ”€β”€ Card.tsx
β”‚   β”‚   β”œβ”€β”€ Input.tsx
β”‚   β”‚   └── Skeleton.tsx
β”‚   β”‚
β”‚   β”œβ”€β”€ forms/                    # Form components
β”‚   β”‚   β”œβ”€β”€ AccountForm.tsx
β”‚   β”‚   β”œβ”€β”€ TransactionForm.tsx
β”‚   β”‚   β”œβ”€β”€ BudgetForm.tsx
β”‚   β”‚   └── GoalForm.tsx
β”‚   β”‚
β”‚   β”œβ”€β”€ animations/               # Animation wrappers
β”‚   β”‚   β”œβ”€β”€ FadeIn.tsx
β”‚   β”‚   β”œβ”€β”€ SlideIn.tsx
β”‚   β”‚   β”œβ”€β”€ ScaleIn.tsx
β”‚   β”‚   └── StaggerChildren.tsx
β”‚   β”‚
β”‚   β”œβ”€β”€ Header.tsx                # Top navigation
β”‚   β”œβ”€β”€ Sidebar.tsx               # Side navigation
β”‚   β”œβ”€β”€ StatCard.tsx              # Statistics card
β”‚   β”œβ”€β”€ TransactionCard.tsx       # Transaction list item
β”‚   β”œβ”€β”€ ChartContainer.tsx        # Chart wrapper
β”‚   β”œβ”€β”€ VirtualTransactionList.tsx # Optimized transaction list
β”‚   β”œβ”€β”€ EmptyState.tsx            # Empty state placeholder
β”‚   β”œβ”€β”€ ErrorBoundary.tsx         # Error boundary
β”‚   β”œβ”€β”€ ThemeProvider.tsx         # Dark mode provider
β”‚   β”œβ”€β”€ QueryProvider.tsx         # React Query provider
β”‚   └── Toaster.tsx               # Toast notifications
β”‚
β”œβ”€β”€ services/                     # API service layer
β”‚   └── api/
β”‚       β”œβ”€β”€ client.ts             # Axios instance with interceptors
β”‚       β”œβ”€β”€ auth.service.ts       # Authentication API
β”‚       β”œβ”€β”€ accounts.service.ts   # Accounts CRUD
β”‚       β”œβ”€β”€ transactions.service.ts # Transactions CRUD
β”‚       β”œβ”€β”€ budgets.service.ts    # Budgets CRUD
β”‚       β”œβ”€β”€ bills.service.ts      # Bills CRUD
β”‚       β”œβ”€β”€ goals.service.ts      # Goals CRUD
β”‚       β”œβ”€β”€ categories.service.ts # Categories CRUD
β”‚       └── analytics.service.ts  # Analytics & insights
β”‚
β”œβ”€β”€ hooks/                        # Custom React hooks
β”‚   β”œβ”€β”€ useAuth.ts                # Authentication hooks
β”‚   β”œβ”€β”€ useAccounts.ts            # Account queries/mutations
β”‚   β”œβ”€β”€ useTransactions.ts        # Transaction queries/mutations
β”‚   β”œβ”€β”€ useBudgets.ts             # Budget queries/mutations
β”‚   β”œβ”€β”€ useBills.ts               # Bill queries/mutations
β”‚   β”œβ”€β”€ useGoals.ts               # Goal queries/mutations
β”‚   β”œβ”€β”€ useCategories.ts          # Category queries/mutations
β”‚   β”œβ”€β”€ useAnalytics.ts           # Analytics queries
β”‚   └── useDebounce.ts            # Debounce utility hook
β”‚
β”œβ”€β”€ types/                        # TypeScript type definitions
β”‚   β”œβ”€β”€ index.ts                  # Domain models
β”‚   └── dto.ts                    # Data Transfer Objects (shared with backend)
β”‚
β”œβ”€β”€ lib/                          # Utilities and configurations
β”‚   β”œβ”€β”€ queryClient.ts            # React Query configuration
β”‚   β”œβ”€β”€ store.ts                  # Zustand store
β”‚   β”œβ”€β”€ featureFlags.ts           # Feature flags
β”‚   β”œβ”€β”€ validations/              # Zod schemas
β”‚   β”‚   β”œβ”€β”€ auth.ts
β”‚   β”‚   β”œβ”€β”€ account.ts
β”‚   β”‚   └── transaction.ts
β”‚   └── utils.ts                  # Utility functions
β”‚
β”œβ”€β”€ utils/                        # Helper utilities
β”‚   β”œβ”€β”€ currency.ts               # Currency formatting
β”‚   └── date.ts                   # Date formatting
β”‚
β”œβ”€β”€ middleware.ts                 # Next.js middleware (auth check)
β”œβ”€β”€ tailwind.config.js            # Tailwind configuration
β”œβ”€β”€ next.config.js                # Next.js configuration
β”œβ”€β”€ tsconfig.json                 # TypeScript configuration
β”œβ”€β”€ vitest.config.ts              # Vitest configuration
β”œβ”€β”€ .env.local                    # Environment variables
└── package.json                  # Dependencies and scripts

πŸ— Architecture

Application Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         Browser                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚              Next.js Frontend (Port 3001)              β”‚ β”‚
β”‚  β”‚                                                        β”‚ β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚
β”‚  β”‚  β”‚   UI Layer   β”‚  β”‚ Server       β”‚  β”‚   Server    β”‚ β”‚ β”‚
β”‚  β”‚  β”‚ (Components) β”‚  β”‚ Components   β”‚  β”‚   Actions   β”‚ β”‚ β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚  β”‚         β”‚                 β”‚                  β”‚        β”‚ β”‚
β”‚  β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚ β”‚
β”‚  β”‚                           β”‚                           β”‚ β”‚
β”‚  β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚ β”‚
β”‚  β”‚         β”‚                                     β”‚       β”‚ β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”  β”‚ β”‚
β”‚  β”‚  β”‚  React Query  β”‚              β”‚  Service Layer β”‚  β”‚ β”‚
β”‚  β”‚  β”‚  (TanStack)   │◄──────────────   (Axios)      β”‚  β”‚ β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚ β”‚
β”‚  β”‚                                           β”‚          β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                                              β”‚            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                               β”‚
                                    HTTP/REST  β”‚
                                               β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    NestJS Backend (Port 3000)             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚                   API Layer                        β”‚  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚  β”‚
β”‚  β”‚  β”‚Controllers β”‚  β”‚  Services  β”‚  β”‚ Repositoriesβ”‚ β”‚  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ β”‚  β”‚
β”‚  β”‚        β”‚                β”‚                β”‚        β”‚  β”‚
β”‚  β”‚        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚  β”‚
β”‚  β”‚                         β”‚                         β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                            β”‚                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚               TypeORM / PostgreSQL                β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                        β”‚
                                        β”‚ SQL/TCP
                                        β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   Supabase PostgreSQL                     β”‚
β”‚                    (Cloud Database)                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Data Flow

  1. User Interaction β†’ UI Component
  2. Component β†’ React Query Hook
  3. Hook β†’ Service Layer (Axios)
  4. Service β†’ Backend API (NestJS)
  5. Backend β†’ TypeORM β†’ PostgreSQL
  6. Response β†’ Service β†’ Hook β†’ Component β†’ UI

Authentication Flow

1. User submits login form
2. Frontend calls auth.service.login()
3. Service sends POST /api/v1/auth/login
4. Backend validates credentials
5. Backend returns { user, accessToken, refreshToken }
6. Frontend stores tokens in:
   - localStorage (client-side access)
   - HTTP-only cookies (server-side access)
7. Subsequent requests include Authorization: Bearer <token>
8. On 401 error, automatic token refresh is triggered
9. If refresh fails, redirect to /login

πŸ”Œ API Integration

API Client Configuration

The application uses a singleton Axios instance with interceptors for authentication and error handling:

// services/api/client.ts
const apiClient = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  timeout: 30000,
  headers: {
    'Content-Type': 'application/json',
  },
})

// Request interceptor - Add auth token
apiClient.interceptors.request.use((config) => {
  const token = localStorage.getItem('accessToken')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

// Response interceptor - Handle token refresh
apiClient.interceptors.response.use(
  (response) => response,
  async (error) => {
    if (error.response?.status === 401) {
      // Attempt token refresh
      const refreshed = await refreshToken()
      if (refreshed) {
        // Retry original request
        return apiClient(error.config)
      }
      // Redirect to login
      window.location.href = '/login'
    }
    return Promise.reject(error)
  }
)

Service Layer Pattern

All API calls go through dedicated service modules:

// services/api/transactions.service.ts
class TransactionsService {
  async getAll(filters: TransactionFiltersDTO): Promise<TransactionDTO[]> {
    const response = await apiClient.get('/transactions', { params: filters })
    return response.data
  }

  async create(data: CreateTransactionDTO): Promise<TransactionDTO> {
    const response = await apiClient.post('/transactions', data)
    return response.data
  }

  // ... other methods
}

export const transactionsService = TransactionsService.getInstance()

Backend Endpoints

Method Endpoint Description
Authentication
POST /auth/login User login
POST /auth/register User registration
POST /auth/logout User logout
POST /auth/refresh Refresh access token
POST /auth/forgot-password Request password reset
POST /auth/reset-password Reset password
GET /auth/me Get current user
Accounts
GET /accounts List all accounts
GET /accounts/:id Get account by ID
POST /accounts Create new account
PATCH /accounts/:id Update account
DELETE /accounts/:id Delete account
POST /accounts/transfer Transfer between accounts
GET /accounts/:id/balance-history Get balance history
Transactions
GET /transactions List transactions (with filters)
GET /transactions/:id Get transaction by ID
POST /transactions Create transaction
PATCH /transactions/:id Update transaction
DELETE /transactions/:id Delete transaction
POST /transactions/import Import transactions (CSV/OFX)
POST /transactions/export Export transactions
GET /transactions/stats Get transaction statistics
Budgets
GET /budgets List all budgets
GET /budgets/:id Get budget by ID
POST /budgets Create budget
PATCH /budgets/:id Update budget
DELETE /budgets/:id Delete budget
GET /budgets/:id/progress Get budget progress
Bills
GET /bills List all bills
GET /bills/:id Get bill by ID
POST /bills Create bill
PATCH /bills/:id Update bill
DELETE /bills/:id Delete bill
POST /bills/:id/pay Mark bill as paid
GET /bills/upcoming Get upcoming bills
Goals
GET /goals List all goals
GET /goals/:id Get goal by ID
POST /goals Create goal
PATCH /goals/:id Update goal
DELETE /goals/:id Delete goal
POST /goals/:id/contribute Add contribution to goal
Categories
GET /categories List all categories
GET /categories/:id Get category by ID
POST /categories Create category
PATCH /categories/:id Update category
DELETE /categories/:id Delete category
GET /categories/spending Get spending by category
Analytics
GET /analytics/overview Get financial overview
GET /analytics/cash-flow Get cash flow analysis
GET /analytics/trends Get spending trends
GET /analytics/spending-by-category Category breakdown

πŸ” Authentication

JWT Token Management

The application uses JWT tokens for authentication:

  • Access Token: Short-lived (15 minutes), used for API requests
  • Refresh Token: Long-lived (7 days), used to obtain new access tokens

Token Storage

  • localStorage: For client-side JavaScript access
  • HTTP-only Cookies: For server-side rendering and enhanced security

Protected Routes

Routes are protected using Next.js middleware:

// middleware.ts
export function middleware(request: NextRequest) {
  const token = request.cookies.get('accessToken')

  if (!token && !isPublicRoute(request.nextUrl.pathname)) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
}

Password Requirements

  • Minimum 8 characters
  • At least one uppercase letter
  • At least one lowercase letter
  • At least one number
  • At least one special character

🎯 State Management

Server State - React Query

React Query handles all server state with automatic caching, background refetching, and optimistic updates.

Configuration

// lib/queryClient.ts
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000, // 5 minutes
      gcTime: 10 * 60 * 1000, // 10 minutes
      refetchOnWindowFocus: false,
      retry: 3,
    },
  },
})

Query Keys Structure

// Hierarchical query keys for efficient cache management
const queryKeys = {
  auth: ['auth'] as const,
  user: () => [...queryKeys.auth, 'user'] as const,

  accounts: ['accounts'] as const,
  accountList: () => [...queryKeys.accounts, 'list'] as const,
  accountDetail: (id: string) => [...queryKeys.accounts, 'detail', id] as const,

  transactions: ['transactions'] as const,
  transactionList: (filters: any) =>
    [...queryKeys.transactions, 'list', filters] as const,
  transactionDetail: (id: string) =>
    [...queryKeys.transactions, 'detail', id] as const,
}

Usage Example

// hooks/useTransactions.ts
export function useTransactions(filters: TransactionFiltersDTO) {
  return useQuery({
    queryKey: queryKeys.transactionList(filters),
    queryFn: () => transactionsService.getAll(filters),
    staleTime: 5 * 60 * 1000,
  })
}

export function useCreateTransaction() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: (data: CreateTransactionDTO) =>
      transactionsService.create(data),
    onSuccess: () => {
      // Invalidate and refetch
      queryClient.invalidateQueries({
        queryKey: queryKeys.transactions
      })
      toast.success('Transaction created successfully')
    },
    onError: (error) => {
      toast.error(error.message)
    },
  })
}

Client State - Zustand

Lightweight client-side state for UI preferences and filters:

// lib/store.ts
interface FilterState {
  selectedAccount: string | null
  dateRange: { start: Date; end: Date }
  selectedCategories: string[]
  searchQuery: string
  setSelectedAccount: (id: string | null) => void
  setDateRange: (range: { start: Date; end: Date }) => void
  // ... other actions
}

export const useFilterStore = create<FilterState>()(
  persist(
    (set) => ({
      selectedAccount: null,
      dateRange: { start: startOfMonth(new Date()), end: new Date() },
      selectedCategories: [],
      searchQuery: '',
      setSelectedAccount: (id) => set({ selectedAccount: id }),
      setDateRange: (range) => set({ dateRange: range }),
      // ... other implementations
    }),
    { name: 'filter-storage' }
  )
)

⚑ Performance Optimizations

Server Components

Most pages use React Server Components for optimal performance:

// app/(dashboard)/dashboard/page.tsx - Server Component
export default async function DashboardPage() {
  // Data fetching happens on the server
  // No JavaScript sent to the client for this component
  return (
    <div>
      <DashboardStats /> {/* Server Component */}
      <RecentTransactions /> {/* Server Component */}
      <InteractiveChart /> {/* 'use client' - Client Component */}
    </div>
  )
}

Code Splitting

Webpack is configured for optimal bundle splitting:

// next.config.js
webpack: (config, { isServer }) => {
  if (!isServer) {
    config.optimization.splitChunks = {
      chunks: 'all',
      cacheGroups: {
        recharts: {
          test: /[\\/]node_modules[\\/](recharts|d3-.*)[\\/]/,
          name: 'recharts',
          priority: 20,
        },
        reactQuery: {
          test: /[\\/]node_modules[\\/](@tanstack[\\/]react-query)[\\/]/,
          name: 'react-query',
          priority: 15,
        },
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          priority: 10,
        },
      },
    }
  }
  return config
}

Virtual Scrolling

Large transaction lists use virtual scrolling for optimal performance:

// components/VirtualTransactionList.tsx
import { useVirtualizer } from '@tanstack/react-virtual'

export function VirtualTransactionList({ transactions }: Props) {
  const parentRef = useRef<HTMLDivElement>(null)

  const virtualizer = useVirtualizer({
    count: transactions.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 80,
    overscan: 5,
  })

  return (
    <div ref={parentRef} className="h-[600px] overflow-auto">
      <div style={{ height: `${virtualizer.getTotalSize()}px` }}>
        {virtualizer.getVirtualItems().map((virtualItem) => (
          <TransactionCard
            key={virtualItem.key}
            transaction={transactions[virtualItem.index]}
            style={{
              transform: `translateY(${virtualItem.start}px)`,
            }}
          />
        ))}
      </div>
    </div>
  )
}

Image Optimization

Next.js Image component with optimal configuration:

// next.config.js
images: {
  formats: ['image/avif', 'image/webp'],
  deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
  imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
}

Production Optimizations

  • SWC Minification: Faster than Terser
  • Tree Shaking: Remove unused code
  • Console Log Removal: Automatically removed in production
  • Source Maps: Disabled in production for smaller bundle size

🚒 Deployment

Prerequisites

  • Backend API deployed and accessible
  • PostgreSQL database (Supabase recommended)
  • Environment variables configured

Vercel Deployment (Recommended)

  1. Connect Repository
vercel
  1. Configure Environment Variables

Add all .env.local variables to Vercel project settings:

  • NEXT_PUBLIC_API_URL
  • API_URL
  • NEXT_PUBLIC_APP_URL
  • Other feature flags
  1. Deploy
vercel --prod

Docker Deployment

# Dockerfile
FROM node:18-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:18-alpine AS runner

WORKDIR /app
ENV NODE_ENV production

COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

EXPOSE 3001
CMD ["npm", "start"]
docker build -t bankdash-frontend .
docker run -p 3001:3001 bankdash-frontend

Environment Variables

Variable Description Required
NEXT_PUBLIC_API_URL Backend API URL Yes
API_URL Backend API URL (server-side) Yes
NEXT_PUBLIC_APP_URL Frontend URL Yes
NEXT_PUBLIC_API_TIMEOUT API timeout in milliseconds No
NEXT_PUBLIC_ENABLE_AI_INSIGHTS Enable AI features No
NEXT_PUBLIC_ENABLE_BANK_SYNC Enable bank synchronization No

πŸ§ͺ Testing

Running Tests

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with UI
npm run test:ui

# Generate coverage report
npm run test:coverage

Test Structure

tests/
β”œβ”€β”€ unit/
β”‚   β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ hooks/
β”‚   └── utils/
β”œβ”€β”€ integration/
β”‚   └── api/
└── e2e/
    └── flows/

Example Test

// tests/unit/components/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from '@/components/ui/Button'

describe('Button', () => {
  it('renders correctly', () => {
    render(<Button>Click me</Button>)
    expect(screen.getByText('Click me')).toBeInTheDocument()
  })

  it('handles click events', () => {
    const handleClick = jest.fn()
    render(<Button onClick={handleClick}>Click me</Button>)
    fireEvent.click(screen.getByText('Click me'))
    expect(handleClick).toHaveBeenCalledTimes(1)
  })
})

🀝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Coding Standards

  • Follow TypeScript best practices
  • Use ESLint and Prettier for code formatting
  • Write tests for new features
  • Update documentation as needed
  • Follow conventional commit messages

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


πŸ™ Acknowledgments


πŸ“ž Support

For issues, questions, or contributions:

  • Open an issue on GitHub
  • Contact the maintainers
  • Check the documentation

πŸ—Ί Roadmap

  • AI-powered insights and recommendations
  • Real-time notifications with WebSockets
  • Multi-currency support
  • Bank account integration (Plaid)
  • Mobile app (React Native)
  • Advanced reporting (PDF generation)
  • Receipt OCR scanning
  • Collaborative budgets
  • Tax optimization tools
  • Investment tracking

Built with ❀️ using Next.js, TypeScript, and Tailwind CSS

⬆ Back to Top

About

Financial-banking-analytics portfolio

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages