Update Reports.tsx
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
|
||||
import React, { useEffect, useState, useMemo } from 'react';
|
||||
import { CondoService } from '../services/mockDb';
|
||||
import { Payment, Family, Condo } from '../types';
|
||||
import { PieChart, Download, Calendar, Search, CreditCard, Banknote, Filter, ArrowUpRight } from 'lucide-react';
|
||||
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 = [
|
||||
@@ -13,7 +13,8 @@ const MONTH_NAMES = [
|
||||
export const ReportsPage: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [payments, setPayments] = useState<Payment[]>([]);
|
||||
const [payments, setPayments] = useState<Payment[]>([]); // Entrate
|
||||
const [expenses, setExpenses] = useState<CondoExpense[]>([]); // Uscite
|
||||
const [families, setFamilies] = useState<Family[]>([]);
|
||||
const [activeCondo, setActiveCondo] = useState<Condo | undefined>(undefined);
|
||||
const [availableYears, setAvailableYears] = useState<number[]>([]);
|
||||
@@ -23,6 +24,9 @@ export const ReportsPage: React.FC = () => {
|
||||
const [selectedMonth, setSelectedMonth] = useState<number | 'ALL'>('ALL');
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
|
||||
// Tab View
|
||||
const [activeTab, setActiveTab] = useState<'income' | 'expenses' | 'balance'>('balance');
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
@@ -37,12 +41,14 @@ export const ReportsPage: React.FC = () => {
|
||||
setActiveCondo(condo);
|
||||
|
||||
if (condo) {
|
||||
const [payList, famList, years] = await Promise.all([
|
||||
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)) {
|
||||
@@ -58,8 +64,8 @@ export const ReportsPage: React.FC = () => {
|
||||
fetchData();
|
||||
}, [navigate]);
|
||||
|
||||
// Filter Logic
|
||||
const filteredData = useMemo(() => {
|
||||
// --- 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;
|
||||
@@ -82,15 +88,41 @@ export const ReportsPage: React.FC = () => {
|
||||
});
|
||||
}, [payments, families, selectedYear, selectedMonth, searchTerm]);
|
||||
|
||||
// Statistics
|
||||
const stats = useMemo(() => {
|
||||
const totalAmount = filteredData.reduce((acc, curr) => acc + curr.amount, 0);
|
||||
const paypalAmount = filteredData.filter(p => p.method === 'PayPal').reduce((acc, curr) => acc + curr.amount, 0);
|
||||
const manualAmount = filteredData.filter(p => p.method === 'Manuale').reduce((acc, curr) => acc + curr.amount, 0);
|
||||
const count = filteredData.length;
|
||||
// --- 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
|
||||
|
||||
return { totalAmount, paypalAmount, manualAmount, count };
|
||||
}, [filteredData]);
|
||||
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";
|
||||
@@ -101,17 +133,38 @@ export const ReportsPage: React.FC = () => {
|
||||
const handleExportCSV = () => {
|
||||
if (!activeCondo) return;
|
||||
|
||||
const headers = ["Data Pagamento", "Famiglia", "Interno", "Mese Rif.", "Anno Rif.", "Importo", "Metodo", "Note"];
|
||||
const rows = filteredData.map(p => [
|
||||
new Date(p.datePaid).toLocaleDateString(),
|
||||
p.familyName,
|
||||
p.familyUnit,
|
||||
getMonthLabel(p.forMonth),
|
||||
p.forYear,
|
||||
p.amount.toFixed(2),
|
||||
p.method,
|
||||
p.notes ? `"${p.notes.replace(/"/g, '""')}"` : ""
|
||||
]);
|
||||
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"
|
||||
@@ -120,7 +173,7 @@ export const ReportsPage: React.FC = () => {
|
||||
const encodedUri = encodeURI(csvContent);
|
||||
const link = document.createElement("a");
|
||||
link.setAttribute("href", encodedUri);
|
||||
link.setAttribute("download", `Report_Pagamenti_${activeCondo.name}_${selectedYear}.csv`);
|
||||
link.setAttribute("download", filename);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
@@ -129,13 +182,13 @@ export const ReportsPage: React.FC = () => {
|
||||
if (loading) return <div className="p-8 text-center text-slate-400">Caricamento report...</div>;
|
||||
|
||||
return (
|
||||
<div className="space-y-8 pb-20 animate-fade-in">
|
||||
<div className="space-y-6 pb-20 animate-fade-in">
|
||||
<div className="flex flex-col md:flex-row justify-between md:items-end gap-4">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-slate-800 flex items-center gap-2">
|
||||
<PieChart className="w-6 h-6 text-blue-600"/> Reportistica & Transazioni
|
||||
<PieChart className="w-6 h-6 text-blue-600"/> Reportistica & Bilancio
|
||||
</h2>
|
||||
<p className="text-slate-500 text-sm">Analisi incassi per {activeCondo?.name}</p>
|
||||
<p className="text-slate-500 text-sm">Analisi finanziaria per {activeCondo?.name}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
@@ -171,11 +224,11 @@ export const ReportsPage: React.FC = () => {
|
||||
</select>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Cerca Famiglia/ID</label>
|
||||
<label className="text-xs font-bold text-slate-500 uppercase mb-1 block">Cerca...</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Cerca..."
|
||||
placeholder={activeTab === 'income' ? "Cerca Famiglia..." : "Cerca Fornitore..."}
|
||||
value={searchTerm}
|
||||
onChange={e => setSearchTerm(e.target.value)}
|
||||
className="w-full border p-2 pl-9 rounded-lg text-slate-700 bg-slate-50"
|
||||
@@ -186,93 +239,141 @@ export const ReportsPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats Cards */}
|
||||
{/* TABS */}
|
||||
<div className="flex border-b border-slate-200">
|
||||
<button onClick={() => setActiveTab('balance')} className={`px-4 py-2 font-medium text-sm flex items-center gap-2 border-b-2 transition-colors ${activeTab === 'balance' ? 'border-blue-600 text-blue-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}>
|
||||
<Wallet className="w-4 h-4"/> Bilancio
|
||||
</button>
|
||||
<button onClick={() => setActiveTab('income')} className={`px-4 py-2 font-medium text-sm flex items-center gap-2 border-b-2 transition-colors ${activeTab === 'income' ? 'border-green-600 text-green-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}>
|
||||
<ArrowUpRight className="w-4 h-4"/> Entrate (Rate)
|
||||
</button>
|
||||
<button onClick={() => setActiveTab('expenses')} className={`px-4 py-2 font-medium text-sm flex items-center gap-2 border-b-2 transition-colors ${activeTab === 'expenses' ? 'border-red-600 text-red-600' : 'border-transparent text-slate-500 hover:text-slate-700'}`}>
|
||||
<TrendingDown className="w-4 h-4"/> Uscite (Fornitori)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Overview Cards (Dynamic based on Tab) */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div className="bg-blue-600 text-white p-6 rounded-xl shadow-lg shadow-blue-200">
|
||||
<p className="text-blue-100 text-sm font-medium mb-1">Incasso Totale</p>
|
||||
<h3 className="text-3xl font-bold">€ {stats.totalAmount.toLocaleString('it-IT', { minimumFractionDigits: 2 })}</h3>
|
||||
<p className="text-blue-200 text-xs mt-2 flex items-center gap-1">
|
||||
<ArrowUpRight className="w-3 h-3"/> su {stats.count} transazioni
|
||||
</p>
|
||||
{/* IN (Always shown or contextual) */}
|
||||
<div className={`p-6 rounded-xl shadow-lg transition-all ${activeTab === 'expenses' ? 'bg-white border border-slate-200 opacity-60' : 'bg-green-600 text-white shadow-green-200'}`}>
|
||||
<p className={`${activeTab === 'expenses' ? 'text-slate-500' : 'text-green-100'} text-sm font-medium mb-1`}>Totale Entrate</p>
|
||||
<h3 className={`text-3xl font-bold ${activeTab === 'expenses' ? 'text-slate-800' : ''}`}>€ {stats.totalIncome.toLocaleString('it-IT', { minimumFractionDigits: 2 })}</h3>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-slate-500 text-xs font-bold uppercase mb-1 flex items-center gap-1"><CreditCard className="w-3 h-3"/> PayPal / Online</p>
|
||||
<h3 className="text-2xl font-bold text-slate-800">€ {stats.paypalAmount.toLocaleString('it-IT', { minimumFractionDigits: 2 })}</h3>
|
||||
</div>
|
||||
<div className="h-10 w-10 bg-blue-50 text-blue-600 rounded-full flex items-center justify-center font-bold text-xs">
|
||||
{stats.count > 0 ? Math.round((stats.paypalAmount / stats.totalAmount) * 100) : 0}%
|
||||
</div>
|
||||
{/* OUT */}
|
||||
<div className={`p-6 rounded-xl shadow-lg transition-all ${activeTab === 'income' ? 'bg-white border border-slate-200 opacity-60' : 'bg-red-600 text-white shadow-red-200'}`}>
|
||||
<p className={`${activeTab === 'income' ? 'text-slate-500' : 'text-red-100'} text-sm font-medium mb-1`}>Totale Uscite</p>
|
||||
<h3 className={`text-3xl font-bold ${activeTab === 'income' ? 'text-slate-800' : ''}`}>€ {stats.totalExpenses.toLocaleString('it-IT', { minimumFractionDigits: 2 })}</h3>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-xl border border-slate-200 shadow-sm flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-slate-500 text-xs font-bold uppercase mb-1 flex items-center gap-1"><Banknote className="w-3 h-3"/> Manuale / Bonifico</p>
|
||||
<h3 className="text-2xl font-bold text-slate-800">€ {stats.manualAmount.toLocaleString('it-IT', { minimumFractionDigits: 2 })}</h3>
|
||||
</div>
|
||||
<div className="h-10 w-10 bg-slate-100 text-slate-600 rounded-full flex items-center justify-center font-bold text-xs">
|
||||
{stats.count > 0 ? Math.round((stats.manualAmount / stats.totalAmount) * 100) : 0}%
|
||||
</div>
|
||||
{/* BALANCE */}
|
||||
<div className={`p-6 rounded-xl shadow-lg border transition-all bg-white border-slate-200`}>
|
||||
<p className="text-slate-500 text-sm font-medium mb-1 uppercase">Saldo Periodo</p>
|
||||
<h3 className={`text-3xl font-bold ${stats.balance >= 0 ? 'text-blue-600' : 'text-red-600'}`}>€ {stats.balance.toLocaleString('it-IT', { minimumFractionDigits: 2 })}</h3>
|
||||
<p className="text-xs text-slate-400 mt-1">Differenza Entrate - Uscite</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Transactions Table */}
|
||||
{/* MAIN DATA TABLE */}
|
||||
<div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
|
||||
<div className="p-4 border-b border-slate-100 bg-slate-50/50 flex justify-between items-center">
|
||||
<h3 className="font-bold text-slate-800">Dettaglio Transazioni</h3>
|
||||
<span className="text-xs bg-slate-200 text-slate-600 px-2 py-1 rounded-full font-bold">{filteredData.length} risultati</span>
|
||||
<h3 className="font-bold text-slate-800">
|
||||
{activeTab === 'income' ? 'Dettaglio Versamenti Famiglie' : activeTab === 'expenses' ? 'Dettaglio Spese Fornitori' : 'Movimenti Recenti'}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-sm text-slate-600">
|
||||
<thead className="bg-slate-50 text-slate-700 font-semibold border-b border-slate-200">
|
||||
<tr>
|
||||
<th className="px-6 py-3 whitespace-nowrap">Data</th>
|
||||
<th className="px-6 py-3">Famiglia</th>
|
||||
<th className="px-6 py-3">Riferimento</th>
|
||||
<th className="px-6 py-3">Metodo</th>
|
||||
<th className="px-6 py-3">Note / ID Transazione</th>
|
||||
<th className="px-6 py-3 text-right">Importo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{filteredData.length === 0 ? (
|
||||
<tr><td colSpan={6} className="p-8 text-center text-slate-400">Nessuna transazione trovata con i filtri attuali.</td></tr>
|
||||
) : (
|
||||
filteredData.map((t) => (
|
||||
<tr key={t.id} className="hover:bg-slate-50 transition-colors">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-slate-500 font-medium">
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="w-4 h-4 text-slate-300"/>
|
||||
{new Date(t.datePaid).toLocaleDateString()}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<div className="font-bold text-slate-700">{t.familyName}</div>
|
||||
<div className="text-xs text-slate-400">Int. {t.familyUnit}</div>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className="bg-slate-100 text-slate-600 px-2 py-1 rounded text-xs font-bold uppercase">
|
||||
{getMonthLabel(t.forMonth).substring(0, 5)} {t.forYear}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className={`inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs font-bold uppercase ${t.method === 'PayPal' ? 'bg-blue-100 text-blue-700' : 'bg-green-100 text-green-700'}`}>
|
||||
{t.method === 'PayPal' ? <CreditCard className="w-3 h-3"/> : <Banknote className="w-3 h-3"/>}
|
||||
{t.method}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 max-w-xs truncate text-slate-500 text-xs" title={t.notes}>
|
||||
{t.notes || '-'}
|
||||
</td>
|
||||
<td className="px-6 py-4 text-right font-bold text-slate-800">
|
||||
€ {t.amount.toFixed(2)}
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
{activeTab === 'expenses' ? (
|
||||
// EXPENSES TABLE
|
||||
<table className="w-full text-left text-sm text-slate-600">
|
||||
<thead className="bg-slate-50 text-slate-700 font-semibold border-b border-slate-200">
|
||||
<tr>
|
||||
<th className="px-6 py-3">Data</th>
|
||||
<th className="px-6 py-3">Fornitore</th>
|
||||
<th className="px-6 py-3">Descrizione</th>
|
||||
<th className="px-6 py-3">Stato</th>
|
||||
<th className="px-6 py-3 text-right">Importo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{filteredExpenses.length === 0 ? (
|
||||
<tr><td colSpan={5} className="p-8 text-center text-slate-400">Nessuna spesa trovata.</td></tr>
|
||||
) : (
|
||||
filteredExpenses.map(e => (
|
||||
<tr key={e.id} className="hover:bg-slate-50 transition-colors">
|
||||
<td className="px-6 py-4">
|
||||
{e.paymentDate ? new Date(e.paymentDate).toLocaleDateString() : new Date(e.createdAt).toLocaleDateString()}
|
||||
</td>
|
||||
<td className="px-6 py-4 font-bold text-slate-700">{e.supplierName}</td>
|
||||
<td className="px-6 py-4">{e.description}</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className={`px-2 py-1 rounded-full text-[10px] font-bold uppercase ${
|
||||
e.status === 'PAID' ? 'bg-green-100 text-green-700' :
|
||||
e.status === 'SUSPENDED' ? 'bg-amber-100 text-amber-700' :
|
||||
'bg-red-100 text-red-700'
|
||||
}`}>
|
||||
{e.status === 'PAID' ? 'Saldato' : e.status === 'SUSPENDED' ? 'Sospeso' : 'Insoluto'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-right font-bold text-slate-800">
|
||||
€ {e.amount.toFixed(2)}
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
// INCOME TABLE (Default for Balance too)
|
||||
<table className="w-full text-left text-sm text-slate-600">
|
||||
<thead className="bg-slate-50 text-slate-700 font-semibold border-b border-slate-200">
|
||||
<tr>
|
||||
<th className="px-6 py-3 whitespace-nowrap">Data</th>
|
||||
<th className="px-6 py-3">Famiglia</th>
|
||||
<th className="px-6 py-3">Riferimento</th>
|
||||
<th className="px-6 py-3">Metodo</th>
|
||||
<th className="px-6 py-3">Note</th>
|
||||
<th className="px-6 py-3 text-right">Importo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
{filteredIncome.length === 0 ? (
|
||||
<tr><td colSpan={6} className="p-8 text-center text-slate-400">Nessuna transazione trovata.</td></tr>
|
||||
) : (
|
||||
filteredIncome.map((t) => (
|
||||
<tr key={t.id} className="hover:bg-slate-50 transition-colors">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-slate-500 font-medium">
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar className="w-4 h-4 text-slate-300"/>
|
||||
{new Date(t.datePaid).toLocaleDateString()}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<div className="font-bold text-slate-700">{t.familyName}</div>
|
||||
<div className="text-xs text-slate-400">Int. {t.familyUnit}</div>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className="bg-slate-100 text-slate-600 px-2 py-1 rounded text-xs font-bold uppercase">
|
||||
{getMonthLabel(t.forMonth).substring(0, 5)} {t.forYear}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<span className={`inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs font-bold uppercase ${t.method === 'PayPal' ? 'bg-blue-100 text-blue-700' : 'bg-green-100 text-green-700'}`}>
|
||||
{t.method === 'PayPal' ? <CreditCard className="w-3 h-3"/> : <Banknote className="w-3 h-3"/>}
|
||||
{t.method}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 max-w-xs truncate text-slate-500 text-xs" title={t.notes}>
|
||||
{t.notes || '-'}
|
||||
</td>
|
||||
<td className="px-6 py-4 text-right font-bold text-slate-800">
|
||||
€ {t.amount.toFixed(2)}
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user