import React, { useState, useEffect, useMemo, useRef } from 'react'; import { initializeApp } from 'firebase/app'; import { getFirestore, collection, doc, setDoc, onSnapshot, query, addDoc, deleteDoc, updateDoc } from 'firebase/firestore'; import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } from 'firebase/auth'; import { LayoutDashboard, TrendingUp, TrendingDown, Settings, PlusCircle, Trash2, Edit3, X, Wallet, HandCoins, History, Cloud, User } from 'lucide-react'; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler } from 'chart.js'; import { Line } from 'react-chartjs-2'; // Register ChartJS ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler ); // --- Firebase Config --- const firebaseConfig = JSON.parse(__firebase_config); const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = typeof __app_id !== 'undefined' ? __app_id : 'fintrack-pro-v1'; export default function App() { const [user, setUser] = useState(null); const [activeTab, setActiveTab] = useState('dashboard'); const [transactions, setTransactions] = useState([]); const [categories, setCategories] = useState([]); const [isModalOpen, setIsModalOpen] = useState(false); const [modalMode, setModalMode] = useState('transaction'); const [editingItem, setEditingItem] = useState(null); const [dateFilter, setDateFilter] = useState('all'); const [formData, setFormData] = useState({ amount: '', description: '', categoryId: '', type: 'expense', isRepayment: false, date: new Date().toISOString().split('T')[0] }); const [catFormData, setCatFormData] = useState({ name: '', type: 'expense', color: '#6366f1' }); // --- Auth Logic --- useEffect(() => { const initAuth = async () => { try { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(auth, __initial_auth_token); } else { await signInAnonymously(auth); } } catch (err) { console.error("Auth Error:", err); } }; initAuth(); const unsubscribe = onAuthStateChanged(auth, setUser); return () => unsubscribe(); }, []); // --- Data Sync --- useEffect(() => { if (!user) return; // Sync Categories const catCol = collection(db, 'artifacts', appId, 'users', user.uid, 'categories'); const unsubCats = onSnapshot(catCol, (snapshot) => { const cats = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); // Initialize default categories if none exist if (cats.length === 0) { const defaults = [ { name: 'Salary', type: 'income', color: '#10b981' }, { name: 'Food', type: 'expense', color: '#ef4444' }, { name: 'Rent', type: 'expense', color: '#f59e0b' }, { name: 'Lending', type: 'debt', color: '#3b82f6' }, { name: 'Borrowing', type: 'debt', color: '#8b5cf6' }, ]; defaults.forEach(d => addDoc(catCol, d)); } setCategories(cats); }, (err) => console.error("Firestore Error:", err)); // Sync Transactions const txCol = collection(db, 'artifacts', appId, 'users', user.uid, 'transactions'); const unsubTxs = onSnapshot(txCol, (snapshot) => { const txs = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); setTransactions(txs); }, (err) => console.error("Firestore Error:", err)); return () => { unsubCats(); unsubTxs(); }; }, [user]); // --- Calculations --- const filteredTransactions = useMemo(() => { const sorted = [...transactions].sort((a, b) => new Date(b.date) - new Date(a.date)); if (dateFilter === 'all') return sorted; const now = new Date(); return sorted.filter(tx => { const txDate = new Date(tx.date); if (dateFilter === 'week') { const weekAgo = new Date(); weekAgo.setDate(now.getDate() - 7); return txDate >= weekAgo; } return true; }); }, [transactions, dateFilter]); const totals = useMemo(() => { return filteredTransactions.reduce((acc, curr) => { const cat = categories.find(c => c.id === curr.categoryId); const val = parseFloat(curr.amount) || 0; const type = curr.type || cat?.type; if (type === 'income') acc.income += val; else if (type === 'expense') acc.expense += val; else if (type === 'debt') { const isBorrowing = cat?.name.toLowerCase().includes('borrow'); if (curr.isRepayment) { if (isBorrowing) acc.debt -= val; else acc.lent -= val; } else { if (isBorrowing) acc.debt += val; else acc.lent += val; } } return acc; }, { income: 0, expense: 0, debt: 0, lent: 0 }); }, [filteredTransactions, categories]); // --- Chart Data --- const chartData = useMemo(() => { const sorted = [...filteredTransactions].sort((a, b) => new Date(a.date) - new Date(b.date)); const labels = [...new Set(sorted.map(t => t.date))]; const incomeData = labels.map(date => sorted.filter(t => t.date === date && (t.type === 'income')).reduce((s, c) => s + c.amount, 0) ); const expenseData = labels.map(date => sorted.filter(t => t.date === date && (t.type === 'expense')).reduce((s, c) => s + c.amount, 0) ); return { labels, datasets: [ { label: 'Income', data: incomeData, borderColor: '#10b981', backgroundColor: 'rgba(16, 185, 129, 0.1)', fill: true, tension: 0.4 }, { label: 'Expenses', data: expenseData, borderColor: '#ef4444', backgroundColor: 'rgba(239, 68, 68, 0.1)', fill: true, tension: 0.4 } ] }; }, [filteredTransactions]); // --- Actions --- const handleTxSubmit = async (e) => { e.preventDefault(); if (!user) return; const txData = { ...formData, amount: parseFloat(formData.amount) }; const txCol = collection(db, 'artifacts', appId, 'users', user.uid, 'transactions'); if (editingItem) { await updateDoc(doc(db, 'artifacts', appId, 'users', user.uid, 'transactions', editingItem.id), txData); } else { await addDoc(txCol, txData); } setIsModalOpen(false); }; const handleCatSubmit = async (e) => { e.preventDefault(); if (!user) return; const catCol = collection(db, 'artifacts', appId, 'users', user.uid, 'categories'); if (editingItem) { await updateDoc(doc(db, 'artifacts', appId, 'users', user.uid, 'categories', editingItem.id), catFormData); } else { await addDoc(catCol, catFormData); } setIsModalOpen(false); }; const openModal = (mode, item = null) => { setModalMode(mode); setEditingItem(item); if (mode === 'transaction') { setFormData(item || { amount: '', description: '', type: 'expense', categoryId: categories.find(c => c.type === 'expense')?.id || '', isRepayment: false, date: new Date().toISOString().split('T')[0] }); } else { setCatFormData(item || { name: '', type: 'expense', color: '#6366f1' }); } setIsModalOpen(true); }; if (!user) return
Initializing Secure Session...
; return (
{/* Sidebar */} {/* Main content */}

{activeTab}

Manage your financial footprint in real-time.

{activeTab === 'dashboard' && (
{/* Stats */}
{[ { label: 'Balance', val: totals.income - totals.expense, color: 'bg-indigo-600', icon: Wallet }, { label: 'Income', val: totals.income, color: 'bg-emerald-500', icon: TrendingUp }, { label: 'Expenses', val: totals.expense, color: 'bg-rose-500', icon: TrendingDown }, { label: 'Money Lent', val: totals.lent, color: 'bg-blue-500', icon: HandCoins }, ].map(stat => (

{stat.label}

₹{stat.val.toLocaleString()}

))}
{/* Chart */}

CASH FLOW

{['all', 'week'].map(f => ( ))}
{/* Debt View */}

Liability Portfolio

₹{totals.debt.toLocaleString()}

Total active borrowings

0 ? '60%' : '0%' }}>
Payback Status {totals.debt > 0 ? 'Action Required' : 'Cleared'}
{/* Recent Activity */}

LATEST RECORDS

{filteredTransactions.slice(0, 5).map(tx => { const cat = categories.find(c => c.id === tx.categoryId); return (
{tx.isRepayment ? : }

{tx.description}

{cat?.name} • {tx.date}

{tx.isRepayment ? '↓' : (tx.type === 'income' ? '+' : '-')} ₹{tx.amount.toLocaleString()}

); })}
)} {activeTab === 'ledger' && (
{filteredTransactions.map(tx => { const cat = categories.find(c => c.id === tx.categoryId); return ( ); })}
Date Description Category Amount Action
{tx.date} {tx.description} {tx.isRepayment && Repayment} {cat?.name} ₹{tx.amount.toLocaleString()}
)} {activeTab === 'settings' && (
{categories.map(cat => (

{cat.name}

{cat.type}

))}
)}
{/* Modal */} {isModalOpen && (

{editingItem ? 'Edit' : 'Record'} {modalMode}

{modalMode === 'transaction' ? (
{['income', 'expense', 'debt'].map(type => ( ))}
{formData.type === 'debt' && (
)}
setFormData({...formData, amount: e.target.value})} className="w-full bg-slate-50 border border-slate-100 rounded-2xl p-4 font-black text-xl outline-none focus:ring-2 focus:ring-indigo-100" placeholder="0.00" />
setFormData({...formData, description: e.target.value})} className="w-full bg-slate-50 border border-slate-100 rounded-2xl p-4 font-bold outline-none focus:ring-2 focus:ring-indigo-100" placeholder="Lunch, Salary, etc." />
setFormData({...formData, date: e.target.value})} className="w-full bg-slate-50 border border-slate-100 rounded-2xl p-4 font-bold outline-none text-sm" />
) : (
setCatFormData({...catFormData, name: e.target.value})} className="w-full bg-slate-50 border border-slate-100 rounded-2xl p-4 font-bold outline-none" placeholder="Rent, Bonus, etc." />
Theme Color setCatFormData({...catFormData, color: e.target.value})} className="h-10 w-20 rounded-xl cursor-pointer bg-transparent border-none" />
)}
)} {/* Mobile Nav */}
); }