diff --git a/finance-tracker/.gitignore b/finance-tracker/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/finance-tracker/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/finance-tracker/.prettierrc b/finance-tracker/.prettierrc new file mode 100644 index 0000000..af11965 --- /dev/null +++ b/finance-tracker/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "semi": true, + "trailingComma": "es5" +} diff --git a/finance-tracker/app.js b/finance-tracker/app.js index 7cfcd07..2fe2b91 100644 --- a/finance-tracker/app.js +++ b/finance-tracker/app.js @@ -1,3 +1,83 @@ -// This is the entrypoint for your application. +// This is the entrypoint for your application // node app.js +const chalk = require('chalk'); +const transactions = require('./data'); +const { + getTotalIncome, + getTotalExpenses, + getBalance, + getTransactionsByCategory, + getLargestExpense, + getTransactionsByDateRange, + averageExpensePerCategory, +} = require('./finance'); +// Display all transactions +function printAllTransactions() { + console.log(chalk.bold('\nšŸ’° PERSONAL FINANCE TRACKER šŸ’°\n')); + console.log('All Transactions:'); + + transactions.forEach((t, i) => { + const label = + t.type === 'income' ? chalk.green('[INCOME]') : chalk.red('[EXPENSE]'); + const amountColor = + t.type === 'income' + ? chalk.green(`€${t.amount}`) + : chalk.red(`€${t.amount}`); + console.log( + `${i + 1}. ${label} ${t.description} - ${amountColor} (${chalk.yellow(t.category)})` + ); + }); +} + +// Display transactions by date range +function printTransactionsByDateRange(start, end) { + const results = getTransactionsByDateRange(start, end); + console.log(chalk.bold(`\nTransactions from ${start} to ${end}:`)); + + results.forEach((t, i) => { + const label = + t.type === 'income' ? chalk.green('[INCOME]') : chalk.red('[EXPENSE]'); + const amountColor = + t.type === 'income' + ? chalk.green(`€${t.amount}`) + : chalk.red(`€${t.amount}`); + console.log( + `${i + 1}. ${label} ${t.description} - ${amountColor} (${chalk.yellow(t.category)})` + ); + }); +} + +// Display summary +function printSummary() { + const income = getTotalIncome(); + const expenses = getTotalExpenses(); + const balance = getBalance(); + const largestExpense = getLargestExpense(); + + console.log(chalk.bold('\nšŸ“Š FINANCIAL SUMMARY šŸ“Š')); + console.log(chalk.bold(`Total Income: €${income}`)); + console.log(chalk.bold(`Total Expenses: €${expenses}`)); + + const balanceText = + balance >= 0 ? chalk.cyan(`€${balance}`) : chalk.red(`€${balance}`); + console.log(chalk.bold('Current Balance: '), balanceText); + + if (largestExpense) { + console.log( + `\nLargest Expense: ${largestExpense.description} (€${largestExpense.amount})` + ); + } + + console.log(`Total Transactions: ${transactions.length}`); + + const averages = averageExpensePerCategory(); + console.log(chalk.bold('\nAverage Expense per Category:')); + for (const category in averages) { + console.log(`${chalk.yellow(category)}: €${averages[category].toFixed(2)}`); + } +} + +printAllTransactions(); +printSummary(); +printTransactionsByDateRange('2025-01-01', '2025-01-25'); diff --git a/finance-tracker/data.js b/finance-tracker/data.js index d7863ff..6c0e436 100644 --- a/finance-tracker/data.js +++ b/finance-tracker/data.js @@ -1,2 +1,101 @@ // Place here the transaction data array. Use it in your application as needed. -const transactions = []; \ No newline at end of file +const transactions = [ + { + id: 1, + type: 'income', + category: 'salary', + amount: 3000, + description: 'Salary', + date: '2025-01-15', + }, + { + id: 2, + type: 'expense', + category: 'housing', + amount: 1200, + description: 'Rent', + date: '2025-01-01', + }, + { + id: 3, + type: 'expense', + category: 'food', + amount: 450, + description: 'Groceries', + date: '2025-01-10', + }, + { + id: 4, + type: 'income', + category: 'side-income', + amount: 700, + description: 'Freelance', + date: '2025-01-20', + }, + { + id: 5, + type: 'expense', + category: 'bills', + amount: 300, + description: 'Utilities', + date: '2025-01-05', + }, + { + id: 6, + type: 'expense', + category: 'transport', + amount: 120, + description: 'Public transport', + date: '2025-01-12', + }, + { + id: 7, + type: 'expense', + category: 'entertainment', + amount: 200, + description: 'Cinema & dinner', + date: '2025-01-18', + }, + { + id: 8, + type: 'income', + category: 'bonus', + amount: 400, + description: 'Performance bonus', + date: '2025-01-25', + }, + { + id: 9, + type: 'expense', + category: 'health', + amount: 90, + description: 'Pharmacy', + date: '2025-01-08', + }, + { + id: 10, + type: 'expense', + category: 'shopping', + amount: 250, + description: 'Clothes', + date: '2025-01-22', + }, + { + id: 11, + type: 'income', + category: 'gift', + amount: 150, + description: 'Birthday gift', + date: '2025-01-28', + }, + { + id: 12, + type: 'expense', + category: 'food', + amount: 80, + description: 'Restaurant', + date: '2025-01-27', + }, +]; + +module.exports = transactions; diff --git a/finance-tracker/finance.js b/finance-tracker/finance.js index ac2118f..8cd6b56 100644 --- a/finance-tracker/finance.js +++ b/finance-tracker/finance.js @@ -1,27 +1,91 @@ +const transactions = require('./data'); + +// Add new transaction function addTransaction(transaction) { - // TODO: Implement this function + transactions.push({ ...transaction }); } +// Total income function getTotalIncome() { - // TODO: Implement this function + let total = 0; + for (const transaction of transactions) { + if (transaction.type === 'income') total += transaction.amount; + } + return total; } +// Total expenses function getTotalExpenses() { - // TODO: Implement this function + let total = 0; + for (const transaction of transactions) { + if (transaction.type === 'expense') total += transaction.amount; + } + return total; } +// Current balance function getBalance() { - // TODO: Implement this function + return getTotalIncome() - getTotalExpenses(); } +// Filter transactions by category function getTransactionsByCategory(category) { - // TODO: Implement this function + const result = []; + for (const transaction of transactions) { + if (transaction.category.includes(category)) result.push(transaction); + } + return result; } +// Find largest expense function getLargestExpense() { - // TODO: Implement this function + let largest = null; + for (const transaction of transactions) { + if ( + transaction.type === 'expense' && + (!largest || transaction.amount > largest.amount) + ) { + largest = transaction; + } + } + return largest; +} + +// Search by date range +function getTransactionsByDateRange(start, end) { + const result = []; + for (const transaction of transactions) { + if (transaction.date >= start && transaction.date <= end) + result.push(transaction); + } + return result; +} + +// Average expense per category +function averageExpensePerCategory() { + const totals = {}; + const counts = {}; + for (const transaction of transactions) { + if (transaction.type === 'expense') { + totals[transaction.category] = + (totals[transaction.category] || 0) + transaction.amount; + counts[transaction.category] = (counts[transaction.category] || 0) + 1; + } + } + const averages = {}; + for (const category in totals) { + averages[category] = totals[category] / counts[category]; + } + return averages; } -function printAllTransactions() { - // TODO: Implement this function -} \ No newline at end of file +module.exports = { + addTransaction, + getTotalIncome, + getTotalExpenses, + getBalance, + getTransactionsByCategory, + getLargestExpense, + getTransactionsByDateRange, + averageExpensePerCategory, +}; diff --git a/finance-tracker/package-lock.json b/finance-tracker/package-lock.json new file mode 100644 index 0000000..2195e5f --- /dev/null +++ b/finance-tracker/package-lock.json @@ -0,0 +1,105 @@ +{ + "name": "finance-tracker", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "finance-tracker", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "chalk": "^4.1.2" + }, + "devDependencies": { + "prettier": "^3.8.1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/finance-tracker/package.json b/finance-tracker/package.json new file mode 100644 index 0000000..04f03bb --- /dev/null +++ b/finance-tracker/package.json @@ -0,0 +1,19 @@ +{ + "name": "finance-tracker", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "dependencies": { + "chalk": "^4.1.2" + }, + "devDependencies": { + "prettier": "^3.8.1" + } +}