v1.43.1 - Fix: Espaços em branco mobile eliminados + Textos compactos
This commit is contained in:
parent
e753c65cf0
commit
a244632e0a
23
CHANGELOG.md
23
CHANGELOG.md
@ -5,6 +5,29 @@ O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/).
|
||||
Este projeto adota [Versionamento Semântico](https://semver.org/pt-BR/).
|
||||
|
||||
|
||||
## [1.43.1] - 2025-12-16
|
||||
|
||||
### Fixed
|
||||
- **Espaços em branco desnecessários em Mobile** - Otimização completa de altura e padding
|
||||
- Widget Fluxo de Caixa:
|
||||
- Removido `h-100` em mobile (agora se ajusta ao conteúdo)
|
||||
- Card-body padding reduzido: 1rem
|
||||
- Estados loading/erro: altura 280px (antes 350px fixo)
|
||||
- Gráfico: 280px (mobile) vs 350px (desktop)
|
||||
- Resumo: margens reduzidas `mt-3 pt-2`
|
||||
- Widget Projeção de Saldo:
|
||||
- Card-body padding: 1rem em mobile
|
||||
- Gráfico reduzido: 250px (mobile) vs 350px (desktop)
|
||||
- Alertas compactos: padding `p-2`, margens `mb-2/mb-3`, fonte 0.75rem
|
||||
- Textos abreviados em alertas para economizar espaço
|
||||
|
||||
### Improved
|
||||
- **Abreviações em textos mobile** - Labels compactos sem quebra de linha
|
||||
- Fluxo de Caixa: "RECEITAS", "DESPESAS", "MÉD. REC.", "MÉD. DESP."
|
||||
- Sobrepagamentos: "TOTAL", "QTD", "MÉDIA"
|
||||
- Alertas: "Saldo negativo em" ao invés de "Previsão de saldo negativo em"
|
||||
- Todos com `whiteSpace: 'nowrap'` para prevenir quebras
|
||||
|
||||
## [1.43.0] - 2025-12-16
|
||||
|
||||
### Added
|
||||
|
||||
@ -101,7 +101,7 @@ cd frontend && ./deploy.sh
|
||||
### Backend
|
||||
```bash
|
||||
cd backend && ./deploy.sh
|
||||
```
|
||||
```github/.DIRETRIZES_DESENVOLVIMENTO_v5
|
||||
|
||||
## 📖 Documentação
|
||||
|
||||
|
||||
@ -259,19 +259,18 @@ const BalanceProjectionChart = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="card-body" style={{ display: isMobile && !isExpanded ? 'none' : 'block' }}>
|
||||
<div className="card-body" style={{ display: isMobile && !isExpanded ? 'none' : 'block', padding: isMobile ? '1rem' : undefined }}>
|
||||
{/* Summary Stats */}
|
||||
{summary && (
|
||||
<>
|
||||
{/* Alert if overdue transactions included */}
|
||||
{summary.overdue_count > 0 && (
|
||||
<div className="alert alert-warning d-flex align-items-center mb-3" role="alert">
|
||||
<i className="bi bi-exclamation-circle-fill me-2"></i>
|
||||
<div className={`alert alert-warning d-flex align-items-center ${isMobile ? 'mb-2 p-2' : 'mb-3'}`} role="alert" style={isMobile ? { fontSize: '0.75rem' } : undefined}>
|
||||
<i className={`bi bi-exclamation-circle-fill ${isMobile ? 'me-1' : 'me-2'}`} style={isMobile ? { fontSize: '0.9rem' } : undefined}></i>
|
||||
<div>
|
||||
<strong>{t('reports.projectionChart.overdueIncluded') || 'Atenção'}:</strong>{' '}
|
||||
{summary.overdue_count} {t('reports.projectionChart.overdueTransactions') || 'transação(ões) em atraso'}{' '}
|
||||
{t('reports.projectionChart.includedInProjection') || 'já incluída(s) no saldo atual'}.
|
||||
{' '}
|
||||
{!isMobile && (t('reports.projectionChart.includedInProjection') || 'já incluída(s) no saldo atual.')}{' '}
|
||||
<span className={summary.overdue_impact < 0 ? 'text-danger fw-bold' : 'text-success fw-bold'}>
|
||||
({summary.overdue_impact >= 0 ? '+' : ''}{currency(summary.overdue_impact, data?.currency)})
|
||||
</span>
|
||||
@ -328,18 +327,18 @@ const BalanceProjectionChart = () => {
|
||||
|
||||
{/* Alert if negative balance predicted */}
|
||||
{summary?.negative_month && (
|
||||
<div className="alert alert-danger d-flex align-items-center mb-4" role="alert">
|
||||
<i className="bi bi-exclamation-triangle-fill me-2 fs-5"></i>
|
||||
<div className={`alert alert-danger d-flex align-items-center ${isMobile ? 'mb-3 p-2' : 'mb-4'}`} role="alert" style={isMobile ? { fontSize: '0.75rem' } : undefined}>
|
||||
<i className={`bi bi-exclamation-triangle-fill ${isMobile ? 'me-1 fs-6' : 'me-2 fs-5'}`}></i>
|
||||
<div>
|
||||
<strong>{t('reports.projectionChart.warning') || 'Atenção!'}</strong>{' '}
|
||||
{t('reports.projectionChart.negativeAlert') || 'Previsão de saldo negativo em'}{' '}
|
||||
{isMobile ? 'Saldo negativo em' : (t('reports.projectionChart.negativeAlert') || 'Previsão de saldo negativo em')}{' '}
|
||||
<strong>{summary.negative_month}</strong>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Chart */}
|
||||
<div style={{ height: isMobile ? '300px' : '350px' }}>
|
||||
<div style={{ height: isMobile ? '250px' : '350px' }}>
|
||||
<Line data={chartData} options={options} />
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
@ -36,10 +36,17 @@ const CashflowChart = ({ data, loading, scale = 'monthly', primaryCurrency = 'EU
|
||||
const { t } = useTranslation();
|
||||
const { currency } = useFormatters();
|
||||
const chartRef = useRef(null);
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => setIsMobile(window.innerWidth < 768);
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="d-flex justify-content-center align-items-center" style={{ height: '350px' }}>
|
||||
<div className="d-flex justify-content-center align-items-center" style={{ height: isMobile ? '280px' : '350px' }}>
|
||||
<div className="spinner-border text-primary" role="status">
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
@ -49,7 +56,7 @@ const CashflowChart = ({ data, loading, scale = 'monthly', primaryCurrency = 'EU
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
return (
|
||||
<div className="d-flex justify-content-center align-items-center text-slate-400" style={{ height: '350px' }}>
|
||||
<div className="d-flex justify-content-center align-items-center text-slate-400" style={{ height: isMobile ? '280px' : '350px' }}>
|
||||
<div className="text-center">
|
||||
<i className="bi bi-bar-chart fs-1 mb-2"></i>
|
||||
<p>{t('dashboard.noDataAvailable')}</p>
|
||||
@ -211,7 +218,7 @@ const CashflowChart = ({ data, loading, scale = 'monthly', primaryCurrency = 'EU
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ height: '350px' }}>
|
||||
<div style={{ height: isMobile ? '280px' : '350px' }}>
|
||||
<Chart ref={chartRef} type="bar" data={chartData} options={options} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -273,30 +273,30 @@ const OverpaymentsAnalysis = ({ data, loading, onTransactionClick }) => {
|
||||
<div className={`row ${isMobile ? 'g-2' : 'g-3'} mb-3`}>
|
||||
<div className="col-4">
|
||||
<div className={`text-center ${isMobile ? 'p-1' : 'p-2'} rounded`} style={{ background: 'rgba(0,0,0,0.2)' }}>
|
||||
<small className="text-slate-400 d-block text-uppercase" style={{ fontSize: isMobile ? '0.65rem' : '10px' }}>
|
||||
{t('dashboard.totalOverpaid')}
|
||||
<small className="text-slate-400 d-block text-uppercase" style={{ fontSize: isMobile ? '0.6rem' : '10px', whiteSpace: isMobile ? 'nowrap' : 'normal' }}>
|
||||
{isMobile ? 'TOTAL' : t('dashboard.totalOverpaid')}
|
||||
</small>
|
||||
<span className={`text-warning fw-bold ${isMobile ? 'fs-6' : 'fs-5'}`} style={{ fontSize: isMobile ? '0.85rem' : undefined }}>
|
||||
<span className={`text-warning fw-bold d-block ${isMobile ? 'fs-6' : 'fs-5'}`} style={{ fontSize: isMobile ? '0.8rem' : undefined }}>
|
||||
+{currency(selectedMonthData.total, 'BRL')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-4">
|
||||
<div className={`text-center ${isMobile ? 'p-1' : 'p-2'} rounded`} style={{ background: 'rgba(0,0,0,0.2)' }}>
|
||||
<small className="text-slate-400 d-block text-uppercase" style={{ fontSize: isMobile ? '0.65rem' : '10px' }}>
|
||||
{t('dashboard.transactions')}
|
||||
<small className="text-slate-400 d-block text-uppercase" style={{ fontSize: isMobile ? '0.6rem' : '10px', whiteSpace: isMobile ? 'nowrap' : 'normal' }}>
|
||||
{isMobile ? 'QTD' : t('dashboard.transactions')}
|
||||
</small>
|
||||
<span className={`text-white fw-bold ${isMobile ? 'fs-6' : 'fs-5'}`} style={{ fontSize: isMobile ? '0.85rem' : undefined }}>
|
||||
<span className={`text-white fw-bold d-block ${isMobile ? 'fs-6' : 'fs-5'}`} style={{ fontSize: isMobile ? '0.8rem' : undefined }}>
|
||||
{selectedMonthData.count}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-4">
|
||||
<div className={`text-center ${isMobile ? 'p-1' : 'p-2'} rounded`} style={{ background: 'rgba(0,0,0,0.2)' }}>
|
||||
<small className="text-slate-400 d-block text-uppercase" style={{ fontSize: isMobile ? '0.65rem' : '10px' }}>
|
||||
{t('dashboard.avgPerTransaction')}
|
||||
<small className="text-slate-400 d-block text-uppercase" style={{ fontSize: isMobile ? '0.6rem' : '10px', whiteSpace: isMobile ? 'nowrap' : 'normal' }}>
|
||||
{isMobile ? 'MÉDIA' : t('dashboard.avgPerTransaction')}
|
||||
</small>
|
||||
<span className={`text-warning fw-bold ${isMobile ? 'fs-6' : 'fs-5'}`} style={{ fontSize: isMobile ? '0.85rem' : undefined }}>
|
||||
<span className={`text-warning fw-bold d-block ${isMobile ? 'fs-6' : 'fs-5'}`} style={{ fontSize: isMobile ? '0.8rem' : undefined }}>
|
||||
+{currency(selectedMonthData.total / selectedMonthData.count, 'BRL')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -21,6 +21,7 @@ const Dashboard = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [cashflowLoading, setCashflowLoading] = useState(true);
|
||||
const [variancesLoading, setVariancesLoading] = useState(true);
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
|
||||
|
||||
// Dados do dashboard
|
||||
const [summary, setSummary] = useState(null);
|
||||
@ -32,6 +33,13 @@ const Dashboard = () => {
|
||||
const [cashflowMonths, setCashflowMonths] = useState(12);
|
||||
const [variancesMonths, setVariancesMonths] = useState(12);
|
||||
|
||||
// Detectar resize
|
||||
useEffect(() => {
|
||||
const handleResize = () => setIsMobile(window.innerWidth < 768);
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
// Carregar resumo geral
|
||||
const loadSummary = useCallback(async () => {
|
||||
try {
|
||||
@ -325,7 +333,7 @@ const Dashboard = () => {
|
||||
<div className="row g-4">
|
||||
{/* Coluna Principal - Fluxo de Caixa */}
|
||||
<div className="col-lg-8">
|
||||
<div className="card border-0 h-100" style={{ background: '#0f172a' }}>
|
||||
<div className={`card border-0 ${!isMobile ? 'h-100' : ''}`} style={{ background: '#0f172a' }}>
|
||||
<div className="card-header border-0 d-flex justify-content-between align-items-center py-3"
|
||||
style={{ background: 'transparent', borderBottom: '1px solid rgba(59, 130, 246, 0.2)' }}>
|
||||
<h6 className="text-white mb-0 d-flex align-items-center">
|
||||
@ -346,7 +354,7 @@ const Dashboard = () => {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<div className="card-body" style={{ padding: isMobile ? '1rem' : undefined }}>
|
||||
<CashflowChart
|
||||
data={cashflow?.data || []}
|
||||
loading={cashflowLoading}
|
||||
@ -355,36 +363,36 @@ const Dashboard = () => {
|
||||
|
||||
{/* Resumo do período */}
|
||||
{cashflow && !cashflowLoading && (
|
||||
<div className="row mt-4 pt-3" style={{ borderTop: '1px solid rgba(255,255,255,0.1)' }}>
|
||||
<div className={`row ${isMobile ? 'mt-3 pt-2' : 'mt-4 pt-3'}`} style={{ borderTop: '1px solid rgba(255,255,255,0.1)' }}>
|
||||
<div className="col-6 col-md-3 text-center">
|
||||
<small className="text-slate-500 d-block text-uppercase" style={{ fontSize: '10px' }}>
|
||||
{t('dashboard.totalIncome')}
|
||||
<small className="text-slate-500 d-block text-uppercase" style={{ fontSize: isMobile ? '0.65rem' : '10px' }}>
|
||||
{isMobile ? 'RECEITAS' : t('dashboard.totalIncome')}
|
||||
</small>
|
||||
<span className="text-success fw-bold">
|
||||
<span className={`text-success fw-bold d-block ${isMobile ? '' : ''}`} style={{ fontSize: isMobile ? '0.8rem' : undefined }}>
|
||||
{currency(cashflow.totals?.income || 0, summary?.primary_currency || 'EUR')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-6 col-md-3 text-center">
|
||||
<small className="text-slate-500 d-block text-uppercase" style={{ fontSize: '10px' }}>
|
||||
{t('dashboard.totalExpenses')}
|
||||
<small className="text-slate-500 d-block text-uppercase" style={{ fontSize: isMobile ? '0.65rem' : '10px' }}>
|
||||
{isMobile ? 'DESPESAS' : t('dashboard.totalExpenses')}
|
||||
</small>
|
||||
<span className="text-danger fw-bold">
|
||||
<span className={`text-danger fw-bold d-block ${isMobile ? '' : ''}`} style={{ fontSize: isMobile ? '0.8rem' : undefined }}>
|
||||
{currency(cashflow.totals?.expense || 0, summary?.primary_currency || 'EUR')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-6 col-md-3 text-center mt-2 mt-md-0">
|
||||
<small className="text-slate-500 d-block text-uppercase" style={{ fontSize: '10px' }}>
|
||||
{t('dashboard.avgIncome')}
|
||||
<small className="text-slate-500 d-block text-uppercase" style={{ fontSize: isMobile ? '0.65rem' : '10px' }}>
|
||||
{isMobile ? 'MÉD. REC.' : t('dashboard.avgIncome')}
|
||||
</small>
|
||||
<span className="text-success">
|
||||
<span className={`text-success d-block ${isMobile ? '' : ''}`} style={{ fontSize: isMobile ? '0.8rem' : undefined }}>
|
||||
{currency(cashflow.totals?.average_income || 0, summary?.primary_currency || 'EUR')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-6 col-md-3 text-center mt-2 mt-md-0">
|
||||
<small className="text-slate-500 d-block text-uppercase" style={{ fontSize: '10px' }}>
|
||||
{t('dashboard.avgExpense')}
|
||||
<small className="text-slate-500 d-block text-uppercase" style={{ fontSize: isMobile ? '0.65rem' : '10px' }}>
|
||||
{isMobile ? 'MÉD. DESP.' : t('dashboard.avgExpense')}
|
||||
</small>
|
||||
<span className="text-danger">
|
||||
<span className={`text-danger d-block ${isMobile ? '' : ''}`} style={{ fontSize: isMobile ? '0.8rem' : undefined }}>
|
||||
{currency(cashflow.totals?.average_expense || 0, summary?.primary_currency || 'EUR')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user