import React, { useEffect, useState, useMemo } from 'react'; import { CondoService } from '../services/mockDb'; import { Payment, Family, Condo, CondoExpense } from '../types'; import { PieChart, Download, Calendar, Search, CreditCard, Banknote, Filter, ArrowUpRight, TrendingDown, Wallet, ArrowDownRight } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; const MONTH_NAMES = [ "Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre" ]; export const ReportsPage: React.FC = () => { const navigate = useNavigate(); const [loading, setLoading] = useState(true); const [payments, setPayments] = useState([]); // Entrate const [expenses, setExpenses] = useState([]); // Uscite const [families, setFamilies] = useState([]); const [activeCondo, setActiveCondo] = useState(undefined); const [availableYears, setAvailableYears] = useState([]); // Filters const [selectedYear, setSelectedYear] = useState(new Date().getFullYear()); const [selectedMonth, setSelectedMonth] = useState('ALL'); const [searchTerm, setSearchTerm] = useState(''); // Tab View const [activeTab, setActiveTab] = useState<'income' | 'expenses' | 'balance'>('balance'); useEffect(() => { const fetchData = async () => { setLoading(true); try { const settings = await CondoService.getSettings(); if (!settings.features.reports) { navigate('/'); return; } const condo = await CondoService.getActiveCondo(); setActiveCondo(condo); if (condo) { const [payList, expList, famList, years] = await Promise.all([ CondoService.getCondoPayments(condo.id), CondoService.getCondoExpenses(undefined), // Fetch ALL to filter locally or by year later CondoService.getFamilies(condo.id), CondoService.getAvailableYears() ]); setPayments(payList); setExpenses(expList); setFamilies(famList); setAvailableYears(years); if (years.length > 0 && !years.includes(selectedYear)) { setSelectedYear(years[0]); } } } catch (e) { console.error("Error loading reports", e); } finally { setLoading(false); } }; fetchData(); }, [navigate]); // --- FILTERED INCOME DATA (Payments from Families) --- const filteredIncome = useMemo(() => { return payments.filter(p => { const matchesYear = p.forYear === selectedYear; const matchesMonth = selectedMonth === 'ALL' || p.forMonth === selectedMonth; const familyName = families.find(f => f.id === p.familyId)?.name.toLowerCase() || ''; const matchesSearch = searchTerm === '' || familyName.includes(searchTerm.toLowerCase()) || (p.notes && p.notes.toLowerCase().includes(searchTerm.toLowerCase())); return matchesYear && matchesMonth && matchesSearch; }).map(p => { const isPayPal = p.notes && p.notes.includes("PayPal"); const family = families.find(f => f.id === p.familyId); return { ...p, familyName: family ? family.name : 'Sconosciuto', familyUnit: family ? family.unitNumber : '-', method: isPayPal ? 'PayPal' : 'Manuale' }; }); }, [payments, families, selectedYear, selectedMonth, searchTerm]); // --- FILTERED EXPENSES DATA (Payments to Suppliers) --- const filteredExpenses = useMemo(() => { return expenses.filter(e => { // Determine "Year" and "Month" for expense. Use Payment Date if exists, else CreatedAt const dateRef = e.paymentDate ? new Date(e.paymentDate) : new Date(e.createdAt); const expYear = dateRef.getFullYear(); const expMonth = dateRef.getMonth() + 1; // 1-12 const matchesYear = expYear === selectedYear; const matchesMonth = selectedMonth === 'ALL' || expMonth === selectedMonth; const matchesSearch = searchTerm === '' || e.description.toLowerCase().includes(searchTerm.toLowerCase()) || e.supplierName.toLowerCase().includes(searchTerm.toLowerCase()); return matchesYear && matchesMonth && matchesSearch; }); }, [expenses, selectedYear, selectedMonth, searchTerm]); // Statistics Calculation const stats = useMemo(() => { // Income Stats const totalIncome = filteredIncome.reduce((acc, curr) => acc + curr.amount, 0); const incomeCount = filteredIncome.length; // Expense Stats const totalExpenses = filteredExpenses.reduce((acc, curr) => acc + curr.amount, 0); const expenseCount = filteredExpenses.length; const paidExpenses = filteredExpenses.filter(e => e.status === 'PAID').reduce((acc, curr) => acc + curr.amount, 0); // Balance const balance = totalIncome - totalExpenses; return { totalIncome, incomeCount, totalExpenses, expenseCount, paidExpenses, balance }; }, [filteredIncome, filteredExpenses]); const getMonthLabel = (month: number) => { if (month === 13) return "Extra"; if (month >= 1 && month <= 12) return MONTH_NAMES[month - 1]; return "-"; }; const handleExportCSV = () => { if (!activeCondo) return; let headers: string[] = []; let rows: string[][] = []; let filename = ""; if (activeTab === 'income' || activeTab === 'balance') { // Export Income headers = ["Data Pagamento", "Famiglia", "Interno", "Mese Rif.", "Anno Rif.", "Importo", "Metodo", "Note"]; rows = filteredIncome.map(p => [ new Date(p.datePaid).toLocaleDateString(), p.familyName, p.familyUnit, getMonthLabel(p.forMonth), p.forYear.toString(), p.amount.toFixed(2), p.method, p.notes ? `"${p.notes.replace(/"/g, '""')}"` : "" ]); filename = `Report_Entrate_${activeCondo.name}_${selectedYear}.csv`; } else { // Export Expenses headers = ["Data", "Fornitore", "Descrizione", "Importo", "Stato", "Metodo", "Fattura"]; rows = filteredExpenses.map(e => [ e.paymentDate ? new Date(e.paymentDate).toLocaleDateString() : new Date(e.createdAt).toLocaleDateString(), e.supplierName, `"${e.description}"`, e.amount.toFixed(2), e.status, e.paymentMethod || '', e.invoiceNumber || '' ]); filename = `Report_Uscite_${activeCondo.name}_${selectedYear}.csv`; } const csvContent = "data:text/csv;charset=utf-8," + headers.join(",") + "\n" + rows.map(e => e.join(",")).join("\n"); const encodedUri = encodeURI(csvContent); const link = document.createElement("a"); link.setAttribute("href", encodedUri); link.setAttribute("download", filename); document.body.appendChild(link); link.click(); document.body.removeChild(link); }; if (loading) return
Caricamento report...
; return (

Reportistica & Bilancio

Analisi finanziaria per {activeCondo?.name}

{/* Filters Bar */}
setSearchTerm(e.target.value)} className="w-full border p-2 pl-9 rounded-lg text-slate-700 bg-slate-50" />
{/* TABS */}
{/* Overview Cards (Dynamic based on Tab) */}
{/* IN (Always shown or contextual) */}

Totale Entrate

€ {stats.totalIncome.toLocaleString('it-IT', { minimumFractionDigits: 2 })}

{/* OUT */}

Totale Uscite

€ {stats.totalExpenses.toLocaleString('it-IT', { minimumFractionDigits: 2 })}

{/* BALANCE */}

Saldo Periodo

= 0 ? 'text-blue-600' : 'text-red-600'}`}>€ {stats.balance.toLocaleString('it-IT', { minimumFractionDigits: 2 })}

Differenza Entrate - Uscite

{/* MAIN DATA TABLE */}

{activeTab === 'income' ? 'Dettaglio Versamenti Famiglie' : activeTab === 'expenses' ? 'Dettaglio Spese Fornitori' : 'Movimenti Recenti'}

{activeTab === 'expenses' ? ( // EXPENSES TABLE {filteredExpenses.length === 0 ? ( ) : ( filteredExpenses.map(e => ( )) )}
Data Fornitore Descrizione Stato Importo
Nessuna spesa trovata.
{e.paymentDate ? new Date(e.paymentDate).toLocaleDateString() : new Date(e.createdAt).toLocaleDateString()} {e.supplierName} {e.description} {e.status === 'PAID' ? 'Saldato' : e.status === 'SUSPENDED' ? 'Sospeso' : 'Insoluto'} € {e.amount.toFixed(2)}
) : ( // INCOME TABLE (Default for Balance too) {filteredIncome.length === 0 ? ( ) : ( filteredIncome.map((t) => ( )) )}
Data Famiglia Riferimento Metodo Note Importo
Nessuna transazione trovata.
{new Date(t.datePaid).toLocaleDateString()}
{t.familyName}
Int. {t.familyUnit}
{getMonthLabel(t.forMonth).substring(0, 5)} {t.forYear} {t.method === 'PayPal' ? : } {t.method} {t.notes || '-'} € {t.amount.toFixed(2)}
)}
); };