Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 112 additions & 106 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,117 +1,123 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Expense Tracker</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.2/jspdf.plugin.autotable.min.js"></script>
</head>

<body>

<header>
<h1>Expense Tracker</h1>
<div class="dark-box">
<button id="darkToggle" class="dark-mode-btn" aria-label="Toggle dark mode">
<span class="icon">πŸŒ™</span>
</button>
</div>
</header>
<div id="notification" class="notification"></div>
<div class="container">
<div class="card">

<h2>Add Expense</h2>
<input type="text" id="name" placeholder="Expense Name">
<input type="number" id="amount" placeholder="Amount">
<input type="date" id="date">

<select id="category">
<option value="Food">Food</option>
<option value="Transport">Transport</option>
<option value="Shopping">Shopping</option>
<option value="Bills">Bills</option>
<option value="Entertainment">Entertainment</option>
<option value="Other">Other</option>
</select>

<button onclick="addExpense()">Add</button>

<h3>Monthly Budget</h3>
<input type="number" id="monthlyBudget" placeholder="Set Monthly Budget">
<button onclick="setBudget()">Set Budget</button>

<h2>Filter & Analyze</h2>

<input type="text" id="search" placeholder="Search by name">

<select id="monthFilter" onchange="renderExpenses()"></select>

<select id="categoryFilter">
<option value="All">All Categories</option>
<option value="Food">Food</option>
<option value="Transport">Transport</option>
<option value="Shopping">Shopping</option>
<option value="Bills">Bills</option>
<option value="Entertainment">Entertainment</option>
<option value="Other">Other</option>
</select>

<select id="sortOption">
<option value="dateDesc">Date (Newest)</option>
<option value="dateAsc">Date (Oldest)</option>
<option value="category">Category</option>
</select>

<button onclick="applyFilters()">Apply</button>

<div class="total">
Selected Month Total: β‚Ή<span id="totalAmount">0</span>
</div>

<div class="warning" id="budgetWarning"></div>

<table>
<thead>
<tr>
<th>Name</th>
<th>Amount</th>
<th>Date</th>
<th>Category</th>
<th>Action</th>
</tr>
</thead>
<tbody id="expenseList"></tbody>
</table>

<h2>Monthly Summary</h2>
<table>
<thead>
<tr>
<th>Month</th>
<th>Total Expense</th>
</tr>
</thead>
<tbody id="monthlySummary"></tbody>
</table>

<h2>Category Distribution (Pie Chart)</h2>

<div class="chart-container">
<canvas id="expenseChart"></canvas>
</div>
</div>
</div>

<footer>
© 2026 Expense Tracker|Built with ❀️
</footer>
<div id="confirmModal" class="modal">
<div class="modal-content">
<p id="confirmMessage"></p>
<button id="confirmYes">Yes</button>
<button id="confirmNo">Cancel</button>
</div>
</div>
<script src="script.js"></script>
<header>
<h1>Expense Tracker</h1>
<div class="dark-box">
<button id="darkToggle" class="dark-mode-btn" aria-label="Toggle dark mode">
<span class="icon">πŸŒ™</span>
</button>
</div>
</header>
<div id="notification" class="notification"></div>
<div class="container">
<div class="card">

<h2>Add Expense</h2>
<input type="text" id="name" placeholder="Expense Name">
<input type="number" id="amount" placeholder="Amount">
<input type="date" id="date">

<select id="category">
<option value="Food">Food</option>
<option value="Transport">Transport</option>
<option value="Shopping">Shopping</option>
<option value="Bills">Bills</option>
<option value="Entertainment">Entertainment</option>
<option value="Other">Other</option>
</select>

<button onclick="addExpense()">Add</button>

<h3>Monthly Budget</h3>
<input type="number" id="monthlyBudget" placeholder="Set Monthly Budget">
<button onclick="setBudget()">Set Budget</button>

<h2>Filter & Analyze</h2>

<input type="text" id="search" placeholder="Search by name">

<select id="monthFilter" onchange="renderExpenses()"></select>

<select id="categoryFilter">
<option value="All">All Categories</option>
<option value="Food">Food</option>
<option value="Transport">Transport</option>
<option value="Shopping">Shopping</option>
<option value="Bills">Bills</option>
<option value="Entertainment">Entertainment</option>
<option value="Other">Other</option>
</select>

<select id="sortOption">
<option value="dateDesc">Date (Newest)</option>
<option value="dateAsc">Date (Oldest)</option>
<option value="category">Category</option>
</select>

<button onclick="applyFilters()">Apply</button>

<div class="total">
Selected Month Total: β‚Ή<span id="totalAmount">0</span>
<button id="exportPdfBtn" onclick="exportToPDF()" class="export-btn">πŸ“„ Export to PDF</button>
</div>

<div class="warning" id="budgetWarning"></div>

<table>
<thead>
<tr>
<th>Name</th>
<th>Amount</th>
<th>Date</th>
<th>Category</th>
<th>Action</th>
</tr>
</thead>
<tbody id="expenseList"></tbody>
</table>

<h2>Monthly Summary</h2>
<table>
<thead>
<tr>
<th>Month</th>
<th>Total Expense</th>
</tr>
</thead>
<tbody id="monthlySummary"></tbody>
</table>

<h2>Category Distribution (Pie Chart)</h2>

<div class="chart-container">
<canvas id="expenseChart"></canvas>
</div>
</div>
</div>

<footer>
© 2026 Expense Tracker|Built with ❀️
</footer>
<div id="confirmModal" class="modal">
<div class="modal-content">
<p id="confirmMessage"></p>
<button id="confirmYes">Yes</button>
<button id="confirmNo">Cancel</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>

</html>
143 changes: 140 additions & 3 deletions script.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,16 @@ if (localStorage.getItem("darkMode") === "enabled") {

toggle.addEventListener("change", function () {
document.body.classList.toggle("dark");
localStorage.setItem("darkMode",
document.body.classList.contains("dark") ? "enabled" : "disabled"
);
const icon = darkToggle.querySelector('.icon');

// Update button icon based on mode
if (document.body.classList.contains("dark")) {
icon.textContent = 'β˜€οΈ'; // Sun for dark mode (to switch to light)
localStorage.setItem("darkMode", "enabled");
} else {
icon.textContent = 'πŸŒ™'; // Moon for light mode (to switch to dark)
localStorage.setItem("darkMode", "disabled");
}
});

function saveData() {
Expand Down Expand Up @@ -275,4 +282,134 @@ function generatePieChart(selectedMonth) {
});
}

function exportToPDF() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();

// Get current filter state
const month = document.getElementById("monthFilter").value;
const category = document.getElementById("categoryFilter").value;
const total = document.getElementById("totalAmount").textContent;

// Filter expenses same as renderExpenses
let filtered = [...expenses];
const search = document.getElementById("search").value.toLowerCase();

if (search)
filtered = filtered.filter(e => e.name.toLowerCase().includes(search));
if (month !== "All")
filtered = filtered.filter(e => e.date && e.date.startsWith(month));
if (category !== "All")
filtered = filtered.filter(e => e.category === category);

const sort = document.getElementById("sortOption").value;
if (sort === "dateAsc")
filtered.sort((a, b) => new Date(a.date) - new Date(b.date));
else if (sort === "dateDesc")
filtered.sort((a, b) => new Date(b.date) - new Date(a.date));
else if (sort === "category")
filtered.sort((a, b) => a.category.localeCompare(b.category));

// --- Title ---
doc.setFontSize(20);
doc.setTextColor(46, 125, 50);
doc.text("Expense Tracker Report", 14, 20);

// --- Report info ---
doc.setFontSize(10);
doc.setTextColor(100);
const reportDate = new Date().toLocaleDateString("en-IN", {
year: "numeric", month: "long", day: "numeric"
});
doc.text("Generated: " + reportDate, 14, 28);
const filterLabel = month !== "All" ? "Month: " + month : "All Months";
const catLabel = category !== "All" ? " | Category: " + category : "";
doc.text("Filters: " + filterLabel + catLabel, 14, 34);

// --- Total ---
doc.setFontSize(13);
doc.setTextColor(0);
doc.text("Total: \u20b9" + total, 14, 44);

// --- Budget status ---
if (monthlyBudget > 0) {
const totalNum = parseFloat(total);
doc.setFontSize(10);
if (totalNum > monthlyBudget) {
doc.setTextColor(211, 47, 47);
doc.text("Budget: \u20b9" + monthlyBudget + " \u2014 EXCEEDED", 14, 50);
} else if (totalNum >= monthlyBudget * 0.8) {
doc.setTextColor(245, 124, 0);
doc.text("Budget: \u20b9" + monthlyBudget + " \u2014 Approaching limit (>= 80%)", 14, 50);
} else {
doc.setTextColor(46, 125, 50);
doc.text("Budget: \u20b9" + monthlyBudget + " \u2014 Within limit", 14, 50);
}
}

// --- Expense table ---
if (filtered.length === 0) {
doc.setFontSize(11);
doc.setTextColor(100);
doc.text("No expenses to display for the selected filters.", 14, 62);
} else {
const tableData = filtered.map(function (exp) {
return [exp.name, "\u20b9" + exp.amount, exp.date || "N/A", exp.category];
});

doc.autoTable({
startY: monthlyBudget > 0 ? 56 : 50,
head: [["Name", "Amount", "Date", "Category"]],
body: tableData,
theme: "striped",
headStyles: { fillColor: [46, 125, 50] },
styles: { fontSize: 9 }
});
}

// --- Category summary ---
const categoryTotals = {};
filtered.forEach(function (exp) {
categoryTotals[exp.category] = (categoryTotals[exp.category] || 0) + exp.amount;
});

if (Object.keys(categoryTotals).length > 0) {
const catData = Object.entries(categoryTotals).map(function (entry) {
return [entry[0], "\u20b9" + entry[1]];
});
const startY = doc.lastAutoTable ? doc.lastAutoTable.finalY + 10 : 70;

doc.setFontSize(13);
doc.setTextColor(0);
doc.text("Category Summary", 14, startY);

doc.autoTable({
startY: startY + 4,
head: [["Category", "Total"]],
body: catData,
theme: "grid",
headStyles: { fillColor: [21, 101, 192] },
styles: { fontSize: 9 }
});
}

// --- Footer on every page ---
const pageCount = doc.internal.getNumberOfPages();
for (var i = 1; i <= pageCount; i++) {
doc.setPage(i);
doc.setFontSize(8);
doc.setTextColor(150);
doc.text(
"Expense Tracker \u2014 Page " + i + " of " + pageCount,
doc.internal.pageSize.getWidth() / 2,
doc.internal.pageSize.getHeight() - 10,
{ align: "center" }
);
}

// --- Save ---
const filename = month !== "All" ? "expenses_" + month + ".pdf" : "expenses_report.pdf";
doc.save(filename);
}

fetchExpenses();
Loading