v1.43.6 - Modal de detalhes Liabilities otimizado para mobile
- Modal fullscreen mobile, xl desktop - Summary cards: Grid 2x2 mobile, fontes compactas - Taxas: Grid 3 colunas com labels curtos (Mensal/Anual/Total) - Progress bar: 12px mobile, fontes reduzidas - Parcelas: Cards layout mobile substituindo tabela - Cards incluem: status, valores, juros, taxas, reconciliação, botões - Footer: Botão full-width mobile - i18n: Adicionadas traduções 'monthly', 'annual', 'total'
This commit is contained in:
parent
454ef516f4
commit
1637e5da0c
16
CHANGELOG.md
16
CHANGELOG.md
@ -5,6 +5,22 @@ O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/).
|
||||
Este projeto adota [Versionamento Semântico](https://semver.org/pt-BR/).
|
||||
|
||||
|
||||
## [1.43.6] - 2025-12-16
|
||||
|
||||
### Improved
|
||||
- **Modal Detalhes Liabilities - Mobile Otimizado** - Visualização completa mobile
|
||||
- Modal: fullscreen mobile vs xl desktop
|
||||
- Header: fontes 0.95rem, ícone 0.9rem, texto truncado
|
||||
- Summary cards: Grid 2x2 mobile, padding 0.5rem, fontes 0.65-0.85rem
|
||||
- Taxas: Grid 3 colunas mobile, labels curtos (Mensal/Anual/Total)
|
||||
- Progress bar: 12px mobile, fontes 0.65-0.75rem
|
||||
- Parcelas: **Cards layout mobile** com todas as info compactas
|
||||
- Cards parcelas: status badge, valores, juros, taxas, botões de ação
|
||||
- Botão footer: full-width mobile, fonte 0.85rem
|
||||
- Desktop: Tabela completa preservada
|
||||
- i18n: Adicionadas chaves 'monthly', 'annual', 'total' em pt-BR e es
|
||||
|
||||
|
||||
## [1.43.5] - 2025-12-16
|
||||
|
||||
### Improved
|
||||
|
||||
@ -395,8 +395,11 @@
|
||||
"dueDate": "Vencimiento",
|
||||
"paid": "Pagado",
|
||||
"monthlyRate": "Tasa Mensual",
|
||||
"monthly": "Mensual",
|
||||
"annualRate": "Tasa Anual",
|
||||
"annual": "Anual",
|
||||
"totalRate": "Tasa Total",
|
||||
"total": "Total",
|
||||
"progress": "Progreso",
|
||||
"paymentProgress": "Progreso de Pago",
|
||||
"markPaid": "Marcar como Pagado",
|
||||
|
||||
@ -397,8 +397,11 @@
|
||||
"dueDate": "Vencimento",
|
||||
"paid": "Pago",
|
||||
"monthlyRate": "Taxa Mensal",
|
||||
"monthly": "Mensal",
|
||||
"annualRate": "Taxa Anual",
|
||||
"annual": "Anual",
|
||||
"totalRate": "Taxa Total",
|
||||
"total": "Total",
|
||||
"progress": "Progresso",
|
||||
"paymentProgress": "Progresso de Pagamento",
|
||||
"markPaid": "Marcar como Pago",
|
||||
|
||||
@ -729,13 +729,13 @@ const LiabilityAccounts = () => {
|
||||
{/* Detail Modal */}
|
||||
{showDetailModal && selectedAccount && (
|
||||
<div className="modal show d-block" tabIndex="-1" style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}>
|
||||
<div className="modal-dialog modal-xl modal-dialog-scrollable">
|
||||
<div className={`modal-dialog ${isMobile ? 'modal-fullscreen' : 'modal-xl'} modal-dialog-scrollable`}>
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title d-flex align-items-center">
|
||||
<div className="modal-header" style={{ padding: isMobile ? '0.75rem' : '1rem' }}>
|
||||
<h5 className="modal-title d-flex align-items-center" style={{ fontSize: isMobile ? '0.95rem' : '1.25rem' }}>
|
||||
<i className={`${selectedAccount.icon || 'bi-file-earmark-text'} me-2`}
|
||||
style={{ color: selectedAccount.color }}></i>
|
||||
{selectedAccount.name}
|
||||
style={{ color: selectedAccount.color, fontSize: isMobile ? '0.9rem' : '1rem' }}></i>
|
||||
<span className="text-truncate">{selectedAccount.name}</span>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-link text-info p-0 ms-2"
|
||||
@ -751,105 +751,197 @@ const LiabilityAccounts = () => {
|
||||
onClick={handleCloseDetail}
|
||||
></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="modal-body" style={{ padding: isMobile ? '0.75rem' : '1rem' }}>
|
||||
{/* Contract Type / Description */}
|
||||
{selectedAccount.description && (
|
||||
<div className="alert alert-info mb-4">
|
||||
<div className="alert alert-info mb-3" style={{ fontSize: isMobile ? '0.75rem' : '0.875rem', padding: isMobile ? '0.5rem' : '0.75rem' }}>
|
||||
<i className="bi bi-info-circle me-2"></i>
|
||||
<strong>{t('liabilities.contractType')}:</strong> {selectedAccount.description}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Summary Cards */}
|
||||
<div className="row g-3 mb-4">
|
||||
<div className="col-md-3">
|
||||
<div className="row g-2 mb-3">
|
||||
<div className={isMobile ? "col-6" : "col-md-3"}>
|
||||
<div className="card bg-light">
|
||||
<div className="card-body py-2 text-center">
|
||||
<div className="small text-muted">{t('liabilities.principal')}</div>
|
||||
<div className="h5 mb-0">{formatCurrency(selectedAccount.principal_amount, selectedAccount.currency)}</div>
|
||||
<div className="card-body text-center" style={{ padding: isMobile ? '0.5rem' : '0.75rem' }}>
|
||||
<div className="text-muted" style={{ fontSize: isMobile ? '0.65rem' : '0.875rem' }}>{t('liabilities.principal')}</div>
|
||||
<div className="mb-0 fw-bold" style={{ fontSize: isMobile ? '0.85rem' : '1.25rem' }}>{formatCurrency(selectedAccount.principal_amount, selectedAccount.currency)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<div className={isMobile ? "col-6" : "col-md-3"}>
|
||||
<div className="card bg-light">
|
||||
<div className="card-body py-2 text-center">
|
||||
<div className="small text-muted">{t('liabilities.totalInterest')}</div>
|
||||
<div className="h5 mb-0">{formatCurrency(selectedAccount.total_interest, selectedAccount.currency)}</div>
|
||||
<div className="card-body text-center" style={{ padding: isMobile ? '0.5rem' : '0.75rem' }}>
|
||||
<div className="text-muted" style={{ fontSize: isMobile ? '0.65rem' : '0.875rem' }}>{t('liabilities.totalInterest')}</div>
|
||||
<div className="mb-0 fw-bold" style={{ fontSize: isMobile ? '0.85rem' : '1.25rem' }}>{formatCurrency(selectedAccount.total_interest, selectedAccount.currency)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<div className={isMobile ? "col-6" : "col-md-3"}>
|
||||
<div className="card bg-light">
|
||||
<div className="card-body py-2 text-center">
|
||||
<div className="small text-muted">{t('liabilities.totalFees')}</div>
|
||||
<div className="h5 mb-0 text-warning">{formatCurrency(selectedAccount.total_fees, selectedAccount.currency)}</div>
|
||||
<div className="small text-muted">{t('liabilities.extraCharges')}</div>
|
||||
<div className="card-body text-center" style={{ padding: isMobile ? '0.5rem' : '0.75rem' }}>
|
||||
<div className="text-muted" style={{ fontSize: isMobile ? '0.65rem' : '0.875rem' }}>{t('liabilities.totalFees')}</div>
|
||||
<div className="mb-0 text-warning fw-bold" style={{ fontSize: isMobile ? '0.85rem' : '1.25rem' }}>{formatCurrency(selectedAccount.total_fees, selectedAccount.currency)}</div>
|
||||
{!isMobile && <div className="small text-muted">{t('liabilities.extraCharges')}</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<div className={isMobile ? "col-6" : "col-md-3"}>
|
||||
<div className="card bg-light">
|
||||
<div className="card-body py-2 text-center">
|
||||
<div className="small text-muted">{t('liabilities.totalContract')}</div>
|
||||
<div className="h5 mb-0">{formatCurrency(selectedAccount.total_contract_value, selectedAccount.currency)}</div>
|
||||
<div className="card-body text-center" style={{ padding: isMobile ? '0.5rem' : '0.75rem' }}>
|
||||
<div className="text-muted" style={{ fontSize: isMobile ? '0.65rem' : '0.875rem' }}>{t('liabilities.totalContract')}</div>
|
||||
<div className="mb-0 fw-bold" style={{ fontSize: isMobile ? '0.85rem' : '1.25rem' }}>{formatCurrency(selectedAccount.total_contract_value, selectedAccount.currency)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Interest Rates */}
|
||||
<div className="row g-3 mb-4">
|
||||
<div className="col-md-4">
|
||||
<div className="row g-2 mb-3">
|
||||
<div className={isMobile ? "col-4" : "col-md-4"}>
|
||||
<div className="card border-info">
|
||||
<div className="card-body py-2 text-center">
|
||||
<div className="small text-muted">{t('liabilities.monthlyRate')}</div>
|
||||
<div className="h4 mb-0 text-info">{formatPercent(selectedAccount.monthly_interest_rate)}</div>
|
||||
<div className="card-body text-center" style={{ padding: isMobile ? '0.5rem' : '0.75rem' }}>
|
||||
<div className="text-muted" style={{ fontSize: isMobile ? '0.6rem' : '0.875rem' }}>{isMobile ? t('liabilities.monthly') : t('liabilities.monthlyRate')}</div>
|
||||
<div className="mb-0 text-info fw-bold" style={{ fontSize: isMobile ? '0.9rem' : '1.5rem' }}>{formatPercent(selectedAccount.monthly_interest_rate)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
<div className={isMobile ? "col-4" : "col-md-4"}>
|
||||
<div className="card border-info">
|
||||
<div className="card-body py-2 text-center">
|
||||
<div className="small text-muted">{t('liabilities.annualRate')}</div>
|
||||
<div className="h4 mb-0 text-info">{formatPercent(selectedAccount.annual_interest_rate)}</div>
|
||||
<div className="card-body text-center" style={{ padding: isMobile ? '0.5rem' : '0.75rem' }}>
|
||||
<div className="text-muted" style={{ fontSize: isMobile ? '0.6rem' : '0.875rem' }}>{isMobile ? t('liabilities.annual') : t('liabilities.annualRate')}</div>
|
||||
<div className="mb-0 text-info fw-bold" style={{ fontSize: isMobile ? '0.9rem' : '1.5rem' }}>{formatPercent(selectedAccount.annual_interest_rate)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
<div className={isMobile ? "col-4" : "col-md-4"}>
|
||||
<div className="card border-info">
|
||||
<div className="card-body py-2 text-center">
|
||||
<div className="small text-muted">{t('liabilities.totalRate')}</div>
|
||||
<div className="h4 mb-0 text-info">{formatPercent(selectedAccount.total_interest_rate)}</div>
|
||||
<div className="card-body text-center" style={{ padding: isMobile ? '0.5rem' : '0.75rem' }}>
|
||||
<div className="text-muted" style={{ fontSize: isMobile ? '0.6rem' : '0.875rem' }}>{isMobile ? t('liabilities.total') : t('liabilities.totalRate')}</div>
|
||||
<div className="mb-0 text-info fw-bold" style={{ fontSize: isMobile ? '0.9rem' : '1.5rem' }}>{formatPercent(selectedAccount.total_interest_rate)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Progress */}
|
||||
<div className="mb-4">
|
||||
<div className="d-flex justify-content-between mb-2">
|
||||
<div className="mb-3">
|
||||
<div className="d-flex justify-content-between mb-2" style={{ fontSize: isMobile ? '0.75rem' : '0.875rem' }}>
|
||||
<span>{t('liabilities.paymentProgress')}</span>
|
||||
<span className="fw-bold">{selectedAccount.paid_installments}/{selectedAccount.total_installments} {t('liabilities.installments').toLowerCase()}</span>
|
||||
<span className="fw-bold">{selectedAccount.paid_installments}/{selectedAccount.total_installments} {isMobile ? '' : t('liabilities.installments').toLowerCase()}</span>
|
||||
</div>
|
||||
<div className="progress" style={{ height: '20px' }}>
|
||||
<div className="progress" style={{ height: isMobile ? '12px' : '20px' }}>
|
||||
<div
|
||||
className="progress-bar bg-success"
|
||||
style={{ width: `${selectedAccount.progress_percentage || 0}%` }}
|
||||
style={{ width: `${selectedAccount.progress_percentage || 0}%`, fontSize: isMobile ? '0.65rem' : '0.75rem' }}
|
||||
>
|
||||
{selectedAccount.progress_percentage}%
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex justify-content-between small text-muted mt-1">
|
||||
<div className="d-flex justify-content-between text-muted mt-1" style={{ fontSize: isMobile ? '0.65rem' : '0.875rem' }}>
|
||||
<span>{t('liabilities.paid')}: {formatCurrency(selectedAccount.principal_paid, selectedAccount.currency)}</span>
|
||||
<span>{t('liabilities.remaining')}: {formatCurrency(selectedAccount.remaining_balance, selectedAccount.currency)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Installments Table */}
|
||||
<h6 className="mb-3">
|
||||
<h6 className="mb-3" style={{ fontSize: isMobile ? '0.85rem' : '1rem' }}>
|
||||
<i className="bi bi-list-ol me-2"></i>
|
||||
{t('liabilities.installmentsList')}
|
||||
</h6>
|
||||
|
||||
{isMobile ? (
|
||||
/* Mobile: Cards layout */
|
||||
<div className="d-flex flex-column gap-2">
|
||||
{selectedAccount.installments?.map(inst => (
|
||||
<div
|
||||
key={inst.id}
|
||||
className={`card border ${inst.status === 'paid' ? 'border-success bg-success bg-opacity-10' : inst.is_overdue ? 'border-danger bg-danger bg-opacity-10' : 'border-secondary'}`}
|
||||
>
|
||||
<div className="card-body p-2">
|
||||
{/* Header */}
|
||||
<div className="d-flex justify-content-between align-items-center mb-2">
|
||||
<div className="fw-bold" style={{ fontSize: '0.85rem' }}>
|
||||
<i className="bi bi-hash"></i>{inst.installment_number} - {formatDate(inst.due_date)}
|
||||
</div>
|
||||
<span className={`badge ${getStatusBadge(inst.status)}`} style={{ fontSize: '0.65rem' }}>
|
||||
{getStatusLabel(inst.status, true)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Values */}
|
||||
<div className="row g-1 mb-2" style={{ fontSize: '0.7rem' }}>
|
||||
<div className="col-6">
|
||||
<div className="text-muted" style={{ fontSize: '0.6rem' }}>{t('liabilities.installmentAmount')}</div>
|
||||
<div className="fw-bold">{formatCurrency(inst.installment_amount, selectedAccount.currency)}</div>
|
||||
</div>
|
||||
{inst.paid_amount > 0 && (
|
||||
<div className="col-6">
|
||||
<div className="text-muted" style={{ fontSize: '0.6rem' }}>{t('liabilities.paidAmount')}</div>
|
||||
<div className={`fw-bold ${inst.paid_amount > inst.installment_amount ? 'text-warning' : 'text-success'}`}>
|
||||
{formatCurrency(inst.paid_amount, selectedAccount.currency)}
|
||||
{inst.paid_amount > inst.installment_amount && <i className="bi bi-arrow-up-circle-fill ms-1"></i>}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="col-4">
|
||||
<div className="text-muted" style={{ fontSize: '0.6rem' }}>{t('liabilities.capital')}</div>
|
||||
<div>{formatCurrency(inst.principal_amount, selectedAccount.currency)}</div>
|
||||
</div>
|
||||
<div className="col-4">
|
||||
<div className="text-muted" style={{ fontSize: '0.6rem' }}>{t('liabilities.interest')}</div>
|
||||
<div>{formatCurrency(inst.interest_amount, selectedAccount.currency)}</div>
|
||||
</div>
|
||||
{inst.fee_amount > 0 && (
|
||||
<div className="col-4">
|
||||
<div className="text-muted" style={{ fontSize: '0.6rem' }}>{t('liabilities.fees')}</div>
|
||||
<div className="text-warning">{formatCurrency(inst.fee_amount, selectedAccount.currency)}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Reconciliation */}
|
||||
{inst.reconciled_transaction_id ? (
|
||||
<div className="d-flex justify-content-between align-items-center">
|
||||
<span className="badge bg-info" style={{ fontSize: '0.65rem' }}>
|
||||
<i className="bi bi-link-45deg me-1"></i>{t('liabilities.reconciled')}
|
||||
</span>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-warning"
|
||||
onClick={() => handleUnreconcile(inst)}
|
||||
disabled={saving}
|
||||
style={{ fontSize: '0.7rem', padding: '0.25rem 0.5rem' }}
|
||||
>
|
||||
<i className="bi bi-link-45deg"></i>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="d-flex gap-1">
|
||||
{inst.status !== 'paid' && (
|
||||
<button
|
||||
className="btn btn-sm btn-outline-success flex-fill"
|
||||
onClick={() => handleMarkInstallmentPaid(inst)}
|
||||
style={{ fontSize: '0.7rem', padding: '0.25rem' }}
|
||||
>
|
||||
<i className="bi bi-check me-1"></i>{t('liabilities.markPaid')}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="btn btn-sm btn-outline-primary flex-fill"
|
||||
onClick={() => handleOpenReconcileModal(inst)}
|
||||
style={{ fontSize: '0.7rem', padding: '0.25rem' }}
|
||||
>
|
||||
<i className="bi bi-link me-1"></i>{t('liabilities.reconcile')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
/* Desktop: Table layout */
|
||||
<div className="table-responsive">
|
||||
<table className="table table-sm table-hover">
|
||||
<thead className="table-light">
|
||||
@ -948,12 +1040,14 @@ const LiabilityAccounts = () => {
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<div className="modal-footer" style={{ padding: isMobile ? '0.75rem' : '1rem' }}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
className={`btn btn-secondary ${isMobile ? 'w-100' : ''}`}
|
||||
onClick={handleCloseDetail}
|
||||
style={{ fontSize: isMobile ? '0.85rem' : '1rem' }}
|
||||
>
|
||||
{t('common.close')}
|
||||
</button>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user