import React, { useState, useEffect } from 'react'; import { BarChart, LineChart, Line, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; import { PieChart, Pie, Cell } from 'recharts'; import { Upload, DollarSign, Calendar, TrendingUp, Menu, X, Plus, Edit2, Trash2, FileText, Save } from 'lucide-react'; export default function FinanceManager() { // State management const [transactions, setTransactions] = useState([]); const [categories, setCategories] = useState([ 'Housing', 'Food', 'Transportation', 'Utilities', 'Entertainment', 'Healthcare', 'Shopping', 'Personal', 'Debt', 'Income', 'Other' ]); const [budgets, setBudgets] = useState({}); const [activeTab, setActiveTab] = useState('dashboard'); const [isMenuOpen, setIsMenuOpen] = useState(false); const [isAddBudgetModalOpen, setIsAddBudgetModalOpen] = useState(false); const [newBudget, setNewBudget] = useState({ month: '', category: '', amount: '' }); const [forecastMonths, setForecastMonths] = useState(3); const [csvParseError, setCsvParseError] = useState(''); // Load data from localStorage on component mount useEffect(() => { const savedTransactions = localStorage.getItem('transactions'); const savedBudgets = localStorage.getItem('budgets'); if (savedTransactions) { setTransactions(JSON.parse(savedTransactions)); } if (savedBudgets) { setBudgets(JSON.parse(savedBudgets)); } }, []); // Save data to localStorage when it changes useEffect(() => { localStorage.setItem('transactions', JSON.stringify(transactions)); }, [transactions]); useEffect(() => { localStorage.setItem('budgets', JSON.stringify(budgets)); }, [budgets]); // CSV file handling const handleFileUpload = (event) => { const file = event.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (e) => { try { const csvData = parseCSV(e.target.result); setTransactions([...transactions, ...csvData]); setCsvParseError(''); } catch (error) { setCsvParseError('Error parsing CSV file. Please check the format.'); console.error('CSV parsing error:', error); } }; reader.readAsText(file); } }; // Parse CSV data const parseCSV = (csvText) => { const lines = csvText.split('\n'); const headers = lines[0].split(',').map(header => header.trim()); // Check if required columns exist const dateIndex = headers.findIndex(h => h.toLowerCase().includes('date')); const amountIndex = headers.findIndex(h => h.toLowerCase().includes('amount')); const descriptionIndex = headers.findIndex(h => h.toLowerCase().includes('description')); if (dateIndex === -1 || amountIndex === -1 || descriptionIndex === -1) { throw new Error('CSV must contain date, amount, and description columns'); } const parsedData = []; for (let i = 1; i < lines.length; i++) { if (!lines[i].trim()) continue; const values = lines[i].split(',').map(value => value.trim()); if (values.length >= Math.max(dateIndex, amountIndex, descriptionIndex) + 1) { const transaction = { id: Date.now() + i, date: new Date(values[dateIndex]), amount: parseFloat(values[amountIndex].replace(/[^0-9.-]+/g, '')), description: values[descriptionIndex], category: guessCategory(values[descriptionIndex]) }; parsedData.push(transaction); } } return parsedData; }; // Guess category based on description const guessCategory = (description) => { description = description.toLowerCase(); if (description.includes('rent') || description.includes('mortgage')) return 'Housing'; if (description.includes('grocery') || description.includes('restaurant') || description.includes('food')) return 'Food'; if (description.includes('gas') || description.includes('uber') || description.includes('lyft') || description.includes('transit')) return 'Transportation'; if (description.includes('electric') || description.includes('water') || description.includes('internet') || description.includes('phone')) return 'Utilities'; if (description.includes('movie') || description.includes('netflix') || description.includes('spotify')) return 'Entertainment'; if (description.includes('doctor') || description.includes('pharmacy') || description.includes('medical')) return 'Healthcare'; if (description.includes('amazon') || description.includes('walmart') || description.includes('target')) return 'Shopping'; if (description.includes('loan') || description.includes('credit card')) return 'Debt'; if (description.includes('salary') || description.includes('paycheck') || description.includes('deposit')) return 'Income'; return 'Other'; }; // Update transaction category const updateTransactionCategory = (id, category) => { setTransactions(transactions.map(transaction => transaction.id === id ? { ...transaction, category } : transaction )); }; // Delete transaction const deleteTransaction = (id) => { setTransactions(transactions.filter(transaction => transaction.id !== id)); }; // Add or update budget const handleAddBudget = () => { if (!newBudget.month || !newBudget.category || !newBudget.amount) return; setBudgets({ ...budgets, [newBudget.month]: { ...(budgets[newBudget.month] || {}), [newBudget.category]: parseFloat(newBudget.amount) } }); setNewBudget({ month: '', category: '', amount: '' }); setIsAddBudgetModalOpen(false); }; // Calculate spending by category for a specific month const getSpendingByCategory = (month) => { const filteredTransactions = transactions.filter(transaction => { const transactionMonth = `${transaction.date.getFullYear()}-${String(transaction.date.getMonth() + 1).padStart(2, '0')}`; return transactionMonth === month && transaction.amount < 0; }); const spending = {}; categories.forEach(category => { spending[category] = 0; }); filteredTransactions.forEach(transaction => { spending[transaction.category] = (spending[transaction.category] || 0) + Math.abs(transaction.amount); }); return spending; }; // Calculate total income for a specific month const getTotalIncome = (month) => { const filteredTransactions = transactions.filter(transaction => { const transactionMonth = `${transaction.date.getFullYear()}-${String(transaction.date.getMonth() + 1).padStart(2, '0')}`; return transactionMonth === month && transaction.amount > 0; }); return filteredTransactions.reduce((sum, transaction) => sum + transaction.amount, 0); }; // Calculate total expenses for a specific month const getTotalExpenses = (month) => { const filteredTransactions = transactions.filter(transaction => { const transactionMonth = `${transaction.date.getFullYear()}-${String(transaction.date.getMonth() + 1).padStart(2, '0')}`; return transactionMonth === month && transaction.amount < 0; }); return Math.abs(filteredTransactions.reduce((sum, transaction) => sum + transaction.amount, 0)); }; // Get current month in YYYY-MM format const getCurrentMonth = () => { const now = new Date(); return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; }; // Get list of months with transactions const getMonthsWithTransactions = () => { const months = new Set(); transactions.forEach(transaction => { const month = `${transaction.date.getFullYear()}-${String(transaction.date.getMonth() + 1).padStart(2, '0')}`; months.add(month); }); return Array.from(months).sort(); }; // Generate forecast data const generateForecast = () => { const months = getMonthsWithTransactions(); if (months.length === 0) return []; const forecastData = []; const historicalData = {}; // Calculate average spending by category categories.forEach(category => { let totalSpending = 0; let monthCount = 0; months.forEach(month => { const spending = getSpendingByCategory(month); if (spending[category] > 0) { totalSpending += spending[category]; monthCount++; } }); historicalData[category] = monthCount > 0 ? totalSpending / monthCount : 0; }); // Generate forecast for future months const lastMonth = months[months.length - 1]; const [lastYear, lastMonthNum] = lastMonth.split('-').map(num => parseInt(num)); for (let i = 1; i <= forecastMonths; i++) { let forecastMonth = lastMonthNum + i; let forecastYear = lastYear; if (forecastMonth > 12) { forecastMonth -= 12; forecastYear += 1; } const monthStr = `${forecastYear}-${String(forecastMonth).padStart(2, '0')}`; const forecastSpending = { ...historicalData }; forecastData.push({ month: monthStr, ...forecastSpending }); } return forecastData; }; // Format currency const formatCurrency = (amount) => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount); }; // Format date const formatDate = (date) => { return new Date(date).toLocaleDateString(); }; // Dashboard data const currentMonth = getCurrentMonth(); const monthlySpending = getSpendingByCategory(currentMonth); const totalIncome = getTotalIncome(currentMonth); const totalExpenses = getTotalExpenses(currentMonth); const netSavings = totalIncome - totalExpenses; // Prepare data for charts const pieChartData = Object.entries(monthlySpending) .filter(([_, value]) => value > 0) .map(([name, value]) => ({ name, value })); const barChartData = getMonthsWithTransactions().map(month => ({ month, income: getTotalIncome(month), expenses: getTotalExpenses(month) })); const COLORS = [ '#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#8884D8', '#82CA9D', '#FF6B6B', '#6A7FDB', '#61DAFB', '#F06292', '#BA68C8' ]; // Budget progress calculation const getBudgetProgress = (category) => { const budget = budgets[currentMonth]?.[category] || 0; const spent = monthlySpending[category] || 0; if (budget === 0) return 0; return Math.min((spent / budget) * 100, 100); }; return (
{/* Mobile menu button */}
{/* Sidebar */}

Finance Manager

Import Transactions

Upload a CSV file with your transaction data

{csvParseError && (

{csvParseError}

)}
{/* Main content */}
{/* Dashboard */} {activeTab === 'dashboard' && (

Financial Dashboard

{/* Summary cards */}

Income

{formatCurrency(totalIncome)}

Current month

Expenses

{formatCurrency(totalExpenses)}

Current month

Net Savings

= 0 ? 'text-teal-600' : 'text-red-500'}`}> {formatCurrency(netSavings)}

Current month

{/* Charts */}
{/* Spending by category */}

Spending by Category

{pieChartData.length > 0 ? (
`${name} ${(percent * 100).toFixed(0)}%`} > {pieChartData.map((entry, index) => ( ))} formatCurrency(value)} />
) : (
No spending data available
)}
{/* Income vs Expenses */}

Income vs Expenses

{barChartData.length > 0 ? (
formatCurrency(value)} />
) : (
No income/expense data available
)}
{/* Budget progress */}

Budget Progress

{Object.keys(budgets[currentMonth] || {}).length > 0 ? (
{Object.entries(budgets[currentMonth] || {}).map(([category, budget]) => (
{category} {formatCurrency(monthlySpending[category] || 0)} / {formatCurrency(budget)}
= 100 ? 'bg-red-500' : 'bg-teal-600'}`} style={{ width: `${getBudgetProgress(category)}%` }} >
))}
) : (

No budgets set for this month

)}
)} {/* Transactions */} {activeTab === 'transactions' && (

Transactions

{transactions.length > 0 ? (
{transactions.sort((a, b) => new Date(b.date) - new Date(a.date)).map((transaction) => ( ))}
Date Description Amount Category Actions
{formatDate(transaction.date)} {transaction.description} = 0 ? 'text-green-600' : 'text-red-500'}`}> {formatCurrency(transaction.amount)}
) : (

No transactions yet

)}
)} {/* Budgets */} {activeTab === 'budgets' && (

Budgets

{Object.keys(budgets).length > 0 ? (
{Object.entries(budgets).sort().map(([month, monthBudgets]) => (

{month}

{Object.entries(monthBudgets).map(([category, amount]) => { const spent = month === currentMonth ? (monthlySpending[category] || 0) : 0; const progress = amount > 0 ? Math.min((spent / amount) * 100, 100) : 0; return (
{category} {month === currentMonth ? `${formatCurrency(spent)} / ` : ''} {formatCurrency(amount)}
{month === currentMonth && (
= 100 ? 'bg-red-500' : 'bg-teal-600'}`} style={{ width: `${progress}%` }} >
)}
); })}
))}
) : (

No budgets set yet

)}
)} {/* Forecast */} {activeTab === 'forecast' && (

Expense Forecast

Future Expenses Projection

{transactions.length > 0 ? (
({ month: item.month, expenses: item.expenses, type: 'Historical' })), ...generateForecast().map(item => ({ month: item.month, expenses: Object.values(item).reduce((sum, val) => typeof val === 'number' ? sum + val : sum, 0 ) - item.month.length, // Subtract the length of month string which was counted in reduce type: 'Forecast' })) ]} margin={{ top: 5, right: 30, left: 20, bottom: 5 }} > formatCurrency(value)} />
) : (

Not enough data for forecasting. Please import transactions first.

)}

Forecast Details

{generateForecast().length > 0 ? (
{categories.map(category => ( ))} {generateForecast().map((forecast) => ( {categories.map(category => ( ))} ))}
Month{category}Total
{forecast.month} {formatCurrency(forecast[category] || 0)} {formatCurrency( Object.entries(forecast) .filter(([key]) => key !== 'month') .reduce((sum, [_, value]) => sum + value, 0) )}
) : (

Not enough data for detailed forecasting

)}
)}
{/* Add Budget Modal */} {isAddBudgetModalOpen && (

Add Budget

setNewBudget({ ...newBudget, month: e.target.value })} className="w-full border border-gray-300 rounded-md px-3 py-2" />
setNewBudget({ ...newBudget, amount: e.target.value })} className="w-full border border-gray-300 rounded-md px-3 py-2" min="0" step="0.01" />
)}
); }