From 64bcabfc27f480a177727d5613fe69cf8fff6e43 Mon Sep 17 00:00:00 2001 From: Miuroro Date: Wed, 4 Feb 2026 19:07:40 +0100 Subject: [PATCH 1/2] week 4 assignment --- finance-tracker/.gitignore | 1 + finance-tracker/.prettierrc | 7 ++ finance-tracker/app.js | 16 +++++ finance-tracker/data.js | 51 +++++++++++++- finance-tracker/finance.js | 109 ++++++++++++++++++++++++++---- finance-tracker/package-lock.json | 105 ++++++++++++++++++++++++++++ finance-tracker/package.json | 19 ++++++ 7 files changed, 292 insertions(+), 16 deletions(-) create mode 100644 finance-tracker/.gitignore create mode 100644 finance-tracker/.prettierrc create mode 100644 finance-tracker/package-lock.json create mode 100644 finance-tracker/package.json 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..787be31 --- /dev/null +++ b/finance-tracker/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 80 +} \ No newline at end of file diff --git a/finance-tracker/app.js b/finance-tracker/app.js index 7cfcd07..e51a5b4 100644 --- a/finance-tracker/app.js +++ b/finance-tracker/app.js @@ -1,3 +1,19 @@ // This is the entrypoint for your application. // node app.js +import { transactions } from './data.js'; +import { + addTransaction, + getTotalIncome, + getTotalExpenses, + getBalance, + getTransactionsByCategory, + getLargestExpense, + printAllTransactions, + printSummary, +} from './finance.js'; +// Main execution +console.clear(); +printAllTransactions(transactions); +printSummary(transactions); +// End of file diff --git a/finance-tracker/data.js b/finance-tracker/data.js index d7863ff..b22b302 100644 --- a/finance-tracker/data.js +++ b/finance-tracker/data.js @@ -1,2 +1,51 @@ // Place here the transaction data array. Use it in your application as needed. -const transactions = []; \ No newline at end of file +export const transactions = [ + { + id: 1, + type: 'income', + category: 'salary', + amount: 3000, + description: 'Monthly salary', + date: '2025-11-25', + }, + { + id: 2, + type: 'expense', + category: 'groceries', + amount: 150.5, + description: 'Supermarket shopping', + date: '2025-12-05', + }, + { + id: 3, + type: 'expense', + category: 'rent', + amount: 980, + description: 'Monthly rent', + date: '2025-12-01', + }, + { + id: 4, + type: 'income', + category: 'freelance', + amount: 420, + description: 'Selling socks', + date: '2025-12-20', + }, + { + id: 5, + type: 'expense', + category: 'utilities', + amount: 110.25, + description: 'Electricity and water', + date: '2025-12-15', + }, + { + id: 6, + type: 'expense', + category: 'entertainment', + amount: 60, + description: 'Concert tickets', + date: '2025-12-27', + }, +]; diff --git a/finance-tracker/finance.js b/finance-tracker/finance.js index ac2118f..fb4a0ea 100644 --- a/finance-tracker/finance.js +++ b/finance-tracker/finance.js @@ -1,27 +1,106 @@ -function addTransaction(transaction) { - // TODO: Implement this function +import chalk from 'chalk'; +import { transactions } from './data.js'; + +// Adds a new transaction to the transactions array +export function addTransaction(transaction) { + transactions.push(...[transaction]); +} + +// Calculates and returns the total sum of all income transactions +export function getTotalIncome() { + let total = 0; + for (const transaction of transactions) { + if (transaction.type === 'income') { + total += transaction.amount; + } + } + return total; } -function getTotalIncome() { - // TODO: Implement this function +// Calculates and returns the total sum of all expense transactions +export function getTotalExpenses() { + let total = 0; + for (const transaction of transactions) { + if (transaction.type === 'expense') { + total += transaction.amount; + } + } + return total; } -function getTotalExpenses() { - // TODO: Implement this function +// Calculates and returns the balance (total income minus total expenses) +export function getBalance() { + return getTotalIncome() - getTotalExpenses(); } -function getBalance() { - // TODO: Implement this function +// Filters and returns all transactions that match a specific category +export function getTransactionsByCategory(transactions, category) { + const result = []; + for (const transaction of transactions) { + if (transaction.category === category) { + result.push(transaction); + } + } + return result; } -function getTransactionsByCategory(category) { - // TODO: Implement this function +// Finds and returns the transaction object with the highest expense amount +export function getLargestExpense() { + let largestExpense = null; + let maxAmount = 0; + for (const transaction of transactions) { + if (transaction.type === 'expense' && transaction.amount > maxAmount) { + maxAmount = transaction.amount; + largestExpense = transaction; + } + } + return largestExpense; } -function getLargestExpense() { - // TODO: Implement this function +// Displays all transactions in a formatted console output +export function printAllTransactions(transactions) { + console.log(chalk.bold.cyan('\n💰 PERSONAL FINANCE TRACKER 💰\n')); + console.log(chalk.bold('All Transactions:\n')); + + transactions.forEach((transaction, index) => { + // destructuring + const { id, type, category, amount, description } = transaction; + const typeLabel = type === 'income' ? '[INCOME]' : '[EXPENSE]'; // createing type label + const coloredAmount = + type === 'income' ? chalk.green(`€${amount}`) : chalk.red(`€${amount}`); // green for income red for expense + const coloredCategory = chalk.yellow(`(${category})`); // yellow for category + + console.log( + `${index + 1}. ${typeLabel} ${description} - ${coloredAmount} ${coloredCategory}` + ); // output: 1. typeLabel description - €amount (category) + }); + console.log(); // extra line for better readability } -function printAllTransactions() { - // TODO: Implement this function -} \ No newline at end of file +// Print summary of financial data +export function printSummary(transactions) { + const totalIncome = getTotalIncome(transactions); + const totalExpenses = getTotalExpenses(transactions); + const balance = getBalance(transactions); + const largestExpense = getLargestExpense(transactions); + const transactionCount = transactions.length; + + console.log(chalk.bold.cyan('\n📊 FINANCIAL SUMMARY 📊\n')); + + console.log(`Total Income: ${chalk.green(`€${totalIncome}`)}`); + console.log(`Total Expenses: ${chalk.red(`€${totalExpenses}`)}`); + + // Balance color-coded based on positive or negative + const balanceColor = balance >= 0 ? chalk.cyan : chalk.red; + console.log(`Current Balance: ${balanceColor(`€${balance}`)}`); + + if (largestExpense) { + console.log( + `\nLargest Expense: ${largestExpense.description} (${chalk.red( + `€${largestExpense.amount}` + )})` + ); + } + + console.log(`Total Transactions: ${chalk.bold(transactionCount)}\n`); +} 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..4d49f8d --- /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": "module", + "dependencies": { + "chalk": "^4.1.2" + }, + "devDependencies": { + "prettier": "^3.8.1" + } +} From bb8f6bd04bbaf1fa6d4206f293ee6c65b06fe7de Mon Sep 17 00:00:00 2001 From: Miuroro Date: Fri, 6 Feb 2026 11:01:03 +0100 Subject: [PATCH 2/2] added AverageExpensePerCategory and multi-line commenting --- finance-tracker/app.js | 2 ++ finance-tracker/data.js | 32 ++++++++++++++++++++++++++++++++ finance-tracker/finance.js | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/finance-tracker/app.js b/finance-tracker/app.js index e51a5b4..649cd18 100644 --- a/finance-tracker/app.js +++ b/finance-tracker/app.js @@ -10,10 +10,12 @@ import { getLargestExpense, printAllTransactions, printSummary, + getAverageExpensePerCategory, } from './finance.js'; // Main execution console.clear(); printAllTransactions(transactions); printSummary(transactions); +getAverageExpensePerCategory(transactions); // End of file diff --git a/finance-tracker/data.js b/finance-tracker/data.js index b22b302..592e837 100644 --- a/finance-tracker/data.js +++ b/finance-tracker/data.js @@ -48,4 +48,36 @@ export const transactions = [ description: 'Concert tickets', date: '2025-12-27', }, + { + id: 7, + type: 'income', + category: 'investment', + amount: 150, + description: 'Stock market', + date: '2025-12-10', + }, + { + id: 8, + type: 'expense', + category: 'transportation', + amount: 45, + description: 'Monthly bus pass', + date: '2025-11-03', + }, + { + id: 9, + type: 'expense', + category: 'groceries', + amount: 95, + description: 'Weekly groceries', + date: '2025-11-10', + }, + { + id: 10, + type: 'expense', + category: 'entertainment', + amount: 120, + description: 'New video game', + date: '2025-11-20', + }, ]; diff --git a/finance-tracker/finance.js b/finance-tracker/finance.js index fb4a0ea..fa3d0cb 100644 --- a/finance-tracker/finance.js +++ b/finance-tracker/finance.js @@ -104,3 +104,40 @@ export function printSummary(transactions) { console.log(`Total Transactions: ${chalk.bold(transactionCount)}\n`); } + +// Calculates and returns the average expense amount for each category +export function getAverageExpensePerCategory(transactions) { + const expenses = transactions.filter( + (transaction) => transaction.type === 'expense' + ); + const categoryTotals = {}; + const categoryCounts = {}; + /** + * This loop iterates through each transaction in the `expenses` array. + * For each transaction, it updates two objects: + * 1. `categoryTotals`: It adds the transaction's `amount` to a running total for its specific `category`. + * If a category is seen for the first time, it's initialized to 0 before adding the amount. + * 2. `categoryCounts`: It increments a counter for the transaction's `category`. + * If a category is seen for the first time, its count is initialized to 0 before incrementing. + * This effectively calculates both the total amount spent and the number of transactions per category. + */ + for (const { category, amount } of expenses) { + categoryTotals[category] = (categoryTotals[category] || 0) + amount; + categoryCounts[category] = (categoryCounts[category] || 0) + 1; + } + + const averages = {}; // storing average expense per category + for (const category in categoryTotals) { + const total = categoryTotals[category]; + const count = categoryCounts[category]; + averages[category] = +(total / count).toFixed(2); // Calculate average and round to 2 decimal places + } + + console.log(chalk.bold('Average Expense per Category:')); + for (const category in averages) { + const average = averages[category]; + console.log(`- ${category}: ${chalk.red(`€${average}`)}`); + } + + return averages; +} \ No newline at end of file