diff --git a/financial-tracker/api-gateway/app/app.py b/financial-tracker/api-gateway/app/app.py
index 67d640d46436493486b5925f70776080adfc7d61..c3961c4e73dc3dc6d3f76873775ab59aded6259c 100644
--- a/financial-tracker/api-gateway/app/app.py
+++ b/financial-tracker/api-gateway/app/app.py
@@ -95,6 +95,43 @@ def me():
         app.logger.error(f"Error contacting user-service: {e}")
         return jsonify({"error": "User service unavailable"}), 503
 
+# Configure transaction service URL
+TRANSACTION_SERVICE_URL = os.getenv('TRANSACTION_SERVICE_URL', 'http://transaction-service:5002')
+
+@app.route('/transactions', methods=["GET", "POST"])
+def handle_transactions():
+    """
+    Forward GET/POST transaction requests to the transaction-service.
+    """
+    try:
+        if request.method == "GET":
+            response = requests.get(f"{TRANSACTION_SERVICE_URL}/transactions", params=request.args)
+        else:
+            response = requests.post(f"{TRANSACTION_SERVICE_URL}/transactions", json=request.get_json())
+
+        return jsonify(response.json()), response.status_code
+
+    except requests.exceptions.RequestException as e:
+        app.logger.error(f"Error contacting transaction-service: {e}")
+        return jsonify({"error": "Transaction service unavailable"}), 503
+
+
+@app.route('/transactions/<int:tx_id>', methods=["PUT", "DELETE"])
+def handle_transaction_by_id(tx_id):
+    """
+    Forward PUT/DELETE requests to transaction-service for specific transaction.
+    """
+    try:
+        if request.method == "PUT":
+            response = requests.put(f"{TRANSACTION_SERVICE_URL}/transactions/{tx_id}", json=request.get_json())
+        else:
+            response = requests.delete(f"{TRANSACTION_SERVICE_URL}/transactions/{tx_id}")
+
+        return jsonify(response.json()), response.status_code
+
+    except requests.exceptions.RequestException as e:
+        app.logger.error(f"Error contacting transaction-service: {e}")
+        return jsonify({"error": "Transaction service unavailable"}), 503
 
 @app.route('/expenses', methods=['POST'])
 def add_expense():
@@ -230,6 +267,114 @@ def fetch_categories():
         return jsonify({"error": "Transaction service unavailable"}), 503
 
 
+# INCOME ROUTES
+@app.route('/income', methods=['POST'])
+@jwt_required()
+def add_income():
+    """Forward request to transaction-service to add an income."""
+    token = request.headers.get("Authorization")
+    if not token:
+        app.logger.error("Authorization token is missing.")
+        return jsonify({"error": "Authorization token is missing"}), 400
+    try:
+        headers = {"Authorization": token, "Content-Type": "application/json"}
+        data = request.get_json()
+        app.logger.info(f"Forwarding income addition request: {data}")
+
+        response = requests.post(f"{TRANSACTION_SERVICE_URL}/income", headers=headers, json=request.get_json())
+        return jsonify(response.json()), response.status_code
+    except requests.exceptions.RequestException:
+        app.logger.error(f"Error contacting transaction-service: {e}")
+        return jsonify({"error": "Transaction service unavailable"}), 503
+
+@app.route('/income', methods=['GET'])
+@jwt_required()
+def fetch_income():
+    """Fetch income for the logged-in user."""
+    token = request.headers.get("Authorization")
+
+    if not token:
+        return jsonify({"error": "Authorization token is missing"}), 400
+    try:
+        headers = {"Authorization": token}
+        response = requests.get(f"{TRANSACTION_SERVICE_URL}/income", headers=headers)
+        app.logger.info(f"Response from transaction-service (income): {response.status_code} - {response.json()}")
+        return jsonify(response.json()), response.status_code
+    except requests.exceptions.RequestException:
+        return jsonify({"error": "Transaction service unavailable"}), 503
+    
+@app.route('/income/<income_id>', methods=['PUT'])
+@jwt_required()
+def update_income(income_id):
+    """Forward request to transaction-service to edit an income (excluding currency)."""
+    token = request.headers.get("Authorization")
+
+    if not token:
+        app.logger.error("Authorization token is missing.")
+        return jsonify({"error": "Authorization token is missing"}), 400
+    try:
+        headers = {"Authorization": token, "Content-Type": "application/json"}
+        data = request.get_json()
+        # Ensure currency field is not included in the update request
+        if "currency" in data:
+            app.logger.warning(f"Attempt to modify currency field in income {income_id}")
+            return jsonify({"error": "Currency field cannot be updated"}), 400
+
+        app.logger.info(f"Forwarding income update request for ID {income_id}: {data}")
+
+        response = requests.put(f"{TRANSACTION_SERVICE_URL}/income/{income_id}", headers=headers, json=request.get_json())
+        return jsonify(response.json()), response.status_code
+    except requests.exceptions.RequestException:
+        app.logger.error(f"Error contacting transaction-service: {e}")
+        return jsonify({"error": "Transaction service unavailable"}), 503
+    
+@app.route('/income/<income_id>', methods=['DELETE'])
+@jwt_required()
+def delete_income(income_id):
+    """Forward request to transaction-service to delete an income."""
+    token = request.headers.get("Authorization")
+    if not token:
+        app.logger.error("Authorization token is missing.")
+        return jsonify({"error": "Authorization token is missing"}), 400
+    try:
+        headers = {"Authorization": token}
+        response = requests.delete(f"{TRANSACTION_SERVICE_URL}/income/{income_id}", headers=headers)
+        
+        if response.status_code == 200:
+            app.logger.info(f"Income with ID {income_id} deleted successfully.")
+            return jsonify({"message": "Income deleted successfully."}), 200
+
+        else:
+                app.logger.error(f"Failed to delete income with ID {income_id}.")
+                return jsonify({"error": "Failed to delete income"}), response.status_code
+
+    except requests.exceptions.RequestException:
+        app.logger.error(f"Error contacting transaction-service: {e}")
+        return jsonify({"error": "Transaction service unavailable"}), 503
+
+@app.route('/income/sources', methods=['GET'])
+@jwt_required()
+def get_income_sources():
+    """Fetch available income sources from transaction-service."""
+    token = request.headers.get("Authorization")
+    if not token:
+        return jsonify({"error": "Authorization token is missing"}), 400
+    
+    try:
+        user_id = get_jwt_identity()  # Extract user_id from JWT token
+        headers = {"Authorization": token}
+        response = requests.get(f"{TRANSACTION_SERVICE_URL}/income/sources", headers=headers)
+
+        # Add CORS headers to the response
+        response_data = jsonify(response.json())
+        response_data.headers.add("Access-Control-Allow-Origin", "http://localhost:3000")
+        response_data.headers.add("Access-Control-Allow-Headers", "Content-Type, Authorization")
+        response_data.headers.add("Access-Control-Allow-Methods", "GET, OPTIONS")
+
+        return jsonify(response.json()), response.status_code
+    except requests.exceptions.RequestException:
+        return jsonify({"error": "Transaction service unavailable"}), 503
+
 @app.route('/budgets', methods=['POST'])
 @jwt_required()
 def add_budget():
@@ -331,3 +476,4 @@ def fetch_budgets():
 
 if __name__ == '__main__':
     app.run(host='0.0.0.0', port=8000, debug=True)
+
diff --git a/financial-tracker/docker-compose.yml b/financial-tracker/docker-compose.yml
index 04833088ae3ec4398cbd74ee74285a133f37efb6..66cca04e3a3dec9920d14bbf7260eb7a602de0b3 100644
--- a/financial-tracker/docker-compose.yml
+++ b/financial-tracker/docker-compose.yml
@@ -1,4 +1,4 @@
-version: '3.8'
+
 
 services:
   api-gateway:
diff --git a/financial-tracker/frontend/.dockerignore b/financial-tracker/frontend/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..01c0af06357b2cbe08c90e7b6422138e0e3ebc6d
--- /dev/null
+++ b/financial-tracker/frontend/.dockerignore
@@ -0,0 +1,5 @@
+node_modules
+build
+.dockerignore
+Dockerfile
+npm-debug.log
diff --git a/financial-tracker/frontend/Dockerfile b/financial-tracker/frontend/Dockerfile
index cc0921d62435390f4dc454cb07645abcded30952..d1f85e592f0123d788592ddfd853b0c24d8b4568 100644
--- a/financial-tracker/frontend/Dockerfile
+++ b/financial-tracker/frontend/Dockerfile
@@ -21,3 +21,5 @@ EXPOSE 3000
 
 # Start the React app
 CMD ["npm", "start"]
+
+
diff --git a/financial-tracker/frontend/package-lock.json b/financial-tracker/frontend/package-lock.json
index dee52157b450d06e5b5d8f23863adb289323aa3d..24bd576d4d7432126b44ccab8c26f0024d8e18ff 100644
--- a/financial-tracker/frontend/package-lock.json
+++ b/financial-tracker/frontend/package-lock.json
@@ -19,6 +19,7 @@
         "react": "^19.0.0",
         "react-chartjs-2": "^5.3.0",
         "react-dom": "^19.0.0",
+        "react-icons": "^5.5.0",
         "react-paginate": "^8.3.0",
         "react-router-dom": "^7.4.0",
         "react-scripts": "5.0.1",
@@ -14311,6 +14312,15 @@
       "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==",
       "license": "MIT"
     },
+    "node_modules/react-icons": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
+      "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "react": "*"
+      }
+    },
     "node_modules/react-is": {
       "version": "17.0.2",
       "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
diff --git a/financial-tracker/frontend/package.json b/financial-tracker/frontend/package.json
index d81507da563b72f58c878bd7636a9b6be837ace0..ffb0bc3b18555309b54a760b824218b8097ef72c 100644
--- a/financial-tracker/frontend/package.json
+++ b/financial-tracker/frontend/package.json
@@ -14,6 +14,7 @@
     "react": "^19.0.0",
     "react-chartjs-2": "^5.3.0",
     "react-dom": "^19.0.0",
+    "react-icons": "^5.5.0",
     "react-paginate": "^8.3.0",
     "react-router-dom": "^7.4.0",
     "react-scripts": "5.0.1",
diff --git a/financial-tracker/frontend/src/App.js b/financial-tracker/frontend/src/App.js
index 2cf786b0ff5012f5aa99feb4facdf6bdafcf9859..6dbc470864737ab6f2a9049b2928793c09670f6c 100644
--- a/financial-tracker/frontend/src/App.js
+++ b/financial-tracker/frontend/src/App.js
@@ -5,6 +5,10 @@ import Home from "./pages/Home";
 import Register from "./pages/Register";
 import Login from "./pages/Login";
 import Dashboard from "./pages/Dashboard";
+import Income from "./pages/Income";
+
+
+
 import Expenses from "./pages/Expenses";
 import Budget from "./pages/Budget";
 
@@ -34,6 +38,7 @@ function App() {
             {token ? (
               <>
                 <li><Link to="/dashboard">Dashboard</Link></li>
+                <li><Link to="/income">Income</Link></li> {/* 👈 Add this line */}
                 <li><Link to="/expenses">Expenses</Link></li>
                 <li><Link to="/budget">Budget</Link></li> 
                 <li>
@@ -56,6 +61,7 @@ function App() {
           <Route path="/register" element={<Register />} />
           <Route path="/login" element={<Login setToken={setToken} />} />
           <Route path="/dashboard" element={<Dashboard setToken={setToken} />} />
+          <Route path="/income" element={<Income />} />
           <Route path="/expenses" element={<Expenses />} />
           <Route path="/budget" element={<Budget />} /> 
         </Routes>
diff --git a/financial-tracker/frontend/src/pages/Income.js b/financial-tracker/frontend/src/pages/Income.js
new file mode 100644
index 0000000000000000000000000000000000000000..fed1f8fbcc73507bb9a248ca2a032c4df9a10b5e
--- /dev/null
+++ b/financial-tracker/frontend/src/pages/Income.js
@@ -0,0 +1,689 @@
+import React, { useState, useEffect } from "react";
+import axios from "axios";
+import { jwtDecode } from 'jwt-decode';
+import ReactPaginate from "react-paginate";
+import IncomeChart from "./IncomeChart";
+import "../styles/Income.css";
+
+
+
+const Incomes = () => {
+  const token = localStorage.getItem("token");
+
+  const [incomes, setIncomes] = useState([]);
+  const [filteredIncomes, setFilteredIncomes] = useState([]);
+  const [currentPage, setCurrentPage] = useState(0);
+  const incomesPerPage = 5;
+  const offset = currentPage * incomesPerPage;
+  const currentIncomes = filteredIncomes.slice(offset, offset + incomesPerPage);
+  const [sourceOptions, setSourceOptions] = useState([]);
+  const [sources, setSources] = useState([]);
+  const [isSessionValid, setIsSessionValid] = useState(true);
+  
+  /*const SourceEnum = [
+    "SALARY",
+    "BUSINESS",
+    "FREELANCE",
+    "INVESTMENTS",
+    "GIFTS",
+    "LOANS",
+    "OTHER"
+  ];*/
+
+  const [newIncome, setNewIncome] = useState({
+    date: "",
+    source: "",
+    amount: "",
+    description: "",
+    recurring: false,
+    recurring_type: "",
+    frequency: "",
+    interval: "",
+    end_repeat: "never",
+    end_date: "",
+  });
+
+  const [editIncome, setEditIncome] = useState({
+    id: null,
+    date: "",
+    source: "",
+    amount: "",
+    description: "",
+    recurring: false,
+    recurring_type: "",
+    frequency: "",
+    interval: "",
+    end_repeat: "never",
+    end_date: "",
+  });
+
+  const [filters, setFilters] = useState({
+    source: "",
+    minAmount: "",
+    maxAmount: "",
+    startDate: "",
+    endDate: "",
+  });
+
+  const [sortConfig, setSortConfig] = useState({ key: "", direction: "asc" });
+  const [showFilters, setShowFilters] = useState(false);
+  const [chartType, setChartType] = useState("bar");
+  const [timeRange, setTimeRange] = useState("monthly");
+  const [isModalOpen, setIsModalOpen] = useState(false);
+  const [deleteMessage, setDeleteMessage] = useState("");
+  const [successMessage, setSuccessMessage] = useState("");
+
+  const openModal = () => setIsModalOpen(true);
+  const closeModal = () => setIsModalOpen(false);
+
+// Check if token exists in localStorage to determine if session is valid
+  useEffect(() => {
+    const token = localStorage.getItem("token");
+
+    if (token) {
+      setIsSessionValid(true); // Token exists, session is valid
+    } else {
+      setIsSessionValid(false); // No token, session invalid
+      alert("Your session has expired. Please log in again.");
+      window.location.href = "/login"; // Redirect to login page
+    }
+  }, []);
+  
+  useEffect(() => {
+    fetchIncomes();
+    fetchSources();
+  }, []);
+
+  const fetchIncomes = async () => {
+    const token = localStorage.getItem("token");
+    try {
+      const res = await axios.get("http://localhost:8000/income", {
+        headers: { Authorization: `Bearer ${token}` },
+      });
+      setIncomes(res.data.incomes);
+      setFilteredIncomes(res.data.incomes);
+    } catch (err) {
+      console.error("Error fetching incomes:", err);
+    }
+  };
+
+  const fetchSources = async () => {
+    const token = localStorage.getItem("token");
+    try {
+      console.log("Token from localStorage:", token);
+  
+      if (!token) {
+        console.error("No token found, user might not be logged in.");
+        return;
+      }
+  
+      const decoded = jwtDecode(token);
+      console.log("Decoded JWT:", decoded);
+  
+      // Use 'sub' as user_id
+      const user_id = decoded.sub;
+      if (!user_id) {
+        console.error("User ID is undefined. Check JWT structure.");
+        return;
+      }
+  
+      const response = await axios.get("http://localhost:8000/income/sources", {
+        headers: { Authorization: `Bearer ${token}` },
+      });
+      
+      console.log("Sources Response:", response.data);
+  
+      if (Array.isArray(response.data.sources)) {
+      setSources(response.data.sources);
+      } else {
+        console.error("Sources data is not in expected format", response.data);
+     }
+    } catch (err) {
+      console.error("Error fetching sources:", err);
+    }
+  };
+  
+
+  const handleAddIncome = async (e) => {
+    e.preventDefault();
+    const token = localStorage.getItem("token");
+    if (!token) {
+      alert("Please log in to add income.");
+      return;
+    }
+    try {
+      const res = await axios.post(
+        "http://localhost:8000/income",
+        {
+          ...newIncome,
+          currency: "GBP", // or default from user profile
+        },
+        {
+          headers: { Authorization: `Bearer ${token}` },
+        }
+      );
+      setIncomes([...incomes, res.data.income]);
+      setFilteredIncomes([...filteredIncomes, res.data.income]);
+      setNewIncome({
+        date: "",
+        source: "",
+        amount: "",
+        description: "",
+        recurring: false,
+        recurring_type: "",
+        frequency: "",
+        interval: "",
+        end_repeat: "never",
+        end_date: "",
+      });
+      setSuccessMessage("Income added successfully.");
+      // Wait 1.5 seconds before refreshing
+      setTimeout(() => {
+        window.location.reload();
+    }, 1500);
+    } catch (err) {
+      console.error("Failed to add income:", err);
+      alert(
+        `Error adding income: ${
+          err.response ? err.response.data.error : err.message
+        }`
+      );
+
+    }
+  };
+
+  
+  const handleEdit = (income) => {
+    const { currency, ...cleanedIncome } = income;
+    setEditIncome(cleanedIncome);
+    openModal();
+  };
+  
+
+  const handleSaveEdit = async (e, id) => {
+    e.preventDefault();
+    console.log("Saving changes for ID:", id); 
+    const token = localStorage.getItem("token");
+    
+    const {
+      date,
+      source,
+      amount,
+      description,
+      recurring,
+      recurring_type,
+      frequency,
+      interval,
+      end_repeat,
+      end_date
+    } = editIncome;
+  
+    const payload = {
+      date,
+      source,
+      amount: parseFloat(amount), // ensure number
+      description,
+      recurring,
+      recurring_type,
+      frequency,
+      interval,
+      end_repeat,
+      end_date,
+    };
+    try {
+      const { currency, ...incomeData } = editIncome;
+      if (editIncome.end_repeat === "on_date" && !editIncome.end_date) {
+        alert("Please specify an end date.");
+        return;
+      }
+      if (!editIncome.end_date) {
+        delete editIncome.end_date;
+      }
+      
+      console.log("Sending to API:", editIncome);
+
+      const res = await axios.put(
+        `http://localhost:8000/income/${id}`,
+        payload,
+        { headers: { Authorization: `Bearer ${token}` } }
+      );
+      setIncomes(incomes.map((i) => (i.id === id ? res.data.income : i)));
+      setFilteredIncomes(filteredIncomes.map((i) => (i.id === id ? res.data.income : i)));
+      setSuccessMessage("Income updated successfully.");
+      // Wait 1.5 seconds before refreshing
+      setTimeout(() => {
+        window.location.reload();
+    }, 1500);
+      closeModal();
+    } catch (err) {
+      console.error("Failed to update income:", err);
+      alert(
+        `Error updating income: ${
+          err.response ? err.response.data.error || err.response.data.message : err.message
+        }`
+      );
+    }
+  };
+
+  const handleDelete = async (id) => {
+    const confirmDelete = window.confirm("Delete this income?");
+    if (!confirmDelete) return;
+    const token = localStorage.getItem("token");
+    try {
+      await axios.delete(`http://localhost:8000/income/${id}`, {
+        headers: { Authorization: `Bearer ${token}` },
+      });
+      const updated = incomes.filter((i) => i.id !== id);
+      setIncomes(updated);
+      setFilteredIncomes(updated);
+      setDeleteMessage("Income deleted successfully.");
+      // Wait 1.5 seconds before refreshing
+      setTimeout(() => {
+        window.location.reload();
+    }, 1500);
+    } catch (err) {
+      console.error("Failed to delete income:", err);
+      setDeleteMessage("Error deleting income. Please try again.");
+    }
+  };
+
+  const handleChange = (e, stateSetter) => {
+    const { name, type, value, checked } = e.target;
+    stateSetter((prev) => ({
+      ...prev,
+      [name]: type === "checkbox" ? checked : value,
+    }));
+  };
+
+  const sortData = (key) => {
+    let direction = "asc";
+    if (sortConfig.key === key && sortConfig.direction === "asc") direction = "desc";
+    const sorted = [...filteredIncomes].sort((a, b) => {
+      if (key === "date") return direction === "asc" ? new Date(a.date) - new Date(b.date) : new Date(b.date) - new Date(a.date);
+      if (key === "amount") return direction === "asc" ? a.amount - b.amount : b.amount - a.amount;
+      if (key === "source") return direction === "asc" ? a.source.localeCompare(b.source) : b.source.localeCompare(a.source);
+      return 0;
+    });
+    setFilteredIncomes(sorted);
+    setSortConfig({ key, direction });
+  };
+
+  const handlePageClick = (event) => {
+    setCurrentPage(event.selected);
+  };
+
+  const applyFilters = () => {
+    let filtered = incomes.filter((income) => {
+      return (
+        (!filters.source || income.source.toLowerCase().includes(filters.source.toLowerCase())) &&
+        (!filters.minAmount || income.amount >= parseFloat(filters.minAmount)) &&
+        (!filters.maxAmount || income.amount <= parseFloat(filters.maxAmount)) &&
+        (!filters.startDate || income.date >= filters.startDate) &&
+        (!filters.endDate || income.date <= filters.endDate)
+      );
+    });
+    setFilteredIncomes(filtered);
+  };
+
+  return (
+    <div className="income-container">
+      <h1>Income</h1>
+
+      {successMessage && <div className="success-message">{successMessage}</div>}
+      {deleteMessage && <div className="delete-message">{deleteMessage}</div>}
+
+      {/* Add Income Form */}
+      <form className="income-form" onSubmit={handleAddIncome}>
+      <div className="income-inputs">
+        <div className="income-row">
+          <div className="input-group">
+          <label>Source</label>
+          <input
+              type="text"
+              name="source"
+              placeholder="Source"
+              value={newIncome.source}
+              onChange={(e) => handleChange(e, setNewIncome)} required
+            />
+        </div>
+        
+          <div className="input-group">
+            <label>Amount</label>
+            <input type="number" name="amount" step="0.01" value={newIncome.amount} onChange={(e) => handleChange(e, setNewIncome)} required />
+          </div>
+        </div>
+        <div className="income-row">
+          <div className="input-group">
+            <label>Date</label>
+            <input type="date" name="date" value={newIncome.date} onChange={(e) => handleChange(e, setNewIncome)} required />
+          </div>
+          <div className="input-group">
+            <label>Description</label>
+            <input type="text" name="description" value={newIncome.description} onChange={(e) => handleChange(e, setNewIncome)} />
+          </div>
+        </div>
+
+        <div className="checkbox-container">
+          <label className="checkbox-label">
+            <input type="checkbox" name="recurring" checked={newIncome.recurring} onChange={(e) => handleChange(e, setNewIncome)} />
+            Recurring Income
+          </label>
+        </div>
+      </div>
+
+        {newIncome.recurring && (
+          <div className="recurring-options">
+            <div className="recurring-left">
+              <label>Recurring Type</label>
+              <select name="recurring_type" value={newIncome.recurring_type} onChange={(e) => handleChange(e, setNewIncome)}>
+                <option value="">Select...</option>
+                <option value="daily">Daily</option>
+                <option value="weekly">Weekly</option>
+                <option value="every_two_weeks">Every Two Weeks</option>
+                <option value="monthly">Monthly</option>
+                <option value="yearly">Yearly</option>
+                <option value="custom">Custom</option>
+              </select>
+
+              {newIncome.recurring_type === "custom" && (
+                <>
+                  <label>Frequency</label>
+                  <select name="frequency" value={newIncome.frequency} onChange={(e) => handleChange(e, setNewIncome)}>
+                    <option value="">Select...</option>
+                    <option value="daily">Daily</option>
+                    <option value="weekly">Weekly</option>
+                    <option value="monthly">Monthly</option>
+                    <option value="yearly">Yearly</option>
+                  </select>
+                  <label>Interval</label>
+                  <input type="number" name="interval" value={newIncome.interval} onChange={(e) => handleChange(e, setNewIncome)} />
+                </>
+              )}
+            </div>
+            <div className="recurring-right">
+              <label>End Repeat</label>
+              <select name="end_repeat" value={newIncome.end_repeat} onChange={(e) => handleChange(e, setNewIncome)}>
+                <option value="never">Never</option>
+                <option value="on_date">On Date</option>
+              </select>
+
+              {newIncome.end_repeat === "on_date" && (
+                <input type="date" name="end_date" value={newIncome.end_date} onChange={(e) => handleChange(e, setNewIncome)} />
+              )}
+            </div>
+          </div>
+        )}
+
+        <button type="submit" className="add-income-btn">Add Income</button>
+      </form>
+
+      {/* Filters */}
+      <div className="filter-toggle">
+        <button onClick={() => setShowFilters((prev) => !prev)}>
+          {showFilters ? "Hide Filters" : "Show Filters"}
+        </button>
+      </div>
+      {showFilters && (
+        <div className="filter-dropdown">
+        {/* First Column: Source*/}
+        <div className="filter-column">
+          <label htmlFor="source">Source</label>
+          <select
+            name="source"
+            value={filters.source}
+            //value={newIncome.source}
+            onChange={(e) => setFilters({ ...filters, source: e.target.value })}
+            required
+          >
+            <option value="">All Sources</option>
+            {sources.map((src) => (
+              <option key={src} value={src}> 
+                {src}
+              </option>
+            ))}
+          </select>
+        </div>
+
+        {/* Second Column: Min/Max Amount */}
+        <div className="filter-column">
+          <label htmlFor="minAmount">Min Amount</label>
+          <input
+            id="minAmount"
+            type="number"
+            placeholder="Min Amount"
+            value={filters.minAmount}
+            onChange={(e) => setFilters({ ...filters, minAmount: e.target.value })}
+            step="0.01"
+          />
+
+          <label htmlFor="maxAmount">Max Amount</label>
+          <input
+            id="maxAmount"
+            type="number"
+            placeholder="Max Amount"
+            value={filters.maxAmount}
+            onChange={(e) => setFilters({ ...filters, maxAmount: e.target.value })}
+            step="0.01"
+          />
+        </div>
+
+        {/* Third Column: Start/End Date */}
+        <div className="filter-column">
+          <label htmlFor="startDate">Start Date (dd/mm/yyyy)</label>
+          <input
+            id="startDate"
+            type="date"
+            value={filters.startDate}
+            onChange={(e) => setFilters({ ...filters, startDate: e.target.value })}
+          />
+
+          <label htmlFor="end_date">End Date (dd/mm/yyyy)</label>
+          <input
+            id="end_date"
+            type="date"
+            value={filters.end_date}
+            onChange={(e) => setFilters({ ...filters, end_date: e.target.value })}
+          />
+        </div>
+        {/* Apply Filters Button (Centered) */}
+        <div className="apply-filters-container">
+          <button className="apply-filters-btn" onClick={applyFilters}>
+            Apply Filters
+          </button>
+        </div>
+      </div>
+    )}
+
+
+      {/* Income Table */}
+      <table className="income-table">
+        <thead>
+          <tr>
+            <th onClick={() => sortData("date")}>Date</th>
+            <th onClick={() => sortData("source")}>Source</th>
+            <th onClick={() => sortData("amount")}>Amount (£)</th>
+            <th>Description</th>
+            <th>Recurring</th>
+            <th>Actions</th>
+          </tr>
+        </thead>
+        <tbody>
+          {currentIncomes.map((income) => (
+            <tr key={income.id}>
+              <td>{income.date}</td>
+              <td>{income.source}</td>
+              <td>£{parseFloat(income.amount).toFixed(2)}</td>
+              <td>{income.description}</td>
+              <td>{income.recurring ? "Yes" : "No"}</td>
+              <td>
+                <button className="edit-btn" onClick={() => handleEdit(income)}>Edit</button>
+                <button className="delete-btn" onClick={() => handleDelete(income.id)}>Delete</button>
+              </td>
+            </tr>
+          ))}
+        </tbody>
+      </table>
+
+      {/* Pagination */}
+      <ReactPaginate
+          previousLabel={"<< Previous"}
+          nextLabel={"Next >>"}
+          breakLabel={"..."}
+          pageCount={Math.max(1, Math.ceil(filteredIncomes.length / incomesPerPage))}
+          marginPagesDisplayed={2}
+          pageRangeDisplayed={5}
+          onPageChange={handlePageClick}
+          containerClassName={"pagination"}
+          activeClassName={"active"}
+        />
+
+      {/* Charts */}
+      <div className="income-summary">
+        <h2>Income Summary</h2>
+        <p>This section helps you visualize your income streams across time and sources.</p>
+      </div>
+
+      <div className="spending-distribution">
+        <h3>Income Distribution</h3>
+        <p>
+          This chart displays the distribution of your income across various sources. Depending on the selected
+          chart type, you can view this data in different ways.
+        </p>
+
+        {/* Chart Type Selector for Bar/Pie Chart */}
+        <div className="chart-type-selector">
+          <label htmlFor="chartType">Select Chart Type: </label>
+          <select value={chartType} onChange={(e) => setChartType(e.target.value)}>
+            <option value="bar">Bar Chart</option>
+            <option value="pie">Pie Chart</option>
+          </select>
+        </div>
+        
+        <div className="chart-item">
+          <IncomeChart type={chartType} incomes={incomes} />
+        </div>
+      </div>
+
+      <div className="spending-trend">
+        <h3>Income Trend</h3>
+        <p>
+            This section provides a detailed view of your income trends over different time periods—daily, weekly,
+            monthly, or yearly. By visualizing your income across various sources over these time ranges, you 
+            can easily identify patterns, fluctuations, and long-term trends in your earning. These insights 
+            empower you to make smarter financial decisions, manage your budget effectively, and track how 
+            your income evolves over time.
+          </p>
+
+        {/* Time Range Selector for the Line Chart */}
+        <div className="time-range-selector">
+          <label htmlFor="timeRange">Select Time Range: </label>
+        <select value={timeRange} onChange={(e) => setTimeRange(e.target.value)}>
+            <option value="daily">Days</option>
+            <option value="weekly">Weeks</option>
+            <option value="monthly">Months</option>
+            <option value="yearly">Years</option>
+          </select>
+        </div>
+        <div className="chart-item">
+          <IncomeChart type="line" timeRange={timeRange} incomes={incomes} />
+        </div>
+      </div>
+      {isModalOpen && (
+        <div className={`modal-overlay ${isModalOpen ? "show" : ""}`}>
+          <div className="modal-content">
+            <h3>Edit Income</h3>
+            <form onSubmit={(e) => handleSaveEdit(e, editIncome.id)} className="income-form">
+              <div className="income-row">
+                <div className="input-group">
+                  <label>Source</label>
+                  <input type="text" name="source" value={editIncome.source} onChange={(e) => handleChange(e, setEditIncome)} required />
+                </div>
+                <div className="input-group">
+                  <label>Amount</label>
+                  <input type="number" name="amount" step="0.01" value={editIncome.amount} onChange={(e) => handleChange(e, setEditIncome)} required />
+                </div>
+              </div>
+              <div className="income-row">
+                <div className="input-group">
+                  <label>Date</label>
+                  <input type="date" name="date" value={editIncome.date} onChange={(e) => handleChange(e, setEditIncome)} required />
+                </div>
+              
+                <div className="input-group">
+                  <label>Description</label>
+                  <input type="text" name="description" value={editIncome.description} onChange={(e) => handleChange(e, setEditIncome)} />
+                </div>
+              </div>
+                
+
+                <div className="checkbox-container">
+                  <div className="checkbox-label">
+                    <input
+                      type="checkbox"
+                      name="recurring"
+                      checked={editIncome.recurring}
+                      onChange={(e) => handleChange(e, setEditIncome)}
+                      />
+                      <label htmlFor="recurring">Recurring Income</label>
+                  </div>
+                </div>
+
+                {editIncome.recurring && (
+                  <div className="recurring-options">
+                    <div className="recurring-left">
+                      <label>Recurring Type</label>
+                      <select name="recurring_type" value={editIncome.recurring_type} onChange={(e) => handleChange(e, setEditIncome)}>
+                        <option value="">Select...</option>
+                        <option value="daily">Daily</option>
+                        <option value="weekly">Weekly</option>
+                        <option value="every_two_weeks">Every Two Weeks</option>
+                        <option value="monthly">Monthly</option>
+                        <option value="yearly">Yearly</option>
+                        <option value="custom">Custom</option>
+                      </select>
+
+                      {editIncome.recurring_type === "custom" && (
+                        <>
+                        
+                          <label>Frequency</label>
+                          <select name="frequency" value={editIncome.frequency} onChange={(e) => handleChange(e, setEditIncome)}>
+                            <option value="">Select...</option>
+                            <option value="daily">Daily</option>
+                            <option value="weekly">Weekly</option>
+                            <option value="monthly">Monthly</option>
+                            <option value="yearly">Yearly</option>
+                          </select>
+                          <label>Interval</label>
+                          <input type="number" name="interval" value={editIncome.interval} onChange={(e) => handleChange(e, setEditIncome)} />
+                        </>
+                      )}
+                    </div>
+
+                    <div className="recurring-right">
+                      <label>End Repeat</label>
+                      <select name="end_repeat" value={editIncome.end_repeat} onChange={(e) => handleChange(e, setEditIncome)}>
+                        <option value="never">Never</option>
+                        <option value="on_date">On Date</option>
+                      </select>
+
+                      {editIncome.end_repeat === "on_date" && (
+                        <input type="date" name="end_date" value={editIncome.end_date} onChange={(e) => handleChange(e, setEditIncome)} />
+                      )}
+                    </div>
+                  </div>
+                )}
+              <button type="submit" className="add-income-btn">Save Changes</button>
+              <button type="button" className="close-modal-btn" onClick={closeModal}>Cancel</button>
+            </form>
+          </div>
+        </div>
+      )}
+
+    </div>
+  );
+};
+
+export default Incomes;
+
+
+
diff --git a/financial-tracker/frontend/src/pages/IncomeChart.js b/financial-tracker/frontend/src/pages/IncomeChart.js
new file mode 100644
index 0000000000000000000000000000000000000000..83fc4a3e9732a05a6e9ae6e6b449d7c0cfbe0e99
--- /dev/null
+++ b/financial-tracker/frontend/src/pages/IncomeChart.js
@@ -0,0 +1,218 @@
+import React, { useEffect, useState } from "react";
+import { Pie, Bar, Line } from "react-chartjs-2";
+import {
+  Chart,
+  ArcElement,
+  Tooltip,
+  Legend,
+  CategoryScale,
+  LinearScale,
+  BarElement,
+  PointElement,
+  LineElement,
+  LineController,
+} from "chart.js";
+import "../styles/IncomeChart.css";
+
+Chart.register(
+  ArcElement,
+  Tooltip,
+  Legend,
+  CategoryScale,
+  LinearScale,
+  BarElement,
+  PointElement,
+  LineElement,
+  LineController
+);
+
+const IncomeChart = ({ incomes, type, timeRange }) => {
+  const [sourceTotals, setSourceTotals] = useState({});
+  const [timeSeriesData, setTimeSeriesData] = useState({});
+
+  const colorPalette = [
+    "#36A2EB", "#FF6384", "#FFCE56", "#4CAF50", "#FF9800", "#9C27B0",
+    "#FF5733", "#33FF57", "#3357FF", "#FF33A2", "#A233FF", "#57FF33",
+    "#FFD700", "#800080", "#00BFFF", "#00FF00", "#FF1493", "#8A2BE2",
+    "#FF6347", "#7CFC00", "#FF4500", "#32CD32", "#ADFF2F", "#FF8C00",
+  ];
+
+  const groupIncomesByTime = (incomes, range) => {
+    const grouped = {};
+
+    incomes.forEach((income) => {
+      const date = new Date(income.date);
+      let timeKey;
+
+      if (range === "daily") {
+        timeKey = date.toISOString().split("T")[0];
+      } else if (range === "weekly") {
+        const weekNumber = getWeekNumber(date);
+        timeKey = `${date.getFullYear()}-W${weekNumber}`;
+      } else if (range === "monthly") {
+        timeKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`;
+      } else if (range === "yearly") {
+        timeKey = `${date.getFullYear()}`;
+      }
+
+      if (!grouped[income.source]) {
+        grouped[income.source] = {};
+      }
+      grouped[income.source][timeKey] =
+        (grouped[income.source][timeKey] || 0) + income.amount;
+    });
+
+    return grouped;
+  };
+
+  const getWeekNumber = (date) => {
+    const startOfYear = new Date(date.getFullYear(), 0, 1);
+    const pastDays = Math.floor((date - startOfYear) / (24 * 60 * 60 * 1000));
+    return Math.ceil((pastDays + startOfYear.getDay() + 1) / 7);
+  };
+
+  useEffect(() => {
+    if (incomes.length > 0) {
+      const totals = incomes.reduce((acc, income) => {
+        acc[income.source] = (acc[income.source] || 0) + income.amount;
+        return acc;
+      }, {});
+      setSourceTotals(totals);
+
+      const groupedData = groupIncomesByTime(incomes, timeRange);
+      setTimeSeriesData(groupedData);
+    }
+  }, [incomes, timeRange]);
+
+  const dataColors = Object.keys(sourceTotals).map((_, index) => {
+    return colorPalette[index % colorPalette.length];
+  });
+
+  const pieData = {
+    labels: Object.keys(sourceTotals),
+    datasets: [
+      {
+        label: "Income by Source",
+        data: Object.values(sourceTotals),
+        backgroundColor: dataColors,
+      },
+    ],
+  };
+
+  const barData = {
+    labels: Object.keys(sourceTotals),
+    datasets: [
+      {
+        label: "Income Amount",
+        data: Object.values(sourceTotals),
+        backgroundColor: dataColors,
+      },
+    ],
+  };
+
+  const timeLabels = [
+    ...new Set(
+      Object.values(timeSeriesData).flatMap((data) => Object.keys(data))
+    ),
+  ].sort();
+
+  const lineDatasets = Object.keys(timeSeriesData).map((source, index) => ({
+    label: source,
+    data: timeLabels.map((label) => timeSeriesData[source]?.[label] || 0),
+    borderColor: colorPalette[index % colorPalette.length],
+    backgroundColor: colorPalette[index % colorPalette.length],
+    fill: false,
+    tension: 0.1,
+  }));
+
+  const lineData = {
+    labels: timeLabels,
+    datasets: lineDatasets,
+  };
+
+  const options = {
+    responsive: true,
+    maintainAspectRatio: false,
+    scales: {
+      y: {
+        beginAtZero: true,
+        title: {
+          display: true,
+          text: "Amount Earned",
+        },
+      },
+      x: {
+        title: {
+          display: true,
+          text:
+            type === "line"
+              ? `Time (${timeRange.charAt(0).toUpperCase() + timeRange.slice(1)})`
+              : "Sources",
+        },
+      },
+    },
+    plugins: {
+      legend: {
+        display: false,
+      },
+      tooltip: {
+        mode: "index",
+        intersect: false,
+      },
+    },
+  };
+
+  return (
+    <div className="expense-chart-container">
+      {/* Pie Chart Section */}
+      {type === "pie" && (
+        <div className="chart-section">
+          <div className="text-container">
+            <h4>Pie Chart: Income Breakdown</h4>
+            <p>
+              This pie chart illustrates the distribution of your income sources.
+              Each slice shows how much a particular source contributes to your total income.
+            </p>
+          </div>
+          <div className="chart">
+            <Pie data={pieData} />
+          </div>
+        </div>
+      )}
+
+      {/* Bar Chart Section */}
+      {type === "bar" && (
+        <div className="chart-section">
+          <div className="text-container">
+            <h4>Bar Chart: Income by Source</h4>
+            <p>
+              This bar chart compares how much you’ve earned from different sources. 
+              It gives a clear view of your top income contributors.
+            </p>
+          </div>
+          <div className="chart">
+            <Bar data={barData} options={options} />
+          </div>
+        </div>
+      )}
+
+      {/* Line Chart Section */}
+      {type === "line" && (
+        <div className="chart-section">
+          <div className="text-container">
+            <h4>Line Chart: Income Trends Over Time</h4>
+            <p>
+              The line chart shows how your income from each source has varied over time.
+              This helps track your financial growth or identify inconsistencies.
+            </p>
+          </div>
+          <div className="chart">
+            <Line data={lineData} options={options} />
+          </div>
+        </div>
+      )}
+    </div>
+  );
+};
+
+export default IncomeChart;
diff --git a/financial-tracker/frontend/src/pages/Register.js b/financial-tracker/frontend/src/pages/Register.js
index 6c9c327de06922455d8decc3c4fcdb090416dd17..9c46a58e966eada3295331b2f481b930a32fe16f 100644
--- a/financial-tracker/frontend/src/pages/Register.js
+++ b/financial-tracker/frontend/src/pages/Register.js
@@ -1,12 +1,18 @@
-import React from "react";
+import React, { useState } from "react";
 import { Link, useNavigate } from "react-router-dom";
 import { Formik, Form, Field, ErrorMessage } from "formik";
 import * as Yup from "yup";
 import axios from "axios";
 import "../styles/Register.css";
+import { FaEye, FaEyeSlash } from "react-icons/fa";
+
 
 const Register = () => {
   const navigate = useNavigate();
+  const [password, setPassword] = useState("");
+  const [showPassword, setShowPassword] = useState(false);
+
+
 
   // Define validation schema using Yup
   const validationSchema = Yup.object().shape({
@@ -35,7 +41,7 @@ const Register = () => {
       {/* Register Form */}
       <div className="register-container">
         <h2>Register</h2>
-
+        
         <Formik
           initialValues={{ firstName: "", lastName: "", email: "", password: "" }}
           validationSchema={validationSchema}
@@ -60,30 +66,89 @@ const Register = () => {
               {status?.error && <p className="error-message">{status.error}</p>}
               {status?.success && <p className="success-message">{status.success}</p>}
 
+              
               <div className="form-group">
-                <label htmlFor="firstName">First Name:</label>
+              <label htmlFor="firstName">
+                First Name <span className="required-asterisk">*</span>
+              </label>
                 <Field type="text" name="firstName" className="form-control" />
                 <ErrorMessage name="firstName" component="div" className="error" />
               </div>
 
               <div className="form-group">
-                <label htmlFor="lastName">Last Name:</label>
+              <label htmlFor="lastName">
+                Last Name <span className="required-asterisk">*</span>
+              </label>
                 <Field type="text" name="lastName" className="form-control" />
                 <ErrorMessage name="lastName" component="div" className="error" />
               </div>
 
               <div className="form-group">
-                <label htmlFor="email">Email:</label>
+              <label htmlFor="email">
+                Email <span className="required-asterisk">*</span>
+              </label>
                 <Field type="email" name="email" className="form-control" />
                 <ErrorMessage name="email" component="div" className="error" />
               </div>
-
+              
               <div className="form-group">
-                <label htmlFor="password">Password:</label>
-                <Field type="password" name="password" className="form-control" />
-                <ErrorMessage name="password" component="div" className="error" />
-              </div>
+                <label htmlFor="password">
+                  Password <span className="required-asterisk">*</span>
+                </label>
+              <Field name="password">
+                {({ field, form }) => {
+                  // Update local password state every time the field changes
+                  const handlePasswordChange = (e) => {
+                    setPassword(e.target.value);
+                    form.setFieldValue("password", e.target.value); // Keep Formik in sync
+                  };
+
+                  return (
+                    <>
+                      <div className="password-wrapper">
+                        <input
+                          {...field}
+                          type={showPassword ? "text" : "password"}
+                          className="form-control"
+                          onChange={handlePasswordChange}
+                          placeholder="Enter your password"
+                        />
+                        <span
+                          className="toggle-password"
+                          onClick={() => setShowPassword((prev) => !prev)}
+                          title={showPassword ? "Hide password" : "Show password"}
+                        >
+                          {showPassword ? <FaEyeSlash /> : <FaEye />}
+                        </span>
+                        
+
+                      </div>
+
+                      {/* Dynamic password feedback */}
+                      <ul className="password-rules">
+                        <li className={password.length >= 8 ? "valid" : "invalid"}>
+                          {password.length >= 8 ? "✔" : "✖"} At least 8 characters
+                        </li>
+                        <li className={/[A-Z]/.test(password) ? "valid" : "invalid"}>
+                          {/[A-Z]/.test(password) ? "✔" : "✖"} One uppercase letter
+                        </li>
+                        <li className={/[a-z]/.test(password) ? "valid" : "invalid"}>
+                          {/[a-z]/.test(password) ? "✔" : "✖"} One lowercase letter
+                        </li>
+                        <li className={/\d/.test(password) ? "valid" : "invalid"}>
+                          {/\d/.test(password) ? "✔" : "✖"} One number
+                        </li>
+                        <li className={/[@$!%*?&_]/.test(password) ? "valid" : "invalid"}>
+                          {/[@$!%*?&_]/.test(password) ? "✔" : "✖"} One special character
+                        </li>
+                      </ul>
+                    </>
+                  );
+                }}
+              </Field>
+            </div>
 
+              
               <button type="submit" className="btn" disabled={isSubmitting}>
                 {isSubmitting ? "Registering..." : "Register"}
               </button>
diff --git a/financial-tracker/frontend/src/styles/Income.css b/financial-tracker/frontend/src/styles/Income.css
new file mode 100644
index 0000000000000000000000000000000000000000..f9b1f9c367c360895d52df1bc24f9b6ec822be92
--- /dev/null
+++ b/financial-tracker/frontend/src/styles/Income.css
@@ -0,0 +1,617 @@
+/* Main Container */
+.income-container {
+  width: 80%;
+  margin: auto;
+}
+
+
+/* Income font */
+h1 {
+  font-size: 2.5em;
+  text-align: center;
+}
+
+.income-summary h2 {
+  font-size: 2em;
+  margin-top: 15px;
+  font-weight: bold;
+  text-align: center;
+}
+
+
+
+.spending-distribution h3,
+.spending-trend h3 {
+  font-size: 1.5em;
+  margin-top: 15px;
+  font-weight: bold;
+  text-align: left;
+}
+
+.income-summary p {
+  font-size: 1.3em;
+  margin-top: 10px;
+  line-height: 1.5;
+  text-align: justify;
+}
+
+.spending-distribution p,
+.spending-trend p {
+  font-size: 1.1em;
+  margin-top: 10px;
+  line-height: 1.5;
+  text-align: justify;
+}
+
+/* Modal Overlay */
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 999;
+  opacity: 0;
+  pointer-events: none; 
+  transition: opacity 0.3s ease;
+}
+
+.modal-overlay.show {
+  opacity: 1;
+  pointer-events: auto; 
+}
+
+
+/* Modal Content */
+.modal-content {
+  background-color: #fff;
+  padding: 20px;
+  border-radius: 10px;
+  width: 500px;
+  max-width: 100%;
+  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+  text-align: center;
+}
+
+.modal-content .input-group label {
+  font-weight: bold;
+}
+
+.modal-content .income-row {
+  align-items: flex-start;
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  gap: 15px;
+  width: 100%;
+}
+
+.modal-content .income-row .input-group {
+  box-sizing: border-box;
+  flex: 1 1 calc(50% - 15px);
+  min-width: 200px;
+  max-width: 250px;
+  box-sizing: border-box;
+}
+
+.modal-content .input-group input,
+.modal-content .input-group select {
+  width: 80%; 
+  padding: 8px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  max-width: 250px;
+}
+
+.modal-content .custom-recurring label,
+.modal-content .recurring-left label,
+.modal-content .recurring-right label {
+  margin-top: 10px;
+  font-weight: bold;
+}
+
+.modal-content .recurring-options {
+  display: flex;
+  justify-content: space-between;
+  gap: 20px;
+  margin-top: 20px;
+  width: 100%;
+}
+  
+
+.modal-content .recurring-left,
+.modal-content .recurring-right {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+  flex: 1;
+}
+
+.modal-content .input-group {
+  margin-bottom: 15px;
+}
+
+.modal-content .custom-recurring {
+  margin-top: 10px;
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.modal-content .custom-recurring input,
+.modal-content .custom-recurring select {
+  width: 100%;
+}
+
+
+/* income Form */
+.income-form {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 10px;
+  font-weight: bold;
+  margin-bottom: 20px;
+  padding: 20px;
+  border-radius: 10px;
+  background-color: #f9f9f9;
+  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+}
+
+.income-inputs {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  gap: 10px;
+}
+
+.input-group {
+  flex: 1; 
+  min-width: calc(50% - 5px);
+}
+
+.income-inputs input,
+.income-inputs select {
+  padding: 8px;
+  font-size: 14px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  width: calc(50% - 5px);
+  box-sizing: border-box;
+}
+
+.income-inputs input[type="checkbox"] {
+  width: auto;
+}
+
+.income-row {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  width: 100%;
+}
+
+.income-row .input-group {
+  flex: 1;
+  min-width: calc(50% - 5px);
+}
+
+.income-inputs label {
+  font-weight: bold;
+}
+
+.checkbox-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 100%;
+}
+
+.checkbox-label {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 14px;
+  font-weight: bold;
+  gap: 5px;
+}
+/* Recurring Options */
+.recurring-options {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 20px;
+  width: 100%;
+  max-width: 600px;
+  background-color: #f8f8f8;
+  padding: 15px;
+  border-radius: 5px;
+  border: 1px solid #ccc;
+}
+
+.recurring-options label {
+  display: block;
+  font-size: 14px;
+  font-weight: bold;
+  margin-bottom: 5px;
+}
+
+.recurring-left,
+.recurring-right {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.recurring-options select,
+.recurring-options input {
+  width: 100%;
+  padding: 8px;
+  font-size: 14px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  background-color: #fff;
+  box-sizing: border-box;
+}
+
+.recurring-options input[type="date"],
+.recurring-options input[type="number"] {
+  appearance: none;
+  width: 100%; 
+}
+
+
+/* Custom Recurrence */
+.custom-recurring {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+/* End Repeat Section */
+.end-repeat {
+  margin-top: 10px;
+}
+
+/* Add / Submit Button */
+.add-income-btn {
+  padding: 10px 15px;
+  font-size: 14px;
+  background-color: #007bff;
+  color: white;
+  border: none;
+  border-radius: 5px;
+  cursor: pointer;
+  margin-top: 20px;
+}
+
+.add-income-btn:hover {
+  background-color: #0056b3;
+}
+
+/* Close Button */
+.close-modal-btn {
+  padding: 10px 15px;
+  font-size: 14px;
+  background-color: #dc3545;
+  color: white;
+  border: none;
+  border-radius: 5px;
+  cursor: pointer;
+  margin-top: 10px;
+}
+
+.close-modal-btn:hover {
+  background-color: #c82333;
+}
+
+/* income Table */
+.income-table {
+  width: 100%;
+  max-width: 800px;
+  margin: auto;
+  border-collapse: collapse;
+  margin-top: 20px;
+}
+
+th.sortable {
+  cursor: pointer;
+}
+
+.income-table th,
+.income-table td {
+  border: 1px solid #ddd;
+  padding: 10px;
+  text-align: center;
+}
+
+.income-table th {
+  background-color: #f4f4f4;
+}
+
+.income-table tr:nth-child(even) {
+  background-color: #f9f9f9;
+}
+
+.income-table tr:hover {
+  background-color: #f1f1f1;
+}
+
+/* Action Buttons */
+.income-table button {
+  padding: 5px 10px;
+  font-size: 12px;
+  border: none;
+  cursor: pointer;
+  margin: 2px;
+  border-radius: 3px;
+}
+
+.income-table button:first-child {
+  background-color: #ffc107;
+  color: black;
+}
+
+.income-table button:last-child {
+  background-color: #dc3545;
+  color: white;
+}
+
+/* Filter Dropdown */
+.filter-dropdown {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  gap: 20px;
+  align-items: flex-start;
+  margin-top: 20px;
+  max-width: 600px;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+/* Each column */
+.filter-column {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+  flex: 1;
+  min-width: 180px; 
+}
+
+/* Input and select styling */
+.filter-column label {
+  font-size: 14px;
+  font-weight: bold;
+  margin-bottom: 5px;
+}
+
+.filter-column select,
+.filter-column input {
+  width: 100%;
+  padding: 8px;
+  font-size: 14px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  box-sizing: border-box;
+  background-color: #f8f8f8;
+}
+
+.filter-column select {
+  cursor: pointer;
+}
+
+/* Apply Filters Button */
+.apply-filters-container {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+}
+
+/* Apply Filters Button */
+.apply-filters-btn {
+  width: 100%;
+  max-width: 600px; 
+  padding: 12px 20px;
+  background-color: #28a745;
+  color: white;
+  border: none;
+  border-radius: 5px;
+  cursor: pointer;
+  font-size: 14px;
+  margin-top: 15px;
+  transition: background-color 0.3s;
+}
+
+.apply-filters-btn:hover {
+  background-color: #218838;
+}
+
+/* Responsive: Stack on small screens */
+@media (max-width: 600px) {
+  .income-inputs {
+    flex-direction: column;
+    width: 100%;
+  }
+
+  .income-inputs input,
+  .income-inputs select {
+    width: 100%;
+  }
+
+  .income-table {
+    font-size: 12px;
+  }
+
+  .income-table th,
+  .income-table td {
+    padding: 8px;
+  }
+
+  .filter-dropdown {
+    flex-direction: column;
+    align-items: center;
+  }
+
+  .filter-column {
+    width: 100%;
+  }
+
+  .income-inputs {
+    grid-template-columns: 1fr;
+  }
+
+  .recurring-options {
+    grid-template-columns: 1fr;
+  }
+
+  .chart-type-selector,
+  .time-range-selector {
+    flex-direction: column;
+    align-items: flex-start;
+  }
+
+  .chart-type-selector select,
+  .time-range-selector select {
+    width: 100%;
+  }
+}
+
+.delete-message {
+  padding: 10px;
+  margin-top: 10px;
+  background-color: #f44336;
+  color: white;
+  border-radius: 5px;
+  text-align: center;
+}
+
+.success-message {
+  padding: 10px;
+  margin-top: 10px;
+  background-color: #4CAF50;
+  color: white;
+  border-radius: 5px;
+  text-align: center;
+}
+
+/* Pagination */
+.pagination {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  list-style-type: none;
+  padding: 10px 0;
+  margin-top: 20px;
+}
+
+.pagination li {
+  margin: 0 10px;
+  cursor: pointer;
+  font-size: 14px;
+  padding: 8px 12px;
+  border-radius: 5px;
+  transition: background-color 0.3s ease, color 0.3s ease;
+}
+
+.pagination li:hover {
+  background-color: #007bff;
+  color: white;
+}
+
+.pagination .active {
+  background-color: #007bff;
+  color: white;
+  font-weight: bold;
+  border-radius: 5px;
+  padding: 8px 12px;
+}
+
+.pagination li.disabled {
+  cursor: not-allowed;
+  opacity: 0.5;
+}
+
+.pagination .previous, .pagination .next {
+  font-weight: bold;
+}
+
+/* Adding styles for Previous and Next buttons */
+.pagination .previous,
+.pagination .next {
+  padding: 8px 12px;
+  background-color: #007bff;
+  color: white;
+  border-radius: 5px;
+  font-size: 14px;
+  cursor: pointer;
+  transition: background-color 0.3s ease;
+}
+
+.pagination .previous:hover,
+.pagination .next:hover {
+  background-color: #0056b3;
+}
+
+
+/* Chart Type Selector */
+.chart-type-selector {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  margin-top: 20px;
+  margin-bottom: 20px;
+  font-size: 16px;
+}
+
+.chart-type-selector label {
+  font-weight: bold;
+}
+
+.chart-type-selector select {
+  padding: 8px;
+  font-size: 14px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  background-color: #f8f8f8;
+  cursor: pointer;
+  transition: border-color 0.3s;
+}
+
+.chart-type-selector select:focus {
+  border-color: #007bff;
+  outline: none;
+}
+
+/* Time Range Selector */
+.time-range-selector {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  margin-bottom: 20px;
+  font-size: 16px;
+}
+
+.time-range-selector label {
+  font-weight: bold;
+}
+
+.time-range-selector select {
+  padding: 8px;
+  font-size: 14px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  background-color: #f8f8f8;
+  cursor: pointer;
+  transition: border-color 0.3s;
+}
+
+.time-range-selector select:focus {
+  border-color: #007bff;
+  outline: none;
+}
+
+
diff --git a/financial-tracker/frontend/src/styles/IncomeChart.css b/financial-tracker/frontend/src/styles/IncomeChart.css
new file mode 100644
index 0000000000000000000000000000000000000000..d808affb8ad33154cc34ee38e39f0d3e1af59e82
--- /dev/null
+++ b/financial-tracker/frontend/src/styles/IncomeChart.css
@@ -0,0 +1,74 @@
+.charts-container {
+    display: flex;
+    gap: 20px;
+    justify-content: center;
+    margin-top: 20px;
+  }
+  
+  .chart-item {
+    flex: 1;
+    max-width: 100%;
+  }
+  
+  .chart canvas {
+    width: 100% !important;
+    height: 100% !important;
+    min-height: 350px !important;
+  }
+  
+  .income-chart-container {
+    margin: 20px;
+  }
+  
+  .chart-section {
+    margin-bottom: 30px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    height: auto;
+    min-height: 450px;
+  }
+  
+  .chart-section .text-container {
+    width: 100%; 
+    max-width: 800px; 
+    text-align: left;
+  }
+  
+  .chart-section h4 {
+    margin-top: 15px;
+    font-size: 1.2em;
+    font-weight: bold;
+    text-align: center;
+  }
+  
+  .chart-section p {
+    font-size: 1em;
+    margin-top: 10px;
+    line-height: 1.5;
+    text-align: center !important;
+  }
+  
+  .chart {
+    margin-top: 20px;
+    height: 400px !important;
+    min-height: 400px;
+    max-height: 500px;
+    position: relative; 
+  }
+  
+  .line-chart-container {
+    margin-top: 30px;
+    padding: 15px;
+    background-color: #f1f1f1;
+    border-radius: 10px;
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
+  }
+  
+  .line-chart-container h3 {
+    font-size: 1.5rem;
+    margin-bottom: 15px;
+    color: #333;
+  }
+  
\ No newline at end of file
diff --git a/financial-tracker/frontend/src/styles/Register.css b/financial-tracker/frontend/src/styles/Register.css
index 3f7ccaed2a6685e8d478d6b4bb938d4f38f038c7..34fa00a4ad1cb9b96c010065a9e313f0bdde767f 100644
--- a/financial-tracker/frontend/src/styles/Register.css
+++ b/financial-tracker/frontend/src/styles/Register.css
@@ -97,3 +97,35 @@ button:hover {
 .login-link a:hover {
   text-decoration: underline;
 }
+
+.password-wrapper {
+  position: relative;
+  display: flex;
+  align-items: center;
+}
+
+.password-wrapper input {
+  width: 100%;
+  padding-right: 2.5rem;
+}
+
+.toggle-password {
+  position: absolute;
+  right: 10px;
+  top: 50%;
+  transform: translateY(-50%);
+  font-size: 1.2rem;
+  color: rgba(0, 0, 0, 0.3);
+  cursor: pointer;
+  transition: color 0.2s;
+}
+
+.toggle-password:hover {
+  color: rgba(0, 0, 0, 0.6);
+}
+
+.required-asterisk {
+  color: red;
+  margin-left: 0.2rem;
+}
+
diff --git a/financial-tracker/package-lock.json b/financial-tracker/package-lock.json
new file mode 100644
index 0000000000000000000000000000000000000000..bc227805b354553200e0a7bc37123691ce8c2f08
--- /dev/null
+++ b/financial-tracker/package-lock.json
@@ -0,0 +1,378 @@
+{
+  "name": "financial-tracker",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "bootstrap": "^5.3.5",
+        "react-bootstrap": "^2.10.9"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.27.0",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
+      "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
+      "license": "MIT",
+      "dependencies": {
+        "regenerator-runtime": "^0.14.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@popperjs/core": {
+      "version": "2.11.8",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+      "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/popperjs"
+      }
+    },
+    "node_modules/@react-aria/ssr": {
+      "version": "3.9.7",
+      "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz",
+      "integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@swc/helpers": "^0.5.0"
+      },
+      "engines": {
+        "node": ">= 12"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+      }
+    },
+    "node_modules/@restart/hooks": {
+      "version": "0.4.16",
+      "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
+      "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==",
+      "license": "MIT",
+      "dependencies": {
+        "dequal": "^2.0.3"
+      },
+      "peerDependencies": {
+        "react": ">=16.8.0"
+      }
+    },
+    "node_modules/@restart/ui": {
+      "version": "1.9.4",
+      "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.4.tgz",
+      "integrity": "sha512-N4C7haUc3vn4LTwVUPlkJN8Ach/+yIMvRuTVIhjilNHqegY60SGLrzud6errOMNJwSnmYFnt1J0H/k8FE3A4KA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.26.0",
+        "@popperjs/core": "^2.11.8",
+        "@react-aria/ssr": "^3.5.0",
+        "@restart/hooks": "^0.5.0",
+        "@types/warning": "^3.0.3",
+        "dequal": "^2.0.3",
+        "dom-helpers": "^5.2.0",
+        "uncontrollable": "^8.0.4",
+        "warning": "^4.0.3"
+      },
+      "peerDependencies": {
+        "react": ">=16.14.0",
+        "react-dom": ">=16.14.0"
+      }
+    },
+    "node_modules/@restart/ui/node_modules/@restart/hooks": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.5.1.tgz",
+      "integrity": "sha512-EMoH04NHS1pbn07iLTjIjgttuqb7qu4+/EyhAx27MHpoENcB2ZdSsLTNxmKD+WEPnZigo62Qc8zjGnNxoSE/5Q==",
+      "license": "MIT",
+      "dependencies": {
+        "dequal": "^2.0.3"
+      },
+      "peerDependencies": {
+        "react": ">=16.8.0"
+      }
+    },
+    "node_modules/@restart/ui/node_modules/uncontrollable": {
+      "version": "8.0.4",
+      "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz",
+      "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "react": ">=16.14.0"
+      }
+    },
+    "node_modules/@swc/helpers": {
+      "version": "0.5.15",
+      "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+      "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.8.0"
+      }
+    },
+    "node_modules/@types/prop-types": {
+      "version": "15.7.14",
+      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
+      "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/react": {
+      "version": "19.1.0",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz",
+      "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==",
+      "license": "MIT",
+      "dependencies": {
+        "csstype": "^3.0.2"
+      }
+    },
+    "node_modules/@types/react-transition-group": {
+      "version": "4.4.12",
+      "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
+      "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@types/react": "*"
+      }
+    },
+    "node_modules/@types/warning": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
+      "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==",
+      "license": "MIT"
+    },
+    "node_modules/bootstrap": {
+      "version": "5.3.5",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.5.tgz",
+      "integrity": "sha512-ct1CHKtiobRimyGzmsSldEtM03E8fcEX4Tb3dGXz1V8faRwM50+vfHwTzOxB3IlKO7m+9vTH3s/3C6T2EAPeTA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/twbs"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/bootstrap"
+        }
+      ],
+      "license": "MIT",
+      "peerDependencies": {
+        "@popperjs/core": "^2.11.8"
+      }
+    },
+    "node_modules/classnames": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+      "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
+      "license": "MIT"
+    },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+      "license": "MIT"
+    },
+    "node_modules/dequal": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+      "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/dom-helpers": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+      "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.8.7",
+        "csstype": "^3.0.2"
+      }
+    },
+    "node_modules/invariant": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.0.0"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "license": "MIT"
+    },
+    "node_modules/loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "license": "MIT",
+      "dependencies": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "loose-envify": "cli.js"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/prop-types": {
+      "version": "15.8.1",
+      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+      "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.4.0",
+        "object-assign": "^4.1.1",
+        "react-is": "^16.13.1"
+      }
+    },
+    "node_modules/prop-types-extra": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz",
+      "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==",
+      "license": "MIT",
+      "dependencies": {
+        "react-is": "^16.3.2",
+        "warning": "^4.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=0.14.0"
+      }
+    },
+    "node_modules/react": {
+      "version": "19.1.0",
+      "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
+      "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/react-bootstrap": {
+      "version": "2.10.9",
+      "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.9.tgz",
+      "integrity": "sha512-TJUCuHcxdgYpOqeWmRApM/Dy0+hVsxNRFvq2aRFQuxhNi/+ivOxC5OdWIeHS3agxvzJ4Ev4nDw2ZdBl9ymd/JQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.24.7",
+        "@restart/hooks": "^0.4.9",
+        "@restart/ui": "^1.9.4",
+        "@types/prop-types": "^15.7.12",
+        "@types/react-transition-group": "^4.4.6",
+        "classnames": "^2.3.2",
+        "dom-helpers": "^5.2.1",
+        "invariant": "^2.2.4",
+        "prop-types": "^15.8.1",
+        "prop-types-extra": "^1.1.0",
+        "react-transition-group": "^4.4.5",
+        "uncontrollable": "^7.2.1",
+        "warning": "^4.0.3"
+      },
+      "peerDependencies": {
+        "@types/react": ">=16.14.8",
+        "react": ">=16.14.0",
+        "react-dom": ">=16.14.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/react-dom": {
+      "version": "19.1.0",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
+      "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "scheduler": "^0.26.0"
+      },
+      "peerDependencies": {
+        "react": "^19.1.0"
+      }
+    },
+    "node_modules/react-is": {
+      "version": "16.13.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+      "license": "MIT"
+    },
+    "node_modules/react-lifecycles-compat": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+      "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
+      "license": "MIT"
+    },
+    "node_modules/react-transition-group": {
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+      "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@babel/runtime": "^7.5.5",
+        "dom-helpers": "^5.0.1",
+        "loose-envify": "^1.4.0",
+        "prop-types": "^15.6.2"
+      },
+      "peerDependencies": {
+        "react": ">=16.6.0",
+        "react-dom": ">=16.6.0"
+      }
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+      "license": "MIT"
+    },
+    "node_modules/scheduler": {
+      "version": "0.26.0",
+      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
+      "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "license": "0BSD"
+    },
+    "node_modules/uncontrollable": {
+      "version": "7.2.1",
+      "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
+      "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.6.3",
+        "@types/react": ">=16.9.11",
+        "invariant": "^2.2.4",
+        "react-lifecycles-compat": "^3.0.4"
+      },
+      "peerDependencies": {
+        "react": ">=15.0.0"
+      }
+    },
+    "node_modules/warning": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+      "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.0.0"
+      }
+    }
+  }
+}
diff --git a/financial-tracker/package.json b/financial-tracker/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..72a87f2d71eefe128a39a311e2e4f714ecdd9faa
--- /dev/null
+++ b/financial-tracker/package.json
@@ -0,0 +1,6 @@
+{
+  "dependencies": {
+    "bootstrap": "^5.3.5",
+    "react-bootstrap": "^2.10.9"
+  }
+}
diff --git a/financial-tracker/requirements.txt b/financial-tracker/requirements.txt
index b865926eae7539ee6a8aa5e8a4b5604f38cb7bdf..b8d9b5e4abe655328aa8a22037c9704581d8fc06 100644
Binary files a/financial-tracker/requirements.txt and b/financial-tracker/requirements.txt differ
diff --git a/financial-tracker/transaction-service/Dockerfile b/financial-tracker/transaction-service/Dockerfile
index 1c043517201d3b681be99761e927181f217cb741..e9e7b574dd32be04b6e02f5de089e86e5cef09e4 100644
--- a/financial-tracker/transaction-service/Dockerfile
+++ b/financial-tracker/transaction-service/Dockerfile
@@ -10,9 +10,11 @@ COPY requirements.txt .
 RUN pip install --no-cache-dir -r requirements.txt
 
 # Copy only the current service's code
-COPY ./transaction-service/app /app
+COPY ./transaction-service/app/app.py ./app.py
 
 # Expose the service port
 EXPOSE 5002
 
 CMD ["python", "app.py"]
+CMD ["sh", "-c", "echo Starting Transaction Service && ls -l && python app.py"]
+
diff --git a/financial-tracker/transaction-service/app/app.py b/financial-tracker/transaction-service/app/app.py
index c78d6bda411483a425d1ed2fa1e0c740cf4a8fea..beaadaac746eb0f7848604975aa8378f09ec7ab5 100644
--- a/financial-tracker/transaction-service/app/app.py
+++ b/financial-tracker/transaction-service/app/app.py
@@ -1,3 +1,5 @@
+from flask import Flask, jsonify, request
+from datetime import datetime
 from datetime import datetime, timedelta
 from dateutil.relativedelta import relativedelta
 from flask import Flask, request, jsonify
@@ -169,10 +171,308 @@ class Expense(db.Model):
             "end_date": self.end_date.strftime('%Y-%m-%d') if self.end_date else None
         }
 
+class Income(db.Model):
+    id = db.Column(db.Integer, primary_key=True)
+    user_id = db.Column(db.Integer, nullable=False)
+    amount = db.Column(db.Float, nullable=False)
+    currency = db.Column(db.Enum(CurrencyEnum), nullable=False)
+    source = db.Column(db.String(100), nullable=False)
+    date = db.Column(db.Date, nullable=False)
+    description = db.Column(db.String(255))
+
+    #recurring fields 
+    recurring = db.Column(db.Boolean, default=False, nullable=False)
+    recurring_type = db.Column(db.Enum(RecurringTypeEnum), nullable=True)
+    frequency = db.Column(db.Enum(FrequencyEnum), nullable=True)
+    interval = db.Column(db.Integer, nullable=True)
+    end_repeat = db.Column(db.Enum(EndRepeatEnum), nullable=True)
+    end_date = db.Column(db.Date, nullable=True)
+
+    def to_dict(self):
+        return {
+            "id": self.id,
+            "user_id": self.user_id,
+            "amount": self.amount,
+            "currency": self.currency.value,  # Convert Enum to its string value
+            "source": self.source,
+            "date": self.date.strftime('%Y-%m-%d'),
+            "description": self.description,
+            "recurring": self.recurring,
+            "recurring_type": self.recurring_type.value if self.recurring_type else None,
+            "frequency": self.frequency.value if self.frequency else None,
+            "interval": self.interval,
+            "end_repeat": self.end_repeat.value if self.end_repeat else None,
+            "end_date": self.end_date.strftime('%Y-%m-%d') if self.end_date else None
+        }
+
+
+#INCOMES 
+@app.route('/income', methods=['POST'])
+@jwt_required()
+def add_income():
+    """Add a new income for the logged-in user"""
+    data = request.get_json()
+    app.logger.info(f"Received income data: {data}")  
+
+    required_fields = ["amount", "currency", "source", "date", "description", "recurring"]
+    missing_fields = [field for field in required_fields if field not in data]
+
+    if missing_fields:
+        return jsonify({"error": f"Missing fields: {', '.join(missing_fields)}"}), 400
+
+    try:
+        user_id = int(get_jwt_identity())
+        if not user_id:
+            app.logger.error("User ID is missing or invalid.")
+            return jsonify({"error": "User authentication failed"}), 401
+        try:
+            income_date = datetime.strptime(data["date"], '%Y-%m-%d').date()
+            if income_date > datetime.today().date():
+                app.logger.error(f"Invalid date: {income_date}. Future incomes are not allowed.")
+                return jsonify({"error": "Income date cannot be in the future"}), 400
+        except ValueError:
+            app.logger.error(f"Invalid date format: {data['date']}")
+            return jsonify({"error": "Invalid date format. Use YYYY-MM-DD"}), 400
+
+
+        # Convert optional enums
+        recurring_type = data.get("recurring_type") or None
+        frequency = data.get("frequency") or None
+        interval = data.get("interval") or None
+        interval = int(interval) if interval not in [None, ""] else None
+        end_repeat = data.get("end_repeat") or None
+        end_date = data.get("end_date") or None 
+
+        # Convert end_date to a Date object if provided
+        if end_date:
+            try:
+                end_date = datetime.strptime(end_date, '%Y-%m-%d').date()
+                if end_date < income_date:
+                    app.logger.error("End date cannot be before the expense date.")
+                    return jsonify({"error": "End date must be after the expense date"}), 400
+            except ValueError:
+                app.logger.error(f"Invalid end_date format: {end_date}")
+                return jsonify({"error": "Invalid end_date format. Use YYYY-MM-DD"}), 400
+
+    
+        if data["recurring"] and recurring_type == "custom" and (not frequency or not interval):
+            return jsonify({"error": "Custom recurrence requires frequency and interval"}), 400
+
+        new_income = Income(
+            user_id=user_id,
+            amount=data["amount"],
+            currency=CurrencyEnum[data["currency"]],
+            source=data["source"],
+            date=income_date,
+            description=data["description"],
+            recurring=data["recurring"],
+            recurring_type=RecurringTypeEnum(recurring_type) if recurring_type else None,
+            frequency=FrequencyEnum(frequency) if frequency else None,
+            interval=interval,
+            end_repeat=EndRepeatEnum(end_repeat) if end_repeat else None,
+            end_date=end_date
+        )
+
+        db.session.add(new_income)
+        db.session.commit()
+
+        return jsonify({"message": "Income added", "income": new_income.to_dict()}), 201
+
+    except Exception as e:
+        app.logger.error(f"Error adding income: {str(e)}", exc_info=True)
+        return jsonify({"error": "An error occurred while adding income"}), 500
+    
+@app.route('/income', methods=['GET'])
+@jwt_required()
+def get_income():
+    """Retrieve all income for the logged-in user"""
+    try:
+        user_id = int(get_jwt_identity())
+        app.logger.info(f"Retrieved user_id from JWT: {user_id}")
+
+        if not user_id:
+            app.logger.error("User ID is None or invalid.")
+            return jsonify({"error": "Invalid user authentication"}), 401
+        
+        incomes = Income.query.filter_by(user_id=user_id).all()
+        app.logger.info(f"Found {len(incomes)} incomes for user_id {user_id}")
+
+        income_list = [income.to_dict() for income in incomes]
+        return jsonify({"incomes": income_list}), 200
+    except Exception as e:
+        app.logger.error(f"Error fetching incomes: {str(e)}", exc_info=True)
+        return jsonify({"error": "Failed to retrieve incomes"}), 500
+
+
+@app.route('/income/<int:income_id>', methods=['PUT'])
+@jwt_required()
+def edit_income(income_id):
+    """Edit an income by ID for the logged-in user"""
+    try:
+        user_id = int(get_jwt_identity())
+        app.logger.info(f"User ID: {user_id}")
+
+        if not user_id:
+            app.logger.error("User ID is missing or invalid.")
+            return jsonify({"error": "User authentication failed"}), 401
+
+        income = Income.query.filter_by(id=income_id, user_id=user_id).first()
 
-@app.route('/transactions', methods=['GET'])
-def get_transactions():
-    return jsonify({"message": "Transaction service active"}), 200
+        if not income:
+            app.logger.error(f"Income not found for ID {income_id} and User ID {user_id}")
+            return jsonify({"error": "Income not found"}), 404
+
+        data = request.get_json()
+        app.logger.info(f"Received data: {data}") 
+
+        if "date" in data:
+            try:
+                income.date = datetime.strptime(data["date"], '%Y-%m-%d').date()
+            except ValueError:
+                return jsonify({"error": "Invalid date format. Use YYYY-MM-DD"}), 400
+        
+        if "end_date" in data:
+            end_date_raw = data["end_date"]
+            if end_date_raw:
+                try:
+                    end_date = datetime.strptime(end_date_raw, "%Y-%m-%d").date()
+                    if income.date and end_date < income.date:
+                        return jsonify({"error": "End date must be after the income date"}), 400
+                    income.end_date = end_date
+                except ValueError:
+                    return jsonify({"error": "Invalid end_date format. Use YYYY-MM-DD"}), 400
+            else:
+                income.end_date = None
+
+        
+        # Validate custom interval if recurring_type is custom
+        if income.recurring and income.recurring_type == RecurringTypeEnum.CUSTOM:
+            if "interval" in data:
+                try:
+                    interval_val = int(data["interval"])
+                    if interval_val <= 0:
+                        return jsonify({"error": "Interval must be a positive number"}), 400
+                    income.interval = interval_val
+                except ValueError:
+                    return jsonify({"error": "Interval must be an integer"}), 400
+        # Set recurring_type enum
+        if "recurring_type" in data:
+            rt = data["recurring_type"]
+            if rt is not None:
+                rt = rt.lower()
+                mapping = {
+                    "daily": RecurringTypeEnum.DAILY,
+                    "weekly": RecurringTypeEnum.WEEKLY,
+                    "every_two_weeks": RecurringTypeEnum.FORTNIGHTLY,
+                    "monthly": RecurringTypeEnum.MONTHLY,
+                    "yearly": RecurringTypeEnum.YEARLY,
+                    "custom": RecurringTypeEnum.CUSTOM,
+                }
+                income.recurring_type = mapping.get(rt)
+                if income.recurring_type is None:
+                    return jsonify({"error": "Invalid recurring_type"}), 400
+            else:
+                income.recurring_type = None
+
+        # Set frequency enum (only for custom)
+        # Set frequency enum (only for custom)
+        if income.recurring_type == RecurringTypeEnum.CUSTOM and "frequency" in data:
+            freq = data["frequency"]
+            if freq is not None:
+                freq = freq.lower()
+                freq_mapping = {
+                    "daily": FrequencyEnum.DAILY,
+                    "weekly": FrequencyEnum.WEEKLY,
+                    "monthly": FrequencyEnum.MONTHLY,
+                    "yearly": FrequencyEnum.YEARLY,
+                }
+                income.frequency = freq_mapping.get(freq)
+                if income.frequency is None:
+                    return jsonify({"error": "Invalid frequency"}), 400
+            else:
+                income.frequency = None
+
+        # Set end_repeat enum
+        # Set end_repeat enum
+        if "end_repeat" in data:
+            er = data["end_repeat"]
+            if er is not None:
+                er = er.lower()
+                if er == "never":
+                    income.end_repeat = EndRepeatEnum.NEVER
+                    income.end_date = None
+                elif er == "on_date":
+                    income.end_repeat = EndRepeatEnum.ON_DATE
+                else:
+                    return jsonify({"error": "Invalid end_repeat value"}), 400
+            else:
+                income.end_repeat = None
+
+
+        if "amount" in data:
+            income.amount = data["amount"]
+        if "source" in data:
+            income.source = data["source"]
+        if "description" in data:
+            income.description = data["description"]
+        if "recurring" in data:
+            income.recurring = data["recurring"]
+
+        db.session.commit()
+        app.logger.info(f"Income updated: {income.to_dict()}")
+        return jsonify({"message": "Income updated", "income": income.to_dict()}), 200
+
+    except Exception as e:
+        app.logger.error(f"Error updating income: {str(e)}", exc_info=True)
+        return jsonify({"error": "An error occurred while updating income"}), 500
+
+@app.route('/income/<int:income_id>', methods=['DELETE'])
+@jwt_required()
+def delete_income(income_id):
+    """Delete an expense by ID for the logged-in user"""
+    try:
+        user_id = int(get_jwt_identity())
+        app.logger.info(f"User ID: {user_id}")
+
+        if not user_id:
+            app.logger.error("User ID is missing or invalid.")
+            return jsonify({"error": "User authentication failed"}), 401
+
+        income = Income.query.filter_by(id=income_id, user_id=user_id).first()
+
+        if not income:
+            app.logger.error(f"Income not found for ID {income_id} and User ID {user_id}")
+            return jsonify({"error": "Income not found"}), 404
+
+        db.session.delete(income)
+        db.session.commit()
+
+        app.logger.info(f"Income deleted: {income.to_dict()}")
+        return jsonify({"message": "Income deleted"}), 200
+
+    except Exception as e:
+        app.logger.error(f"Error deleting income: {str(e)}", exc_info=True)
+        return jsonify({"error": "An error occurred while deleting income"}), 500
+
+@app.route('/income/sources', methods=['GET'])
+@jwt_required()
+def get_income_sources():
+    """Retrieve distinct income sources for the logged-in user"""
+    try:
+        user_id = int(get_jwt_identity())
+        app.logger.info(f"Retrieved user_id from JWT: {user_id}") 
+        if not user_id:
+            app.logger.error("User ID is None or invalid.")
+            return jsonify({"error": "Invalid user authentication"}), 401
+
+        sources = db.session.query(Income.source).filter_by(user_id=user_id).distinct().all()
+        sources_list = [source[0] for source in sources]
+        app.logger.info(f"Source list: {sources_list}")
+        return jsonify({"sources": sources_list}), 200
+        #return jsonify({"sources": [s[0] for s in sources]}), 200
+    except Exception as e:
+        app.logger.error(f"Error fetching income sources: {str(e)}", exc_info=True)
+        return jsonify({"error": "Could not fetch sources"}), 500
 
 
 # EXPENSES ROUTES