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:
marcoitaloesp-ai 2025-12-16 13:02:58 +00:00 committed by GitHub
parent 454ef516f4
commit 1637e5da0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 164 additions and 48 deletions

View File

@ -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

View File

@ -1 +1 @@
1.43.5
1.43.6

View File

@ -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",

View File

@ -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",

View File

@ -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>