import React, { useState, useRef, useEffect } from 'react';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
BarController,
Title,
Tooltip,
Legend,
} from 'chart.js';
import { Bar } from 'react-chartjs-2';
import { useTranslation } from 'react-i18next';
import useFormatters from '../../hooks/useFormatters';
// Registrar componentes do Chart.js
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
BarController,
Title,
Tooltip,
Legend
);
const OverpaymentsAnalysis = ({ data, loading, onTransactionClick }) => {
const { t, i18n } = useTranslation();
const { currency } = useFormatters();
const chartRef = useRef(null);
const [selectedMonth, setSelectedMonth] = useState(null);
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
const [isExpanded, setIsExpanded] = useState(false);
// Helper para locale dinâmico
const getLocale = () => i18n.language === 'pt-BR' ? 'pt-BR' : i18n.language === 'es' ? 'es-ES' : 'en-US';
// Agrupar transações por mês (apenas sobrepagamentos - variance > 0)
const overpaymentsByMonth = {};
if (data?.transactions) {
data.transactions
.filter(t => t.variance > 0)
.forEach(t => {
const month = t.effective_date.substring(0, 7);
if (!overpaymentsByMonth[month]) {
overpaymentsByMonth[month] = {
month,
total: 0,
count: 0,
transactions: []
};
}
overpaymentsByMonth[month].total += t.variance;
overpaymentsByMonth[month].count += 1;
overpaymentsByMonth[month].transactions.push(t);
});
}
// Criar array de meses ordenados
const monthsData = Object.values(overpaymentsByMonth).sort((a, b) => a.month.localeCompare(b.month));
useEffect(() => {
const handleResize = () => {
const mobile = window.innerWidth < 768;
setIsMobile(mobile);
if (!mobile) {
setIsExpanded(false);
}
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
// Auto-expand if we have data in mobile
useEffect(() => {
if (isMobile && monthsData.length > 0 && !isExpanded) {
setIsExpanded(true);
}
}, [isMobile, monthsData.length, isExpanded]);
if (loading) {
return (
);
}
if (!data || !data.transactions || data.transactions.length === 0) {
return (
{t('dashboard.overpaymentsAnalysis')}
{t('dashboard.noOverpayments')}
);
}
// Calcular total geral
const totalOverpayment = monthsData.reduce((sum, m) => sum + m.total, 0);
// Labels do gráfico formatados
const labels = monthsData.map(d => {
const [year, month] = d.month.split('-');
const date = new Date(year, month - 1);
return date.toLocaleDateString(getLocale(), { month: 'short', year: 'numeric' });
});
const chartData = {
labels,
datasets: [
{
label: t('dashboard.overpayment'),
data: monthsData.map(d => d.total),
backgroundColor: monthsData.map((_, i) =>
selectedMonth === i ? 'rgba(251, 191, 36, 1)' : 'rgba(251, 191, 36, 0.7)'
),
borderColor: 'rgba(251, 191, 36, 1)',
borderWidth: 0,
borderRadius: 4,
hoverBackgroundColor: 'rgba(251, 191, 36, 1)',
},
],
};
const options = {
responsive: true,
maintainAspectRatio: false,
onClick: (event, elements) => {
if (elements.length > 0) {
const index = elements[0].index;
setSelectedMonth(selectedMonth === index ? null : index);
}
},
plugins: {
legend: {
display: false,
},
tooltip: {
backgroundColor: 'rgba(15, 23, 42, 0.95)',
titleColor: '#fbbf24',
bodyColor: '#f1f5f9',
borderColor: '#fbbf24',
borderWidth: 1,
padding: 12,
cornerRadius: 8,
displayColors: false,
callbacks: {
title: (items) => {
const idx = items[0].dataIndex;
return monthsData[idx]?.month || '';
},
label: (context) => {
const idx = context.dataIndex;
const monthData = monthsData[idx];
return [
`${t('dashboard.overpayment')}: ${currency(monthData.total, 'BRL')}`,
`${t('dashboard.transactions')}: ${monthData.count}`
];
},
},
},
},
scales: {
x: {
grid: {
display: false,
},
ticks: {
color: '#64748b',
font: { size: 11 },
},
},
y: {
grid: {
color: 'rgba(100, 116, 139, 0.1)',
drawBorder: false,
},
ticks: {
color: '#64748b',
font: { size: 11 },
callback: (value) => `+${currency(value, 'BRL').replace('R$', '').trim()}`,
},
},
},
};
// Dados do mês selecionado
const selectedMonthData = selectedMonth !== null ? monthsData[selectedMonth] : null;
return (
{/* Header */}
{t('dashboard.overpaymentsAnalysis')} {!isMobile && `(${data.period?.months || 12} ${t('common.months')})`}
{isMobile && (
)}
{(!isMobile || isExpanded) && (
Total: {currency(totalOverpayment, 'BRL')}
)}
{/* Instrução */}
{t('dashboard.clickBarToSeeDetails')}
{/* Gráfico */}
{/* Painel de detalhes do mês selecionado */}
{selectedMonthData && (
{/* Header do painel */}
{t('dashboard.overpayments')} - {(() => {
const [year, month] = selectedMonthData.month.split('-');
return new Date(year, month - 1).toLocaleDateString(getLocale(), { month: 'long', year: 'numeric' });
})()}
{/* Resumo do mês */}
{isMobile ? 'TOTAL' : t('dashboard.totalOverpaid')}
+{currency(selectedMonthData.total, 'BRL')}
{isMobile ? 'QTD' : t('dashboard.transactions')}
{selectedMonthData.count}
{isMobile ? 'MÉDIA' : t('dashboard.avgPerTransaction')}
+{currency(selectedMonthData.total / selectedMonthData.count, 'BRL')}
{/* Tabela de transações */}
{isMobile ? (
/* Layout Mobile - Cards */
{selectedMonthData.transactions.map((tx) => (
onTransactionClick?.(tx.id)}
style={{
cursor: 'pointer',
background: 'rgba(0,0,0,0.3)',
border: '1px solid rgba(251, 191, 36, 0.2)'
}}
>
{tx.description.length > 25 ? tx.description.substring(0, 25) + '...' : tx.description}
+{currency(tx.variance, 'BRL')}
{tx.category && (
{tx.category.name}
)}
{new Date(tx.effective_date).toLocaleDateString(getLocale(), { day: '2-digit', month: '2-digit' })}
+{tx.variance_percent}%
))}
) : (
/* Layout Desktop - Tabela */
| {t('common.date')} |
{t('common.description')} |
{t('dashboard.category')} |
{t('dashboard.plannedValue')} |
{t('dashboard.actualValue')} |
{t('dashboard.difference')} |
{t('dashboard.delay')} |
% |
{selectedMonthData.transactions.map((tx) => (
onTransactionClick?.(tx.id)}
style={{ cursor: 'pointer' }}
>
|
{new Date(tx.effective_date).toLocaleDateString(getLocale())}
|
{tx.description.length > 30 ? tx.description.substring(0, 30) + '...' : tx.description}
|
{tx.category ? (
{tx.category.name}
) : (
-
)}
|
{currency(tx.planned_amount, 'BRL')}
|
{currency(tx.actual_amount, 'BRL')}
|
+{currency(tx.variance, 'BRL')}
|
{tx.delay_days !== null && tx.delay_days !== 0 ? (
0 ? 'rgba(239, 68, 68, 0.8)' : 'rgba(34, 197, 94, 0.8)'
}}
>
0 ? '-history' : ''} me-1`} style={{ fontSize: '9px' }}>
{tx.delay_days > 0 ? `+${tx.delay_days}d` : `${tx.delay_days}d`}
) : (
-
)}
|
+{tx.variance_percent}%
|
))}
)}
)}
{/* Dica */}
{!selectedMonthData && monthsData.length > 0 && (
{t('dashboard.tip')}: {t('dashboard.overpaymentTip')}
)}
);
};
export default OverpaymentsAnalysis;