diff --git a/CHANGELOG.md b/CHANGELOG.md index a46599d..ce7dc13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/). Este projeto adota [Versionamento Semântico](https://semver.org/pt-BR/). +## [1.35.0] - 2025-12-15 + +### Improved +- **UI/UX da Comparação de Períodos** - Interface completamente redesenhada + - Cards KPI com gradientes e ícones grandes para cada métrica + - Badges de variação com validação de NaN/Infinity + - Tabela comparativa detalhada com métricas lado a lado + - Gráfico de barras agrupadas com tooltips aprimorados + - Layout responsivo: tabela 5 colunas + gráfico 7 colunas + - Design profissional com hierarquia visual clara + - Traduções: periodComparison, vsPreviousPeriod, detailedComparison, visualComparison, metric, variation + ## [1.34.7] - 2025-12-15 ### Fixed diff --git a/VERSION b/VERSION index ca38008..2aeaa11 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.34.7 +1.35.0 diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index 8e4d8f1..b682fb2 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -1801,6 +1801,12 @@ "byCategory": "By Category", "byCostCenter": "By Cost Center", "comparison": "Comparison", + "periodComparison": "Period Comparison", + "vsPreviousPeriod": "vs previous period", + "detailedComparison": "Detailed Comparison", + "visualComparison": "Visual Comparison", + "metric": "Metric", + "variation": "Variation", "custom": "Custom", "dayOfWeek": { "friday": "Friday", diff --git a/frontend/src/i18n/locales/es.json b/frontend/src/i18n/locales/es.json index 1a2ed32..457129f 100644 --- a/frontend/src/i18n/locales/es.json +++ b/frontend/src/i18n/locales/es.json @@ -1783,6 +1783,12 @@ "byCostCenter": "Por Centro de Costo", "monthlyEvolution": "Evolución Mensual", "comparison": "Comparativa", + "periodComparison": "Comparación de Períodos", + "vsPreviousPeriod": "vs período anterior", + "detailedComparison": "Comparación Detallada", + "visualComparison": "Comparación Visual", + "metric": "Métrica", + "variation": "Variación", "topExpenses": "Mayores Gastos", "projection": "Proyección", "recurring": "Recurrentes", diff --git a/frontend/src/i18n/locales/pt-BR.json b/frontend/src/i18n/locales/pt-BR.json index 33308cf..14d1454 100644 --- a/frontend/src/i18n/locales/pt-BR.json +++ b/frontend/src/i18n/locales/pt-BR.json @@ -1803,6 +1803,12 @@ "byCategory": "Por Categoria", "byCostCenter": "Por Centro de Custo", "comparison": "Comparação", + "periodComparison": "Comparação de Períodos", + "vsPreviousPeriod": "vs período anterior", + "detailedComparison": "Comparação Detalhada", + "visualComparison": "Comparação Visual", + "metric": "Métrica", + "variation": "Variação", "custom": "Personalizado", "dayOfWeek": { "friday": "Sexta", diff --git a/frontend/src/pages/Reports.jsx b/frontend/src/pages/Reports.jsx index cedaec9..b7d8253 100644 --- a/frontend/src/pages/Reports.jsx +++ b/frontend/src/pages/Reports.jsx @@ -614,75 +614,204 @@ const Reports = () => { return (
-
-
-
-
{comparison.period2.label}
-
-
-
- {t('reports.income')} - {currency(comparison.period2.income, comparison.currency)} + {/* Header com seletor de períodos */} +
+
+
+ + {t('reports.periodComparison')} +
+
+
+ + {/* Cards KPI Comparativos */} +
+
+
+
+
+
{t('reports.income')}
+

{currency(comparison.period1.income, comparison.currency)}

+
+
+ +
-
- {t('reports.expenses')} - {currency(comparison.period2.expense, comparison.currency)} + +
+ {comparison.variation.income !== 0 && !isNaN(comparison.variation.income) && isFinite(comparison.variation.income) && ( + <> + = 0 ? 'bg-white text-success' : 'bg-white text-danger'} px-2 py-1`}> + = 0 ? 'up' : 'down'} me-1`}> + {comparison.variation.income > 0 ? '+' : ''}{Math.abs(comparison.variation.income).toFixed(1)}% + + {t('reports.vsPreviousPeriod')} + + )}
-
- {t('reports.balance')} - = 0 ? 'text-success' : 'text-danger'}> - {currency(comparison.period2.balance, comparison.currency)} - + +
+ +
+
+ {comparison.period2.label}: + {currency(comparison.period2.income, comparison.currency)} +
-
-
+
+
+
+
+
+
{t('reports.expenses')}
+

{currency(comparison.period1.expense, comparison.currency)}

+
+
+ +
+
+ +
+ {comparison.variation.expense !== 0 && !isNaN(comparison.variation.expense) && isFinite(comparison.variation.expense) && ( + <> + + = 0 ? 'up' : 'down'} me-1`}> + {comparison.variation.expense > 0 ? '+' : ''}{Math.abs(comparison.variation.expense).toFixed(1)}% + + {t('reports.vsPreviousPeriod')} + + )} +
+ +
+ +
+
+ {comparison.period2.label}: + {currency(comparison.period2.expense, comparison.currency)} +
+
+
+
+
+ +
+
+
+
+
+
{t('reports.balance')}
+

{currency(comparison.period1.balance, comparison.currency)}

+
+
+ +
+
+ +
+ {comparison.variation.balance !== 0 && !isNaN(comparison.variation.balance) && isFinite(comparison.variation.balance) && ( + <> + = 0 ? 'bg-white text-success' : 'bg-white text-danger'} px-2 py-1`}> + = 0 ? 'up' : 'down'} me-1`}> + {comparison.variation.balance > 0 ? '+' : ''}{Math.abs(comparison.variation.balance).toFixed(1)}% + + {t('reports.vsPreviousPeriod')} + + )} +
+ +
+ +
+
+ {comparison.period2.label}: + {currency(comparison.period2.balance, comparison.currency)} +
+
+
+
+
+ + {/* Tabela Comparativa Detalhada */} +
+
- {comparison.period1.label} - {t('common.current')} + + {t('reports.detailedComparison')}
-
- {t('reports.income')} -
- {currency(comparison.period1.income, comparison.currency)} - {comparison.variation.income !== 0 && ( - = 0 ? 'bg-success' : 'bg-danger'}`}> - {comparison.variation.income > 0 ? '+' : ''}{comparison.variation.income}% - - )} -
-
-
- {t('reports.expenses')} -
- {currency(comparison.period1.expense, comparison.currency)} - {comparison.variation.expense !== 0 && ( - - {comparison.variation.expense > 0 ? '+' : ''}{comparison.variation.expense}% - - )} -
-
-
- {t('reports.balance')} - = 0 ? 'text-success' : 'text-danger'}> - {currency(comparison.period1.balance, comparison.currency)} - -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{t('reports.metric')}{comparison.period2.label}{comparison.period1.label}{t('reports.variation')}
{t('reports.income')}{currency(comparison.period2.income, comparison.currency)}{currency(comparison.period1.income, comparison.currency)} + {comparison.variation.income !== 0 && !isNaN(comparison.variation.income) && isFinite(comparison.variation.income) && ( + = 0 ? 'bg-success' : 'bg-danger'}`}> + {comparison.variation.income > 0 ? '+' : ''}{comparison.variation.income.toFixed(1)}% + + )} +
{t('reports.expenses')}{currency(comparison.period2.expense, comparison.currency)}{currency(comparison.period1.expense, comparison.currency)} + {comparison.variation.expense !== 0 && !isNaN(comparison.variation.expense) && isFinite(comparison.variation.expense) && ( + + {comparison.variation.expense > 0 ? '+' : ''}{comparison.variation.expense.toFixed(1)}% + + )} +
{t('reports.balance')}= 0 ? 'text-success' : 'text-danger'}`}> + {currency(comparison.period2.balance, comparison.currency)} + = 0 ? 'text-success' : 'text-danger'}`}> + {currency(comparison.period1.balance, comparison.currency)} + + {comparison.variation.balance !== 0 && !isNaN(comparison.variation.balance) && isFinite(comparison.variation.balance) && ( + = 0 ? 'bg-success' : 'bg-danger'}`}> + {comparison.variation.balance > 0 ? '+' : ''}{comparison.variation.balance.toFixed(1)}% + + )} +
- {/* Comparison Chart */} -
-
-
+ {/* Gráfico de Barras Agrupadas */} +
+
+
+
+ + {t('reports.visualComparison')} +
+
+
{ { label: comparison.period2.label, data: [comparison.period2.income, comparison.period2.expense, comparison.period2.balance], - backgroundColor: 'rgba(148, 163, 184, 0.5)', - borderRadius: 4, + backgroundColor: 'rgba(100, 116, 139, 0.6)', + borderColor: '#64748b', + borderWidth: 2, + borderRadius: 6, }, { label: comparison.period1.label, data: [comparison.period1.income, comparison.period1.expense, comparison.period1.balance], - backgroundColor: ['rgba(16, 185, 129, 0.7)', 'rgba(239, 68, 68, 0.7)', 'rgba(59, 130, 246, 0.7)'], - borderRadius: 4, + backgroundColor: ['rgba(16, 185, 129, 0.8)', 'rgba(239, 68, 68, 0.8)', 'rgba(59, 130, 246, 0.8)'], + borderColor: ['#10b981', '#ef4444', '#3b82f6'], + borderWidth: 2, + borderRadius: 6, }, ], }} - options={chartOptions} + options={{ + ...chartOptions, + plugins: { + ...chartOptions.plugins, + legend: { + position: 'top', + labels: { + color: '#94a3b8', + padding: 15, + font: { size: 12, weight: 'bold' } + } + }, + tooltip: { + backgroundColor: 'rgba(15, 23, 42, 0.95)', + titleColor: '#fff', + bodyColor: '#94a3b8', + borderColor: '#334155', + borderWidth: 1, + padding: 12, + displayColors: true, + callbacks: { + label: function(context) { + return `${context.dataset.label}: ${currency(context.parsed.y, comparison.currency)}`; + } + } + } + } + }} />