v1.43.11 - Filtro de período personalizável em relatórios

This commit is contained in:
marcoitaloesp-ai 2025-12-16 16:47:03 +00:00 committed by GitHub
parent 30d58c375c
commit efbd5e8aa2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 73 additions and 5 deletions

View File

@ -5,6 +5,32 @@ O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/).
Este projeto adota [Versionamento Semântico](https://semver.org/pt-BR/). Este projeto adota [Versionamento Semântico](https://semver.org/pt-BR/).
## [1.43.11] - 2025-12-16
### Added
- **Relatórios - Filtro de Período**
- Seletores de "Data Início" e "Data Fim" nos relatórios:
* Por Categoria
* Por Centro de Custo
* Maiores Gastos
- Período padrão: Início do ano (01/01/2025) até hoje (16/12/2025)
- Usuário pode alterar o período livremente
- Interface responsiva: campos lado a lado em desktop, empilhados em mobile
- Parâmetros `start_date` e `end_date` enviados à API
- Traduções:
* pt-BR: "Data Início", "Data Fim"
* en: "Start Date", "End Date"
* es: "Fecha Inicio", "Fecha Fin"
### Changed
- **Frontend - Reports.jsx**
- Estados `startDate` e `endDate` adicionados
- useEffect atualizado com dependências `startDate` e `endDate`
- Parâmetros de data incluídos em:
* `reportService.getByCategory()`
* `reportService.getByCostCenter()`
* `reportService.getTopExpenses()`
## [1.43.10] - 2025-12-16 ## [1.43.10] - 2025-12-16
### Fixed ### Fixed

View File

@ -1 +1 @@
1.43.10 1.43.11

View File

@ -1841,6 +1841,8 @@
"overdue": "Overdue", "overdue": "Overdue",
"savingsRate": "Savings Rate", "savingsRate": "Savings Rate",
"selectPeriod": "Select Period", "selectPeriod": "Select Period",
"startDate": "Start Date",
"endDate": "End Date",
"subtitle": "Detailed analysis of your finances", "subtitle": "Detailed analysis of your finances",
"summary": "Summary", "summary": "Summary",
"thisMonth": "This Month", "thisMonth": "This Month",

View File

@ -1806,6 +1806,8 @@
"accounts": "Por Cuenta", "accounts": "Por Cuenta",
"period": "Período", "period": "Período",
"selectPeriod": "Seleccionar período", "selectPeriod": "Seleccionar período",
"startDate": "Fecha Inicio",
"endDate": "Fecha Fin",
"thisMonth": "Este mes", "thisMonth": "Este mes",
"lastMonth": "Mes anterior", "lastMonth": "Mes anterior",
"last3Months": "Últimos 3 meses", "last3Months": "Últimos 3 meses",

View File

@ -1847,6 +1847,8 @@
"overdue": "Vencidas", "overdue": "Vencidas",
"savingsRate": "Taxa de Poupança", "savingsRate": "Taxa de Poupança",
"selectPeriod": "Selecionar Período", "selectPeriod": "Selecionar Período",
"startDate": "Data Início",
"endDate": "Data Fim",
"subtitle": "Análise detalhada das suas finanças", "subtitle": "Análise detalhada das suas finanças",
"summary": "Resumo", "summary": "Resumo",
"thisMonth": "Este Mês", "thisMonth": "Este Mês",

View File

@ -49,6 +49,10 @@ const Reports = () => {
const [year, setYear] = useState(new Date().getFullYear()); const [year, setYear] = useState(new Date().getFullYear());
const [months, setMonths] = useState(12); const [months, setMonths] = useState(12);
// Date range filters (default: start of year to today)
const [startDate, setStartDate] = useState(new Date(new Date().getFullYear(), 0, 1).toISOString().split('T')[0]);
const [endDate, setEndDate] = useState(new Date().toISOString().split('T')[0]);
// Data states // Data states
const [summary, setSummary] = useState(null); const [summary, setSummary] = useState(null);
const [categoryData, setCategoryData] = useState(null); const [categoryData, setCategoryData] = useState(null);
@ -76,7 +80,7 @@ const Reports = () => {
setSummary(summaryRes); setSummary(summaryRes);
break; break;
case 'category': case 'category':
const params = { type: 'debit' }; const params = { type: 'debit', start_date: startDate, end_date: endDate };
if (selectedCategory) { if (selectedCategory) {
params.parent_id = selectedCategory.category_id; params.parent_id = selectedCategory.category_id;
} }
@ -92,7 +96,7 @@ const Reports = () => {
setDayOfWeekData(dowRes); setDayOfWeekData(dowRes);
break; break;
case 'topExpenses': case 'topExpenses':
const topRes = await reportService.getTopExpenses({ limit: 20 }); const topRes = await reportService.getTopExpenses({ limit: 20, start_date: startDate, end_date: endDate });
setTopExpenses(topRes); setTopExpenses(topRes);
break; break;
case 'projection': case 'projection':
@ -104,7 +108,7 @@ const Reports = () => {
setComparison(compRes); setComparison(compRes);
break; break;
case 'costCenter': case 'costCenter':
const ccParams = {}; const ccParams = { start_date: startDate, end_date: endDate };
if (selectedCostCenter) { if (selectedCostCenter) {
ccParams.cost_center_id = selectedCostCenter.id ?? 0; ccParams.cost_center_id = selectedCostCenter.id ?? 0;
} }
@ -136,7 +140,7 @@ const Reports = () => {
} finally { } finally {
setLoading(false); setLoading(false);
} }
}, [activeTab, year, months, selectedCategory, selectedCostCenter, selectedCostCenterCategory]); }, [activeTab, year, months, selectedCategory, selectedCostCenter, selectedCostCenterCategory, startDate, endDate]);
useEffect(() => { useEffect(() => {
loadData(); loadData();
@ -2148,6 +2152,38 @@ const Reports = () => {
{t('reports.subtitle')} {t('reports.subtitle')}
</p> </p>
</div> </div>
{/* Date Range Filters */}
{(activeTab === 'category' || activeTab === 'costCenter' || activeTab === 'topExpenses') && (
<div className={`d-flex ${isMobile ? 'flex-column w-100' : 'gap-2 align-items-center'}`}>
<div className={`d-flex ${isMobile ? 'w-100 mb-2' : ''} gap-2`}>
<div className={isMobile ? 'flex-grow-1' : ''}>
<label className="text-slate-400 mb-1 d-block" style={{ fontSize: '0.75rem' }}>
{t('reports.startDate')}
</label>
<input
type="date"
className="form-control form-control-sm bg-slate-800 text-white border-slate-700"
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
style={{ fontSize: isMobile ? '0.875rem' : '0.875rem' }}
/>
</div>
<div className={isMobile ? 'flex-grow-1' : ''}>
<label className="text-slate-400 mb-1 d-block" style={{ fontSize: '0.75rem' }}>
{t('reports.endDate')}
</label>
<input
type="date"
className="form-control form-control-sm bg-slate-800 text-white border-slate-700"
value={endDate}
onChange={(e) => setEndDate(e.target.value)}
style={{ fontSize: isMobile ? '0.875rem' : '0.875rem' }}
/>
</div>
</div>
</div>
)}
</div> </div>
{/* Tabs */} {/* Tabs */}