expense-tracker-cli-didierganthier
Version:
A simple CLI to track your expenses from the terminal.
143 lines (118 loc) • 4.28 kB
JavaScript
const { program } = require('commander');
const fs = require('fs');
const path = require('path');
const expensesFile = path.join(__dirname, 'expenses.json');
program.name('expense-tracker')
.description('A simple command-line expense tracker')
.version('1.0.0');
const loadExpenses = () => {
if (!fs.existsSync(expensesFile)) {
fs.writeFileSync(expensesFile, '[]');
}
const data = fs.readFileSync(expensesFile);
return JSON.parse(data);
}
const saveExpenses = (expenses) => {
fs.writeFileSync(expensesFile, JSON.stringify(expenses, null, 2));
}
program.command('add')
.description('Add a new expense')
.requiredOption('--description <description>', 'Description of the expense')
.requiredOption('--amount <amount>', 'Amount of the expense')
.action((options) => {
const expenses = loadExpenses();
const id = expenses.length ? expenses[expenses.length - 1].id + 1 : 1;
const date = new Date().toISOString().split('T')[0];
const amount = parseFloat(options.amount);
if (isNaN(amount)) {
console.error('❌ Invalid amount. Please enter a valid number.');
process.exit(1);
}
expenses.push({
id,
description: options.description,
amount,
date
});
saveExpenses(expenses);
console.log(`✅ Expense added: ${options.description} - $${amount} on ${date}`);
});
program.command('list')
.description('List all expenses')
.action(() => {
const expenses = loadExpenses();
if (expenses.length === 0) {
console.log('📄 No expenses found.');
return;
}
console.log('📄 List of expenses:');
console.table(expenses.map(expense => ({
ID: expense.id,
Description: expense.description,
Amount: `$${expense.amount.toFixed(2)}`,
Date: expense.date
})));
});
program.command('delete')
.description('Delete an expense by ID')
.requiredOption('--id <id>', 'ID of the expense to delete')
.action((options) => {
const expenses = loadExpenses();
const id = parseInt(options.id, 10);
const index = expenses.findIndex(expense => expense.id === id);
if (index === -1) {
console.error(`❌ Expense with ID ${id} not found.`);
process.exit(1);
}
expenses.splice(index, 1);
saveExpenses(expenses);
console.log(`✅ Expense with ID ${id} deleted.`);
});
program
.command('summary')
.description('Show total expenses or summary for a specific month')
.option('--month <month>', 'Month number (1-12)')
.action((options) => {
const expenses = loadExpenses();
let filtered = expenses;
if (options.month) {
const month = parseInt(options.month, 10);
filtered = expenses.filter(exp => {
const expMonth = new Date(exp.date).getMonth() + 1;
return expMonth === month;
});
const total = filtered.reduce((sum, exp) => sum + exp.amount, 0);
console.log(`Total expenses for month ${month}: $${total}`);
} else {
const total = expenses.reduce((sum, exp) => sum + exp.amount, 0);
console.log(`Total expenses: $${total}`);
}
});
program
.command('update')
.description('Update an expense by ID')
.requiredOption('--id <id>', 'Expense ID')
.option('--description <description>', 'New description')
.option('--amount <amount>', 'New amount')
.action((options) => {
const expenses = loadExpenses();
const id = parseInt(options.id, 10);
const exp = expenses.find(exp => exp.id === id);
if (!exp) {
console.error('❌ Expense not found.');
process.exit(1);
}
if (options.description) exp.description = options.description;
if (options.amount) {
const amount = parseFloat(options.amount);
if (isNaN(amount) || amount <= 0) {
console.error('❌ Amount must be a positive number.');
process.exit(1);
}
exp.amount = amount;
}
saveExpenses(expenses);
console.log(`✅ Expense updated successfully (ID: ${id})`);
});
program.parse(process.argv);