Bank statement converter downloader
import React, { useState } from 'react';
import { Upload, Download, FileText, TrendingUp, DollarSign, Calendar, Tag, Filter, X } from 'lucide-react';
const BankStatementConverter = () => {
const [statements, setStatements] = useState([]);
const [processing, setProcessing] = useState(false);
const [view, setView] = useState('upload');
const [filterCategory, setFilterCategory] = useState('all');
const [dateRange, setDateRange] = useState({ start: '', end: '' });
const [error, setError] = useState('');
const categories = {
'Food & Dining': ['restaurant', 'cafe', 'food', 'grocery', 'uber eats', 'doordash', 'grubhub', 'starbucks', 'mcdonald', 'pizza'],
'Shopping': ['amazon', 'walmart', 'target', 'store', 'shop', 'retail', 'ebay', 'etsy'],
'Transportation': ['uber', 'lyft', 'gas', 'fuel', 'parking', 'transit', 'shell', 'chevron', 'exxon'],
'Bills & Utilities': ['electric', 'water', 'internet', 'phone', 'utilities', 'insurance', 'verizon', 'att'],
'Entertainment': ['netflix', 'spotify', 'movie', 'theater', 'game', 'steam', 'hulu', 'disney'],
'Healthcare': ['pharmacy', 'doctor', 'medical', 'health', 'cvs', 'walgreens', 'hospital'],
'Transfer': ['transfer', 'payment', 'zelle', 'venmo', 'paypal', 'cashapp'],
'Income': ['deposit', 'salary', 'payroll', 'income', 'refund', 'direct dep'],
'Other': []
};
const categorizeTransaction = (description) => {
const desc = description.toLowerCase();
for (const [category, keywords] of Object.entries(categories)) {
if (keywords.some(keyword => desc.includes(keyword))) {
return category;
}
}
return 'Other';
};
const parseCSVText = (text) => {
const lines = text.split('\n').filter(line => line.trim());
if (lines.length === 0) return [];
const headers = lines[0].split(',').map(h => h.trim().replace(/"/g, ''));
const transactions = [];
for (let i = 1; i < lines.length; i++) {
const values = lines[i].split(',').map(v => v.trim().replace(/"/g, ''));
const row = {};
headers.forEach((header, index) => {
row[header] = values[index] || '';
});
const date = row.Date || row.date || row.DATE || row['Transaction Date'] || row['Posted Date'] || '';
const description = row.Description || row.description || row.DESCRIPTION || row.Memo || row.memo || row.Merchant || '';
const amountStr = (row.Amount || row.amount || row.AMOUNT || row.Debit || row.Credit || '0').replace(/[$,]/g, '');
const amount = parseFloat(amountStr) || 0;
if (date && description) {
transactions.push({
id: `txn_${Date.now()}_${i}`,
date: date,
description: description,
amount: amount,
balance: row.Balance || row.balance || row.BALANCE || '',
type: amount >= 0 ? 'credit' : 'debit',
category: categorizeTransaction(description)
});
}
}
return transactions;
};
const handleFileUpload = async (e) => {
const files = Array.from(e.target.files);
if (files.length === 0) return;
setProcessing(true);
setError('');
try {
const allTransactions = [];
for (const file of files) {
let transactions = [];
if (file.name.endsWith('.csv') || file.type === 'text/csv') {
const text = await file.text();
transactions = parseCSVText(text);
} else {
setError(`File format not supported: ${file.name}. Please upload CSV files.`);
continue;
}
allTransactions.push(...transactions);
}
if (allTransactions.length === 0) {
setError('No valid transactions found. Please check your file format.');
setProcessing(false);
return;
}
setStatements(allTransactions);
setView('analysis');
} catch (error) {
console.error('Error processing files:', error);
setError('Error processing files. Please check the format and try again.');
} finally {
setProcessing(false);
}
};
const downloadCSV = () => {
const headers = ['Date', 'Description', 'Category', 'Amount', 'Type', 'Balance'];
const rows = statements.map(s => [
s.date,
`"${s.description}"`,
s.category,
s.amount,
s.type,
s.balance
]);
const csv = [headers.join(','), ...rows.map(r => r.join(','))].join('\n');
const blob = new Blob([csv], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `bank_statement_categorized_${Date.now()}.csv`;
a.click();
URL.revokeObjectURL(url);
};
const getFilteredStatements = () => {
let filtered = statements;
if (filterCategory !== 'all') {
filtered = filtered.filter(s => s.category === filterCategory);
}
if (dateRange.start) {
filtered = filtered.filter(s => {
try {
return new Date(s.date) >= new Date(dateRange.start);
} catch {
return true;
}
});
}
if (dateRange.end) {
filtered = filtered.filter(s => {
try {
return new Date(s.date) <= new Date(dateRange.end);
} catch {
return true;
}
});
}
return filtered;
};
const calculateStats = () => {
const filtered = getFilteredStatements();
const totalIncome = filtered.filter(s => s.type === 'credit').reduce((sum, s) => sum + s.amount, 0);
const totalExpenses = filtered.filter(s => s.type === 'debit').reduce((sum, s) => sum + Math.abs(s.amount), 0);
const categoryTotals = {};
filtered.forEach(s => {
if (s.type === 'debit') {
categoryTotals[s.category] = (categoryTotals[s.category] || 0) + Math.abs(s.amount);
}
});
return { totalIncome, totalExpenses, categoryTotals, netChange: totalIncome - totalExpenses };
};
const stats = statements.length > 0 ? calculateStats() : null;
const filteredStatements = getFilteredStatements();
return (
);
};
export default BankStatementConverter;
{/* Header */}
{/* Main Content */}
{view === 'upload' && (
)}
{view === 'analysis' && statements.length > 0 && (
Bank Statement Converter
Upload CSV files to analyze and categorize your transactions
Upload Your Bank Statement
Supports CSV format
{error && (
{error}
)}
Features:
-
Automatic transaction categorization into 9 categories -
Spending analytics and income tracking -
Export categorized data to CSV -
Filter by category and date range
CSV Format Tips:
Your CSV should have columns like: Date, Description, Amount (or columns named date, description, amount, etc.)
{/* Stats Cards */}
{/* Filters */}
{(filterCategory !== 'all' || dateRange.start || dateRange.end) && (
)}
{/* Category Breakdown */}
{Object.keys(stats.categoryTotals).length > 0 && (
)}
{/* Export Buttons */}
{/* Transactions Table */}
{filteredStatements.length === 0 && (
)}
Total Income
${stats.totalIncome.toFixed(2)}
{statements.filter(s => s.type === 'credit').length} transactions
Total Expenses
${stats.totalExpenses.toFixed(2)}
{statements.filter(s => s.type === 'debit').length} transactions
Net Change
${stats.netChange.toFixed(2)}
{statements.length} total transactions
setDateRange({...dateRange, start: e.target.value})}
className="w-full border rounded-lg px-4 py-2"
/>
setDateRange({...dateRange, end: e.target.value})}
className="w-full border rounded-lg px-4 py-2"
/>
Spending by Category
{Object.entries(stats.categoryTotals)
.sort(([,a], [,b]) => b - a)
.map(([category, amount]) => (
))}
{category}
${amount.toFixed(2)}
{((amount / stats.totalExpenses) * 100).toFixed(1)}%
| Date | Description | Category | Amount |
|---|---|---|---|
| {txn.date} | {txn.description} | {txn.category} | {txn.type === 'credit' ? '+' : '-'}${Math.abs(txn.amount).toFixed(2)} |
No transactions match your filters
)}
Comments
Post a Comment