import React, { useState, useEffect } from 'react';
import { Wallet, Plus, Minus, DollarSign, TrendingUp, TrendingDown, CreditCard, PiggyBank, AlertCircle, Eye, EyeOff, Trash2, Edit3, Check, X } from 'lucide-react';
const WalletApp = () => {
// State management
const [balance, setBalance] = useState(0);
const [transactions, setTransactions] = useState([]);
const [showBalance, setShowBalance] = useState(true);
const [activeTab, setActiveTab] = useState('overview');
const [newTransaction, setNewTransaction] = useState({
type: 'expense',
amount: '',
description: '',
category: 'other'
});
const [editingTransaction, setEditingTransaction] = useState(null);
// Categories for transactions
const categories = {
income: ['salary', 'freelance', 'investment', 'gift', 'other'],
expense: ['food', 'transport', 'entertainment', 'utilities', 'shopping', 'healthcare', 'other']
};
// Load data from memory on component mount
useEffect(() => {
const savedData = JSON.parse(window.walletData || '{"balance": 0, "transactions": []}');
setBalance(savedData.balance || 0);
setTransactions(savedData.transactions || []);
}, []);
// Save data to memory whenever state changes
useEffect(() => {
const dataToSave = { balance, transactions };
window.walletData = JSON.stringify(dataToSave);
}, [balance, transactions]);
// Add new transaction
const addTransaction = () => {
if (!newTransaction.amount || !newTransaction.description) {
alert('Please fill in all fields');
return;
}
const amount = parseFloat(newTransaction.amount);
const transaction = {
id: Date.now(),
...newTransaction,
amount,
date: new Date().toISOString()
};
setTransactions(prev => [transaction, ...prev]);
// Update balance
const balanceChange = newTransaction.type === 'income' ? amount : -amount;
setBalance(prev => prev + balanceChange);
// Reset form
setNewTransaction({
type: 'expense',
amount: '',
description: '',
category: 'other'
});
};
// Delete transaction
const deleteTransaction = (id) => {
const transaction = transactions.find(t => t.id === id);
if (!transaction) return;
setTransactions(prev => prev.filter(t => t.id !== id));
// Adjust balance
const balanceChange = transaction.type === 'income' ? -transaction.amount : transaction.amount;
setBalance(prev => prev + balanceChange);
};
// Edit transaction
const updateTransaction = (id, updatedTransaction) => {
const oldTransaction = transactions.find(t => t.id === id);
if (!oldTransaction) return;
// Revert old transaction from balance
const oldBalanceChange = oldTransaction.type === 'income' ? -oldTransaction.amount : oldTransaction.amount;
// Apply new transaction to balance
const newBalanceChange = updatedTransaction.type === 'income' ? updatedTransaction.amount : -updatedTransaction.amount;
setBalance(prev => prev + oldBalanceChange + newBalanceChange);
setTransactions(prev => prev.map(t => t.id === id ? { ...updatedTransaction, id, date: t.date } : t));
setEditingTransaction(null);
};
// Calculate statistics
const thisMonthTransactions = transactions.filter(t => {
const transactionDate = new Date(t.date);
const now = new Date();
return transactionDate.getMonth() === now.getMonth() && transactionDate.getFullYear() === now.getFullYear();
});
const thisMonthIncome = thisMonthTransactions
.filter(t => t.type === 'income')
.reduce((sum, t) => sum + t.amount, 0);
const thisMonthExpenses = thisMonthTransactions
.filter(t => t.type === 'expense')
.reduce((sum, t) => sum + t.amount, 0);
// Format currency
const formatCurrency = (amount) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(amount);
};
// Format date
const formatDate = (dateString) => {
return new Date(dateString).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
};
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
<div className="max-w-4xl mx-auto p-6">
{/* Header */}
<div className="text-center mb-8">
<div className="flex items-center justify-center mb-4">
<Wallet className="w-12 h-12 text-indigo-600 mr-3" />
<h1 className="text-4xl font-bold text-gray-800">Personal Wallet</h1>
</div>
<p className="text-gray-600">Your finances, managed locally and securely</p>
</div>
{/* Balance Card */}
<div className="bg-white rounded-2xl shadow-xl p-8 mb-8 border border-gray-200">
<div className="flex items-center justify-between mb-4">
<h2 className="text-2xl font-semibold text-gray-800">Current Balance</h2>
<button
onClick={() => setShowBalance(!showBalance)}
className="p-2 hover:bg-gray-100 rounded-full transition-colors"
>
{showBalance ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
</button>
</div>
<div className="text-5xl font-bold mb-4">
{showBalance ? (
<span className={balance >= 0 ? 'text-green-600' : 'text-red-600'}>
{formatCurrency(balance)}
</span>
) : (
<span className="text-gray-400">••••••</span>
)}
</div>
{/* Monthly Stats */}
<div className="grid grid-cols-2 gap-4 mt-6">
<div className="bg-green-50 p-4 rounded-lg">
<div className="flex items-center">
<TrendingUp className="w-5 h-5 text-green-600 mr-2" />
<span className="text-sm text-green-600 font-medium">This Month Income</span>
</div>
<div className="text-2xl font-bold text-green-700">
{formatCurrency(thisMonthIncome)}
</div>
</div>
<div className="bg-red-50 p-4 rounded-lg">
<div className="flex items-center">
<TrendingDown className="w-5 h-5 text-red-600 mr-2" />
<span className="text-sm text-red-600 font-medium">This Month Expenses</span>
</div>
<div className="text-2xl font-bold text-red-700">
{formatCurrency(thisMonthExpenses)}
</div>
</div>
</div>
</div>
{/* Navigation Tabs */}
<div className="flex bg-white rounded-xl shadow-lg mb-8 p-2">
<button
onClick={() => setActiveTab('overview')}
className={`flex-1 py-3 px-6 rounded-lg font-medium transition-all ${
activeTab === 'overview'
? 'bg-indigo-600 text-white shadow-md'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
<DollarSign className="w-5 h-5 inline mr-2" />
Overview
</button>
<button
onClick={() => setActiveTab('add')}
className={`flex-1 py-3 px-6 rounded-lg font-medium transition-all ${
activeTab === 'add'
? 'bg-indigo-600 text-white shadow-md'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
<Plus className="w-5 h-5 inline mr-2" />
Add Transaction
</button>
</div>
{/* Content based on active tab */}
{activeTab === 'overview' && (
<div className="space-y-6">
{/* Recent Transactions */}
<div className="bg-white rounded-2xl shadow-lg p-6">
<h3 className="text-xl font-semibold text-gray-800 mb-6 flex items-center">
<CreditCard className="w-6 h-6 mr-2 text-indigo-600" />
Recent Transactions
</h3>
{transactions.length === 0 ? (
<div className="text-center py-12">
<PiggyBank className="w-16 h-16 text-gray-300 mx-auto mb-4" />
<p className="text-gray-500">No transactions yet. Add your first transaction!</p>
</div>
) : (
<div className="space-y-3">
{transactions.slice(0, 10).map((transaction) => (
<div key={transaction.id} className="flex items-center justify-between p-4 bg-gray-50 rounded-xl hover:bg-gray-100 transition-colors">
{editingTransaction === transaction.id ? (
<EditTransactionForm
transaction={transaction}
categories={categories}
onSave={(updatedTransaction) => updateTransaction(transaction.id, updatedTransaction)}
onCancel={() => setEditingTransaction(null)}
/>
) : (
<>
<div className="flex items-center flex-1">
<div className={`w-10 h-10 rounded-full flex items-center justify-center mr-4 ${
transaction.type === 'income' ? 'bg-green-100' : 'bg-red-100'
}`}>
{transaction.type === 'income' ? (
<Plus className="w-5 h-5 text-green-600" />
) : (
<Minus className="w-5 h-5 text-red-600" />
)}
</div>
<div>
<div className="font-medium text-gray-800">{transaction.description}</div>
<div className="text-sm text-gray-500 capitalize">
{transaction.category} • {formatDate(transaction.date)}
</div>
</div>
</div>
<div className="flex items-center">
<div className={`text-lg font-semibold mr-3 ${
transaction.type === 'income' ? 'text-green-600' : 'text-red-600'
}`}>
{transaction.type === 'income' ? '+' : '-'}{formatCurrency(transaction.amount)}
</div>
<button
onClick={() => setEditingTransaction(transaction.id)}
className="p-2 text-gray-400 hover:text-indigo-600 hover:bg-indigo-50 rounded-full transition-colors mr-1"
>
<Edit3 className="w-4 h-4" />
</button>
<button
onClick={() => deleteTransaction(transaction.id)}
className="p-2 text-gray-400 hover:text-red-600 hover:bg-red-50 rounded-full transition-colors"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</>
)}
</div>
))}
</div>
)}
</div>
</div>
)}
{activeTab === 'add' && (
<div className="bg-white rounded-2xl shadow-lg p-8">
<h3 className="text-2xl font-semibold text-gray-800 mb-6">Add New Transaction</h3>
<div className="space-y-6">
{/* Transaction Type */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">Transaction Type</label>
<div className="flex space-x-4">
<button
onClick={() => setNewTransaction({...newTransaction, type: 'income'})}
className={`flex-1 py-3 px-6 rounded-xl font-medium transition-all ${
newTransaction.type === 'income'
? 'bg-green-600 text-white shadow-md'
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
}`}
>
<Plus className="w-5 h-5 inline mr-2" />
Income
</button>
<button
onClick={() => setNewTransaction({...newTransaction, type: 'expense'})}
className={`flex-1 py-3 px-6 rounded-xl font-medium transition-all ${
newTransaction.type === 'expense'
? 'bg-red-600 text-white shadow-md'
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
}`}
>
<Minus className="w-5 h-5 inline mr-2" />
Expense
</button>
</div>
</div>
{/* Amount */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Amount</label>
<div className="relative">
<DollarSign className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
<input
type="number"
value={newTransaction.amount}
onChange={(e) => setNewTransaction({...newTransaction, amount: e.target.value})}
className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
placeholder="0.00"
step="0.01"
min="0"
/>
</div>
</div>
{/* Description */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Description</label>
<input
type="text"
value={newTransaction.description}
onChange={(e) => setNewTransaction({...newTransaction, description: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
placeholder="What was this transaction for?"
/>
</div>
{/* Category */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Category</label>
<select
value={newTransaction.category}
onChange={(e) => setNewTransaction({...newTransaction, category: e.target.value})}
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent capitalize"
>
{categories[newTransaction.type].map(category => (
<option key={category} value={category} className="capitalize">
{category}
</option>
))}
</select>
</div>
{/* Submit Button */}
<button
onClick={addTransaction}
className="w-full py-4 bg-indigo-600 text-white rounded-xl font-semibold hover:bg-indigo-700 transition-colors shadow-lg hover:shadow-xl"
>
Add Transaction
</button>
</div>
</div>
)}
{/* Footer */}
<div className="text-center mt-12 text-gray-500">
<div className="flex items-center justify-center mb-2">
<AlertCircle className="w-4 h-4 mr-2" />
<span className="text-sm">Your data is stored locally in your browser</span>
</div>
<p className="text-xs">No servers, no tracking, complete privacy</p>
</div>
</div>
</div>
);
};
// Edit Transaction Form Component
const EditTransactionForm = ({ transaction, categories, onSave, onCancel }) => {
const [editData, setEditData] = useState({
type: transaction.type,
amount: transaction.amount.toString(),
description: transaction.description,
category: transaction.category
});
const handleSave = () => {
if (!editData.amount || !editData.description) {
alert('Please fill in all fields');
return;
}
onSave({
...editData,
amount: parseFloat(editData.amount)
});
};
return (
<div className="w-full space-y-3">
<div className="grid grid-cols-2 gap-3">
<select
value={editData.type}
onChange={(e) => setEditData({...editData, type: e.target.value})}
className="px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-1 focus:ring-indigo-500"
>
<option value="income">Income</option>
<option value="expense">Expense</option>
</select>
<input
type="number"
value={editData.amount}
onChange={(e) => setEditData({...editData, amount: e.target.value})}
className="px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-1 focus:ring-indigo-500"
step="0.01"
min="0"
/>
</div>
<input
type="text"
value={editData.description}
onChange={(e) => setEditData({...editData, description: e.target.value})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-1 focus:ring-indigo-500"
/>
<div className="flex justify-between items-center">
<select
value={editData.category}
onChange={(e) => setEditData({...editData, category: e.target.value})}
className="px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-1 focus:ring-indigo-500 capitalize"
>
{categories[editData.type].map(category => (
<option key={category} value={category} className="capitalize">
{category}
</option>
))}
</select>
<div className="flex space-x-2">
<button
onClick={handleSave}
className="p-2 text-green-600 hover:bg-green-50 rounded-full transition-colors"
>
<Check className="w-4 h-4" />
</button>
<button
onClick={onCancel}
className="p-2 text-red-600 hover:bg-red-50 rounded-full transition-colors"
>
<X className="w-4 h-4" />
</button>
</div>
</div>
</div>
);
};
export default WalletApp;