├── README.md ├── Expense Tracker.py └── Code /README.md: -------------------------------------------------------------------------------- 1 | # Expense Tracker with Budget Alerts 2 | 3 | ## Overview 4 | This is a full-stack **Expense Tracker** application built with **React (frontend)** and **FastAPI (backend)**. The app allows users to: 5 | - **Track expenses** by adding and deleting items. 6 | - **Set a budget** and receive alerts when nearing the limit. 7 | - **Persist data** via API endpoints. 8 | 9 | ## Features 10 | ✅ Add, view, and delete expenses. 11 | ✅ Set and update budget dynamically. 12 | ✅ Alert when expenses reach **90% of the budget**. 13 | ✅ Responsive UI built with **shadcn/ui**, **TailwindCSS**, and **Framer Motion**. 14 | ✅ Fast and lightweight backend using **FastAPI**. 15 | 16 | --- 17 | 18 | ## Tech Stack 19 | - **Frontend:** React, TailwindCSS, shadcn/ui, Framer Motion 20 | - **Backend:** FastAPI, Pydantic 21 | - **Tools:** Fetch API for HTTP requests 22 | 23 | --- 24 | 25 | ## Installation & Setup 26 | 27 | ### Prerequisites 28 | Ensure you have the following installed: 29 | - Node.js & npm 30 | - Python 3.9+ 31 | 32 | ### Clone Repository 33 | ```sh 34 | git clone https://github.com/YOUR_USERNAME/expense-tracker.git 35 | cd expense-tracker 36 | ``` 37 | 38 | ### Backend Setup (FastAPI) 39 | 1. Install dependencies: 40 | ```sh 41 | pip install fastapi uvicorn 42 | ``` 43 | 2. Run the server: 44 | ```sh 45 | uvicorn server:app --reload 46 | ``` 47 | The API will be available at `http://127.0.0.1:8000` 48 | 49 | ### Frontend Setup (React) 50 | 1. Install dependencies: 51 | ```sh 52 | npm install 53 | ``` 54 | 2. Run the development server: 55 | ```sh 56 | npm run dev 57 | ``` 58 | The app will be available at `http://localhost:3000` 59 | 60 | --- 61 | 62 | ## API Endpoints 63 | ### Expenses 64 | - **GET /expenses** → Retrieve all expenses. 65 | - **POST /expenses** → Add a new expense. 66 | - **DELETE /expenses/{index}** → Delete an expense. 67 | 68 | ### Budget 69 | - **GET /budget** → Retrieve the current budget. 70 | - **PUT /budget** → Update the budget. 71 | 72 | --- 73 | 74 | ## Contributing 75 | 1. Fork the repository. 76 | 2. Create a new branch (`feature-branch`). 77 | 3. Commit and push your changes. 78 | 4. Open a pull request. 79 | 80 | --- 81 | 82 | ## License 83 | This project is open-source and available under the **MIT License**. 84 | 85 | --- 86 | 87 | ## Contact 88 | For issues or suggestions, create an [issue](https://github.com/Akajiaku1//expense-tracker/issues) . 89 | 90 | --- 91 | 92 | 🚀 **Happy Expense Tracking!** 93 | 94 | -------------------------------------------------------------------------------- /Expense Tracker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | 7 | class ExpenseTracker: 8 | def __init__(self, budget): 9 | self.budget = budget 10 | self.expenses = [] 11 | self.category_totals = {} # Track total expenses per category 12 | 13 | def add_expense(self, amount, category): 14 | self.expenses.append({"amount": amount, "category": category}) 15 | 16 | # Update category totals 17 | if category in self.category_totals: 18 | self.category_totals[category] += amount 19 | else: 20 | self.category_totals[category] = amount 21 | 22 | self.check_budget() 23 | 24 | def check_budget(self): 25 | total_expenses = sum(expense["amount"] for expense in self.expenses) 26 | if total_expenses > self.budget: 27 | overspend = total_expenses - self.budget 28 | print(f"⚠️ Budget Alert! Exceeded by ${overspend:.2f}") 29 | else: 30 | remaining = self.budget - total_expenses 31 | print(f"✅ Budget OK. Remaining: ${remaining:.2f}") 32 | 33 | def view_expenses(self): 34 | if not self.expenses: 35 | print("No expenses recorded.") 36 | return 37 | print("\n--- All Expenses ---") 38 | for idx, expense in enumerate(self.expenses, 1): 39 | print(f"{idx}. Category: {expense['category']}, Amount: ${expense['amount']:.2f}") 40 | 41 | def view_summary(self): 42 | total_expenses = sum(expense["amount"] for expense in self.expenses) 43 | print("\n--- Budget Summary ---") 44 | print(f"Total Budget: ${self.budget:.2f}") 45 | print(f"Total Spent: ${total_expenses:.2f}") 46 | print(f"Remaining: ${self.budget - total_expenses:.2f}") 47 | 48 | # Display category-wise totals 49 | print("\n--- Category Breakdown ---") 50 | if not self.category_totals: 51 | print("No category expenses yet.") 52 | else: 53 | for category, total in self.category_totals.items(): 54 | print(f"{category}: ${total:.2f}") 55 | print("--------------------------") 56 | 57 | 58 | def main(): 59 | print("🤑 Expense Tracker with Budget Alerts 🚨") 60 | budget = float(input("Enter your monthly budget: $")) 61 | tracker = ExpenseTracker(budget) 62 | 63 | while True: 64 | print("\nOptions:") 65 | print("1. ➕ Add Expense") 66 | print("2. 📜 View Expenses") 67 | print("3. 📊 View Summary") 68 | print("4. ❌ Exit") 69 | choice = input("Choose an option (1-4): ").strip() 70 | 71 | if choice == "1": 72 | amount = float(input("Enter amount: $")) 73 | category = input("Category (e.g., Food, Rent): ").strip().title() 74 | tracker.add_expense(amount, category) 75 | elif choice == "2": 76 | tracker.view_expenses() 77 | elif choice == "3": 78 | tracker.view_summary() 79 | elif choice == "4": 80 | print("Goodbye! 💸") 81 | break 82 | else: 83 | print("Invalid choice. Try again.") 84 | 85 | 86 | if __name__ == "__main__": 87 | main() 88 | 89 | 90 | # In[ ]: 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /Code: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { Card, CardContent } from "@/components/ui/card"; 3 | import { Button } from "@/components/ui/button"; 4 | import { Input } from "@/components/ui/input"; 5 | import { motion } from "framer-motion"; 6 | import { Trash, PlusCircle, AlertTriangle } from "lucide-react"; 7 | 8 | export default function ExpenseTracker() { 9 | const [expenses, setExpenses] = useState([]); 10 | const [budget, setBudget] = useState(500); // Default budget 11 | const [newExpense, setNewExpense] = useState({ name: "", amount: "" }); 12 | const [alert, setAlert] = useState(false); 13 | 14 | useEffect(() => { 15 | const total = expenses.reduce((sum, expense) => sum + expense.amount, 0); 16 | setAlert(total >= budget * 0.9); // Alert when reaching 90% of budget 17 | }, [expenses, budget]); 18 | 19 | const addExpense = () => { 20 | if (!newExpense.name || !newExpense.amount) return; 21 | const amount = parseFloat(newExpense.amount); 22 | if (isNaN(amount) || amount <= 0) return; 23 | setExpenses([...expenses, { ...newExpense, amount }]); 24 | setNewExpense({ name: "", amount: "" }); 25 | }; 26 | 27 | const removeExpense = (index) => { 28 | setExpenses(expenses.filter((_, i) => i !== index)); 29 | }; 30 | 31 | return ( 32 |
33 |

Expense Tracker

34 | {alert && ( 35 | 39 | Warning: You are nearing your budget limit! 40 | 41 | )} 42 | 43 | 44 |
45 | setNewExpense({ ...newExpense, name: e.target.value })} 50 | /> 51 | setNewExpense({ ...newExpense, amount: e.target.value })} 56 | /> 57 | 60 |
61 |
62 |
63 | 64 | 65 |

Expenses

66 | {expenses.length === 0 ? ( 67 |

No expenses yet.

68 | ) : ( 69 |
    70 | {expenses.map((expense, index) => ( 71 |
  • 72 | {expense.name} - ${expense.amount.toFixed(2)} 73 | 76 |
  • 77 | ))} 78 |
79 | )} 80 |
81 |
82 | 83 | 84 | Budget: 85 | setBudget(parseFloat(e.target.value) || 0)} 89 | className="border p-1 rounded w-20" 90 | /> 91 | 92 | 93 |
94 | ); 95 | } 96 | --------------------------------------------------------------------------------