diff --git a/README.md b/README.md index c785ee7..9f80319 100644 --- a/README.md +++ b/README.md @@ -284,6 +284,7 @@ The browser app currently exposes 39 projects: ### Utilities - AI Resume Analyzer +- Budget Tracker - Color Palette Suggestor - Morse Code - Number Converter @@ -299,7 +300,6 @@ These standalone Python project files do not have a browser counterpart yet and - [math/Happy-Number/Happy-Number.py](math/Happy-Number/Happy-Number.py) - [math/Matrix-Calculator/Matrix-Calculator.py](math/Matrix-Calculator/Matrix-Calculator.py) - [math/Quadratic-Solver/Quadratic-Solver.py](math/Quadratic-Solver/Quadratic-Solver.py) -- [utilities/Budget-Tracker/budget_tracker.py](utilities/Budget-Tracker/budget_tracker.py) - [utilities/Caesar-Cipher/Caesar-Cipher.py](utilities/Caesar-Cipher/Caesar-Cipher.py) - [utilities/Fake-News-Headline-Generator/Fake-News-Headline-Generator.py](utilities/Fake-News-Headline-Generator/Fake-News-Headline-Generator.py) diff --git a/web-app/assets/banners/budget-tracker.webp b/web-app/assets/banners/budget-tracker.webp new file mode 100644 index 0000000..d459128 Binary files /dev/null and b/web-app/assets/banners/budget-tracker.webp differ diff --git a/web-app/generate_banners.py b/web-app/generate_banners.py index 478743c..f52470b 100644 --- a/web-app/generate_banners.py +++ b/web-app/generate_banners.py @@ -573,6 +573,7 @@ def draw_o(ox, oy): ("AI Resume Analyzer", "utilities", "resume-analyzer.webp"), ("Caesar Cipher", "utilities", "caesar-cipher.webp"), ("Unit Converter", "utilities", "unit-converter.webp"), + ("Budget Tracker", "utilities", "budget-tracker.webp"), ] # Run generation diff --git a/web-app/index.html b/web-app/index.html index 04b8d0a..93f3e60 100644 --- a/web-app/index.html +++ b/web-app/index.html @@ -874,6 +874,7 @@

Legal

+ @@ -1122,6 +1123,13 @@

Legal

}, // UTILITIES (10+) + { + project: "budget-tracker", + title: "Budget Tracker", + category: "utilities", + desc: "Track income and expenses, view categories, and persist data locally", + tags: "utility,finance,tracker,money" + }, { project: "morse-code", title: "Morse Code", diff --git a/web-app/js/projects.js b/web-app/js/projects.js index 5a94708..d61ec4d 100644 --- a/web-app/js/projects.js +++ b/web-app/js/projects.js @@ -1596,6 +1596,26 @@ function getProjectHTML(projectName) { const projectInstructions = { // GAMES + "war-card-game": { + title: "⚔️ How to Play War Card Game", + steps: [ + "Enter names or check the option to play against the CPU.", + "Each player starts with a deck of 26 cards.", + "Click 'Draw / Battle' to draw the top card from both decks.", + "The player with the higher card rank wins the round and gets a point.", + "Ace is the highest, 2 is the lowest.", + "Play continues until all cards are drawn. The player with the most points wins!" + ] + }, + "number-sliding-puzzle": { + title: "🧩 How to Play Number Sliding Puzzle", + steps: [ + "Use arrow keys (← ↑ → ↓) or click/tap on tiles next to the empty space to slide them.", + "Arrange the numbers in ascending order from 1 to 8, with the blank space at the bottom right.", + "A moves counter keeps track of your steps.", + "Click the Reset button to restart the game." + ] + }, "2048-game": { title: "🎮 How to Play 2048", steps: [ @@ -1864,6 +1884,16 @@ const projectInstructions = { "Click any color to copy its hex code" ] }, + "budget-tracker": { + title: "💰 How to Use Budget Tracker", + steps: [ + "Select the transaction type (Income or Expense) from the dropdown.", + "Enter a category, description (optional), and positive amount value.", + "Click 'Add Transaction' to log it in your dashboard.", + "Analyze spending behavior through dynamic category-wise progress bars.", + "Clear individual records using the delete action or wipe out all history securely." + ] + }, "morse-code": { title: "📻 How to Use Morse Code", steps: [ @@ -3158,6 +3188,10 @@ function initializeProject(projectName) { "color-palette": "initColorPalette", "math-quiz": "initMathQuiz", "resume-analyzer": "initResumeAnalyzer", + "caesar-cipher": "initCaesarCipher", + "war-card-game": "initWarCardGame", + "number-sliding-puzzle": "initNumberSlidingPuzzle", + "budget-tracker": "initBudgetTracker" "caesar-cipher": "initCaesarCipher" }; diff --git a/web-app/js/projects/budget-tracker.js b/web-app/js/projects/budget-tracker.js new file mode 100644 index 0000000..e2eebb6 --- /dev/null +++ b/web-app/js/projects/budget-tracker.js @@ -0,0 +1,636 @@ +function getBudgetTrackerHTML() { + return ` +
+

💰 Budget Tracker

+
+ + +
+
+
📈
+
+ Total Income + ₹0.00 +
+
+
+
📉
+
+ Total Expenses + ₹0.00 +
+
+
+
⚖️
+
+ Net Balance + ₹0.00 +
+
+
+ +
+ +
+

Add New Transaction

+
+
+ + +
+ +
+ + + + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ + +
+

Expense Analytics

+
+

No expense data to analyze yet.

+
+
+
+ + +
+
+

Recent Transactions

+ +
+
+ + + + + + + + + + + + + + +
DateTypeCategoryDescriptionAmountAction
+
+
+ +
+
+ + + + + + `; +} + +function initBudgetTracker() { + const form = document.getElementById('transactionForm'); + const transType = document.getElementById('transType'); + const transCategory = document.getElementById('transCategory'); + const transDescription = document.getElementById('transDescription'); + const transAmount = document.getElementById('transAmount'); + + const totalIncomeEl = document.getElementById('totalIncome'); + const totalExpensesEl = document.getElementById('totalExpenses'); + const netBalanceEl = document.getElementById('netBalance'); + const balanceCard = document.getElementById('balanceCard'); + const balanceIcon = document.getElementById('balanceIcon'); + + const analyticsContainer = document.getElementById('analyticsContainer'); + const transactionList = document.getElementById('transactionList'); + + // Confirmation Modal Elements + const clearAllBtn = document.getElementById('clearAllBtn'); + const budgetConfirmModal = document.getElementById('budgetConfirmModal'); + const cancelClearBtn = document.getElementById('cancelClearBtn'); + const confirmClearBtn = document.getElementById('confirmClearBtn'); + + // Load Initial Transactions + let transactions = JSON.parse(localStorage.getItem('budget_transactions') || '[]'); + + function saveAndRender() { + localStorage.setItem('budget_transactions', JSON.stringify(transactions)); + renderUI(); + } + + function renderUI() { + // 1. Calculate Sums + let totalIncome = 0; + let totalExpenses = 0; + const expenseByCategory = {}; + + transactions.forEach(t => { + if (t.type === 'income') { + totalIncome += t.amount; + } else { + totalExpenses += t.amount; + expenseByCategory[t.category] = (expenseByCategory[t.category] || 0) + t.amount; + } + }); + + const netBalance = totalIncome - totalExpenses; + + // 2. Update Summary Cards + totalIncomeEl.textContent = `₹${totalIncome.toFixed(2)}`; + totalExpensesEl.textContent = `₹${totalExpenses.toFixed(2)}`; + netBalanceEl.textContent = `${netBalance >= 0 ? '' : '-'}₹${Math.abs(netBalance).toFixed(2)}`; + + // Net Balance Styling + if (netBalance >= 0) { + balanceCard.className = 'summary-card balance positive'; + balanceIcon.textContent = '✅'; + } else { + balanceCard.className = 'summary-card balance negative'; + balanceIcon.textContent = '⚠️'; + } + + // 3. Update Category Analytics + analyticsContainer.innerHTML = ''; + const categories = Object.keys(expenseByCategory); + if (categories.length === 0) { + analyticsContainer.innerHTML = '

No expense data to analyze yet.

'; + } else { + const wrapper = document.createElement('div'); + wrapper.className = 'analytics-container'; + + // Sort categories by expenditure (descending) + categories + .sort((a, b) => expenseByCategory[b] - expenseByCategory[a]) + .forEach(cat => { + const amount = expenseByCategory[cat]; + const percentage = totalExpenses > 0 ? (amount / totalExpenses) * 100 : 0; + + const item = document.createElement('div'); + item.className = 'analytics-item'; + item.innerHTML = ` +
+ ${cat} + ₹${amount.toFixed(2)} (${percentage.toFixed(0)}%) +
+
+
+
+ `; + wrapper.appendChild(item); + }); + analyticsContainer.appendChild(wrapper); + } + + // 4. Update Transaction List Table + transactionList.innerHTML = ''; + if (transactions.length === 0) { + transactionList.innerHTML = ` + + No transactions found. Add some above! + + `; + } else { + // Show newest first + [...transactions].reverse().forEach(t => { + const row = document.createElement('tr'); + const badgeClass = t.type === 'income' ? 'badge income' : 'badge expense'; + const symbol = t.type === 'income' ? '+' : '-'; + + row.innerHTML = ` + ${t.date} + ${t.type} + ${t.category} + ${t.description || '-'} + + ${symbol}₹${t.amount.toFixed(2)} + + + + + `; + transactionList.appendChild(row); + }); + + // Hook up delete buttons + document.querySelectorAll('.btn-delete').forEach(btn => { + btn.addEventListener('click', (e) => { + const id = parseInt(btn.getAttribute('data-id')); + transactions = transactions.filter(t => t.id !== id); + saveAndRender(); + }); + }); + } + } + + // Handle Form Submission + form.addEventListener('submit', (e) => { + e.preventDefault(); + + const category = transCategory.value.trim(); + const amount = parseFloat(transAmount.value); + if (!category || isNaN(amount) || amount <= 0) return; + + const newTransaction = { + id: Date.now(), + date: new Date().toLocaleDateString(undefined, { + year: 'numeric', + month: 'short', + day: 'numeric' + }), + type: transType.value, + category: category.charAt(0).toUpperCase() + category.slice(1), + description: transDescription.value.trim(), + amount: parseFloat(amount.toFixed(2)) + }; + + transactions.push(newTransaction); + saveAndRender(); + form.reset(); + }); + + // Handle Custom Confirmation Modal Overlay for Clear All + clearAllBtn.addEventListener('click', () => { + budgetConfirmModal.style.display = 'flex'; + }); + + cancelClearBtn.addEventListener('click', () => { + budgetConfirmModal.style.display = 'none'; + }); + + confirmClearBtn.addEventListener('click', () => { + transactions = []; + saveAndRender(); + budgetConfirmModal.style.display = 'none'; + }); + + // Render initial list + renderUI(); +} diff --git a/web-app/utilities.html b/web-app/utilities.html index 61faf9a..467c396 100644 --- a/web-app/utilities.html +++ b/web-app/utilities.html @@ -350,6 +350,25 @@

Caesar Cipher

Encrypt & decrypt with a shift!

+
+ Budget Tracker +
+ +
+ +

Budget Tracker

+

Track income and expenses, view categories, and persist data locally

+
+
Typing Speed Tester +