v1.43.4 - Padronização de badges e botões em todo o sistema
- Badges: Estilo translúcido uniforme (bg-opacity-25 + text-color) via CSS global - Afetados: RecurringTransactions, Accounts, Categories, TransactionsByWeek - Widgets: UpcomingWidget, OverdueWidget, CalendarWidget, OverpaymentsAnalysis - Botões: Estilo outline padronizado (btn-outline-*) em RecurringTransactions - Simplificação: Remover classes redundantes dos JSX
This commit is contained in:
parent
9800f987df
commit
5f3bf18b99
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/).
|
Este projeto adota [Versionamento Semântico](https://semver.org/pt-BR/).
|
||||||
|
|
||||||
|
|
||||||
|
## [1.43.4] - 2025-12-16
|
||||||
|
|
||||||
|
### Improved
|
||||||
|
- **Badges Padronizados** - Estilo translúcido consistente em todo o sistema
|
||||||
|
- Padrão global via CSS: `bg-{color}` → estilo `bg-opacity-25 text-{color}` automático
|
||||||
|
- Aparência moderna e uniforme: background translúcido + texto colorido
|
||||||
|
- Afetados: RecurringTransactions, Accounts, Categories, TransactionsByWeek, FinancialHealth, CostCenters
|
||||||
|
- Widgets: UpcomingWidget, OverdueWidget, CalendarWidget, OverpaymentsAnalysis
|
||||||
|
- Simplificação: remover classes redundantes dos JSX, CSS aplica estilo
|
||||||
|
|
||||||
|
- **Botões de Ação Padronizados** - RecurringTransactions
|
||||||
|
- Estilo outline consistente: `btn-outline-{color}` em todas as abas
|
||||||
|
- Templates e Instâncias usam mesmo padrão visual
|
||||||
|
- Info (visualizar), Primary (executar), Success (editar), Warning (adiar), Danger (excluir)
|
||||||
|
|
||||||
|
|
||||||
## [1.43.3] - 2025-12-16
|
## [1.43.3] - 2025-12-16
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|||||||
@ -606,7 +606,7 @@ const CalendarWidget = () => {
|
|||||||
{item.description}
|
{item.description}
|
||||||
</span>
|
</span>
|
||||||
{item.type === 'recurring' && (
|
{item.type === 'recurring' && (
|
||||||
<span className="badge bg-warning text-dark" style={{ fontSize: '9px' }}>
|
<span className="badge bg-warning" style={{ fontSize: '9px' }}>
|
||||||
#{item.occurrence_number}
|
#{item.occurrence_number}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -226,7 +226,7 @@ const OverpaymentsAnalysis = ({ data, loading, onTransactionClick }) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{(!isMobile || isExpanded) && (
|
{(!isMobile || isExpanded) && (
|
||||||
<span className={`badge bg-warning text-dark ${isMobile ? 'px-2 py-1' : 'px-3 py-2'}`}>
|
<span className={`badge bg-warning ${isMobile ? 'px-2 py-1' : 'px-3 py-2'}`}>
|
||||||
<i className="bi bi-arrow-up-right me-1"></i>
|
<i className="bi bi-arrow-up-right me-1"></i>
|
||||||
Total: {currency(totalOverpayment, 'BRL')}
|
Total: {currency(totalOverpayment, 'BRL')}
|
||||||
</span>
|
</span>
|
||||||
@ -322,7 +322,7 @@ const OverpaymentsAnalysis = ({ data, loading, onTransactionClick }) => {
|
|||||||
<span className="text-white" style={{ fontSize: '0.75rem', fontWeight: '500' }}>
|
<span className="text-white" style={{ fontSize: '0.75rem', fontWeight: '500' }}>
|
||||||
{tx.description.length > 25 ? tx.description.substring(0, 25) + '...' : tx.description}
|
{tx.description.length > 25 ? tx.description.substring(0, 25) + '...' : tx.description}
|
||||||
</span>
|
</span>
|
||||||
<span className="badge bg-warning text-dark ms-2" style={{ fontSize: '0.65rem' }}>
|
<span className="badge bg-warning ms-2" style={{ fontSize: '0.65rem' }}>
|
||||||
+{currency(tx.variance, 'BRL')}
|
+{currency(tx.variance, 'BRL')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -404,7 +404,7 @@ const OverpaymentsAnalysis = ({ data, loading, onTransactionClick }) => {
|
|||||||
{currency(tx.actual_amount, 'BRL')}
|
{currency(tx.actual_amount, 'BRL')}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-end border-0">
|
<td className="text-end border-0">
|
||||||
<span className="badge bg-warning text-dark small">
|
<span className="badge bg-warning small">
|
||||||
<i className="bi bi-arrow-up me-1" style={{ fontSize: '10px' }}></i>
|
<i className="bi bi-arrow-up me-1" style={{ fontSize: '10px' }}></i>
|
||||||
+{currency(tx.variance, 'BRL')}
|
+{currency(tx.variance, 'BRL')}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -206,7 +206,7 @@ const UpcomingWidget = () => {
|
|||||||
{item.account?.name || '-'}
|
{item.account?.name || '-'}
|
||||||
</small>
|
</small>
|
||||||
{item.type === 'recurring' && (
|
{item.type === 'recurring' && (
|
||||||
<span className="badge bg-warning text-dark" style={{ fontSize: '8px', padding: '1px 4px' }}>
|
<span className="badge bg-warning" style={{ fontSize: '8px', padding: '1px 4px' }}>
|
||||||
#{item.occurrence_number}
|
#{item.occurrence_number}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -2878,3 +2878,11 @@ a,
|
|||||||
.category-dropdown-scroll::-webkit-scrollbar-thumb:hover {
|
.category-dropdown-scroll::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(100, 116, 139, 0.7);
|
background: rgba(100, 116, 139, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Badges padronizados - estilo outline */
|
||||||
|
.badge.bg-primary { background-color: rgba(59, 130, 246, 0.25) !important; color: #3b82f6 !important; }
|
||||||
|
.badge.bg-success { background-color: rgba(34, 197, 94, 0.25) !important; color: #22c55e !important; }
|
||||||
|
.badge.bg-danger { background-color: rgba(239, 68, 68, 0.25) !important; color: #ef4444 !important; }
|
||||||
|
.badge.bg-warning { background-color: rgba(234, 179, 8, 0.25) !important; color: #eab308 !important; }
|
||||||
|
.badge.bg-info { background-color: rgba(14, 165, 233, 0.25) !important; color: #0ea5e9 !important; }
|
||||||
|
.badge.bg-secondary { background-color: rgba(148, 163, 184, 0.25) !important; color: #94a3b8 !important; }
|
||||||
|
|||||||
@ -508,9 +508,9 @@ const Accounts = () => {
|
|||||||
{accountTypes[account.type] || account.type}
|
{accountTypes[account.type] || account.type}
|
||||||
</span>
|
</span>
|
||||||
{account.is_active ? (
|
{account.is_active ? (
|
||||||
<span className="badge bg-success bg-opacity-25 text-success" style={{ fontSize: '0.65rem' }}>{t('common.active')}</span>
|
<span className="badge bg-success" style={{ fontSize: '0.65rem' }}>{t('common.active')}</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="badge bg-secondary bg-opacity-25 text-secondary" style={{ fontSize: '0.65rem' }}>{t('common.inactive')}</span>
|
<span className="badge bg-secondary" style={{ fontSize: '0.65rem' }}>{t('common.inactive')}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={`fw-bold ${parseFloat(account.current_balance) >= 0 ? 'text-success' : 'text-danger'}`} style={{ fontSize: '0.95rem' }}>
|
<div className={`fw-bold ${parseFloat(account.current_balance) >= 0 ? 'text-success' : 'text-danger'}`} style={{ fontSize: '0.95rem' }}>
|
||||||
@ -575,9 +575,9 @@ const Accounts = () => {
|
|||||||
</td>
|
</td>
|
||||||
<td className="py-3 text-center" style={{ backgroundColor: 'transparent' }}>
|
<td className="py-3 text-center" style={{ backgroundColor: 'transparent' }}>
|
||||||
{account.is_active ? (
|
{account.is_active ? (
|
||||||
<span className="badge bg-success bg-opacity-25 text-success">{t('common.active')}</span>
|
<span className="badge bg-success">{t('common.active')}</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="badge bg-secondary bg-opacity-25 text-secondary">{t('common.inactive')}</span>
|
<span className="badge bg-secondary">{t('common.inactive')}</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="py-3 text-end pe-4" style={{ backgroundColor: 'transparent' }}>
|
<td className="py-3 text-end pe-4" style={{ backgroundColor: 'transparent' }}>
|
||||||
@ -664,15 +664,15 @@ const Accounts = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{liability.status === 'active' ? (
|
{liability.status === 'active' ? (
|
||||||
<span className="badge bg-primary bg-opacity-25 text-primary" style={{ fontSize: '0.65rem' }}>
|
<span className="badge bg-primary" style={{ fontSize: '0.65rem' }}>
|
||||||
{t('common.active')}
|
{t('common.active')}
|
||||||
</span>
|
</span>
|
||||||
) : liability.status === 'paid_off' ? (
|
) : liability.status === 'paid_off' ? (
|
||||||
<span className="badge bg-success bg-opacity-25 text-success" style={{ fontSize: '0.65rem' }}>
|
<span className="badge bg-success" style={{ fontSize: '0.65rem' }}>
|
||||||
{t('liabilities.paid')}
|
{t('liabilities.paid')}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="badge bg-secondary bg-opacity-25 text-secondary" style={{ fontSize: '0.65rem' }}>
|
<span className="badge bg-secondary" style={{ fontSize: '0.65rem' }}>
|
||||||
{liability.status}
|
{liability.status}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@ -783,11 +783,11 @@ const Accounts = () => {
|
|||||||
</td>
|
</td>
|
||||||
<td className="py-3 text-center" style={{ backgroundColor: 'transparent' }}>
|
<td className="py-3 text-center" style={{ backgroundColor: 'transparent' }}>
|
||||||
{liability.status === 'active' ? (
|
{liability.status === 'active' ? (
|
||||||
<span className="badge bg-primary bg-opacity-25 text-primary">{t('common.active')}</span>
|
<span className="badge bg-primary">{t('common.active')}</span>
|
||||||
) : liability.status === 'paid_off' ? (
|
) : liability.status === 'paid_off' ? (
|
||||||
<span className="badge bg-success bg-opacity-25 text-success">{t('liabilities.paid')}</span>
|
<span className="badge bg-success">{t('liabilities.paid')}</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="badge bg-secondary bg-opacity-25 text-secondary">{liability.status}</span>
|
<span className="badge bg-secondary">{liability.status}</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -306,9 +306,9 @@ const Categories = () => {
|
|||||||
{/* Status */}
|
{/* Status */}
|
||||||
<div className="me-3">
|
<div className="me-3">
|
||||||
{category.is_active ? (
|
{category.is_active ? (
|
||||||
<span className="badge bg-success bg-opacity-25 text-success">{t('common.active')}</span>
|
<span className="badge bg-success">{t('common.active')}</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="badge bg-secondary bg-opacity-25 text-secondary">{t('common.inactive')}</span>
|
<span className="badge bg-secondary">{t('common.inactive')}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -759,7 +759,7 @@ const Categories = () => {
|
|||||||
{item.description}
|
{item.description}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span className="badge bg-warning text-dark">{item.matched_keyword}</span>
|
<span className="badge bg-warning">{item.matched_keyword}</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="text-info">{item.category_name}</td>
|
<td className="text-info">{item.category_name}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -345,9 +345,9 @@ const CostCenters = () => {
|
|||||||
{/* Status */}
|
{/* Status */}
|
||||||
<div className="d-flex justify-content-between align-items-center">
|
<div className="d-flex justify-content-between align-items-center">
|
||||||
{item.is_active ? (
|
{item.is_active ? (
|
||||||
<span className="badge bg-success bg-opacity-25 text-success">{t('common.active')}</span>
|
<span className="badge bg-success">{t('common.active')}</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="badge bg-secondary bg-opacity-25 text-secondary">{t('common.inactive')}</span>
|
<span className="badge bg-secondary">{t('common.inactive')}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -847,7 +847,7 @@ const FinancialHealth = () => {
|
|||||||
<span className="text-white">{t(`financialHealth.trend.${data.trends?.income_trend?.direction}`)}</span>
|
<span className="text-white">{t(`financialHealth.trend.${data.trends?.income_trend?.direction}`)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="badge bg-success bg-opacity-25 text-success">
|
<span className="badge bg-success">
|
||||||
{data.trends?.income_trend?.strength}%
|
{data.trends?.income_trend?.strength}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,6 +7,9 @@ import { recurringService, accountService, categoryService } from '../services/a
|
|||||||
const RecurringTransactions = () => {
|
const RecurringTransactions = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
// Mobile detection
|
||||||
|
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const [templates, setTemplates] = useState([]);
|
const [templates, setTemplates] = useState([]);
|
||||||
const [pendingInstances, setPendingInstances] = useState([]);
|
const [pendingInstances, setPendingInstances] = useState([]);
|
||||||
@ -80,6 +83,13 @@ const RecurringTransactions = () => {
|
|||||||
loadData();
|
loadData();
|
||||||
}, [loadData]);
|
}, [loadData]);
|
||||||
|
|
||||||
|
// Mobile resize detection
|
||||||
|
useEffect(() => {
|
||||||
|
const handleResize = () => setIsMobile(window.innerWidth < 768);
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
return () => window.removeEventListener('resize', handleResize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Toast helper
|
// Toast helper
|
||||||
const showToast = (message, type = 'success') => {
|
const showToast = (message, type = 'success') => {
|
||||||
setToast({ show: true, message, type });
|
setToast({ show: true, message, type });
|
||||||
@ -314,7 +324,7 @@ const RecurringTransactions = () => {
|
|||||||
>
|
>
|
||||||
<i className="bi bi-clock me-2" />
|
<i className="bi bi-clock me-2" />
|
||||||
{t('recurring.pendingInstances')}
|
{t('recurring.pendingInstances')}
|
||||||
<span className="badge bg-warning text-dark ms-2">{pendingInstances.length}</span>
|
<span className="badge bg-warning ms-2">{pendingInstances.length}</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -379,7 +389,88 @@ const RecurringTransactions = () => {
|
|||||||
<i className="bi bi-inbox display-1" />
|
<i className="bi bi-inbox display-1" />
|
||||||
<p className="mt-3">{t('recurring.noTemplates')}</p>
|
<p className="mt-3">{t('recurring.noTemplates')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
) : isMobile ? (
|
||||||
|
// Mobile: Cards Layout
|
||||||
|
<div className="d-flex flex-column gap-2 p-2">
|
||||||
|
{templates.map((template) => (
|
||||||
|
<div key={template.id} className="card border-secondary" style={{ background: '#0f172a' }}>
|
||||||
|
<div className="card-body p-3">
|
||||||
|
{/* Header: Nome + Status */}
|
||||||
|
<div className="d-flex justify-content-between align-items-start mb-2">
|
||||||
|
<div className="flex-grow-1" style={{ minWidth: 0 }}>
|
||||||
|
<div className="fw-bold text-white mb-1" style={{ fontSize: '0.9rem' }}>
|
||||||
|
{template.name}
|
||||||
|
</div>
|
||||||
|
{template.transaction_description && (
|
||||||
|
<div className="text-muted" style={{ fontSize: '0.7rem' }}>
|
||||||
|
{template.transaction_description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className={`badge ${template.is_active ? 'bg-success' : 'bg-secondary'} ms-2`} style={{ fontSize: '0.65rem' }}>
|
||||||
|
{template.is_active ? t('common.active') : t('common.inactive')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Frequência + Tipo */}
|
||||||
|
<div className="d-flex gap-2 mb-2 flex-wrap">
|
||||||
|
<span className="badge bg-primary" style={{ fontSize: '0.7rem' }}>
|
||||||
|
<i className="bi bi-clock me-1"></i>
|
||||||
|
{t(`recurring.frequencies.${template.frequency}`, template.frequency)}
|
||||||
|
{template.frequency_interval > 1 && ` (x${template.frequency_interval})`}
|
||||||
|
</span>
|
||||||
|
<span className={`badge ${template.type === 'expense' ? 'bg-danger' : 'bg-success'}`} style={{ fontSize: '0.7rem' }}>
|
||||||
|
{t(`transactions.${template.type}`)}
|
||||||
|
</span>
|
||||||
|
{(template.pending_instances_count || 0) > 0 && (
|
||||||
|
<span className="badge bg-warning" style={{ fontSize: '0.7rem' }}>
|
||||||
|
{template.pending_instances_count} {t('recurring.pendingInstances')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Valor + Ações */}
|
||||||
|
<div className="d-flex justify-content-between align-items-center pt-2" style={{ borderTop: '1px solid #334155' }}>
|
||||||
|
<div className={`fw-bold ${template.type === 'expense' ? 'text-danger' : 'text-success'}`} style={{ fontSize: '1rem' }}>
|
||||||
|
{formatCurrency(template.planned_amount)}
|
||||||
|
</div>
|
||||||
|
<div className="btn-group btn-group-sm">
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-info"
|
||||||
|
onClick={() => handleViewInstances(template)}
|
||||||
|
title={t('recurring.actions.viewInstances')}
|
||||||
|
>
|
||||||
|
<i className="bi bi-eye" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-primary"
|
||||||
|
onClick={() => handleEditTemplate(template)}
|
||||||
|
title={t('common.edit')}
|
||||||
|
>
|
||||||
|
<i className="bi bi-pencil" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`btn btn-sm btn-outline-${template.is_active ? 'warning' : 'success'}`}
|
||||||
|
onClick={() => handlePauseResume(template)}
|
||||||
|
title={template.is_active ? t('recurring.actions.pause') : t('recurring.actions.resume')}
|
||||||
|
>
|
||||||
|
<i className={`bi bi-${template.is_active ? 'pause' : 'play'}`} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-danger"
|
||||||
|
onClick={() => handleDelete(template)}
|
||||||
|
title={t('common.delete')}
|
||||||
|
>
|
||||||
|
<i className="bi bi-trash" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
// Desktop: Table Layout
|
||||||
<div className="table-responsive">
|
<div className="table-responsive">
|
||||||
<table className="table table-dark table-hover">
|
<table className="table table-dark table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
@ -417,7 +508,7 @@ const RecurringTransactions = () => {
|
|||||||
{formatCurrency(template.planned_amount)}
|
{formatCurrency(template.planned_amount)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-center">
|
<td className="text-center">
|
||||||
<span className="badge bg-warning text-dark">
|
<span className="badge bg-warning">
|
||||||
{template.pending_instances_count || 0}
|
{template.pending_instances_count || 0}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
@ -479,7 +570,90 @@ const RecurringTransactions = () => {
|
|||||||
<i className="bi bi-check-circle display-1" />
|
<i className="bi bi-check-circle display-1" />
|
||||||
<p className="mt-3">{t('recurring.noPendingInstances')}</p>
|
<p className="mt-3">{t('recurring.noPendingInstances')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
) : isMobile ? (
|
||||||
|
// Mobile: Cards Layout
|
||||||
|
<div className="d-flex flex-column gap-2 p-2">
|
||||||
|
{pendingInstances.map((instance) => {
|
||||||
|
const days = Math.ceil((new Date(instance.due_date) - new Date()) / (1000 * 60 * 60 * 24));
|
||||||
|
const isOverdue = days < 0;
|
||||||
|
const isDueToday = days === 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={instance.id} className={`card border-${isOverdue ? 'danger' : isDueToday ? 'warning' : 'secondary'}`} style={{ background: '#0f172a' }}>
|
||||||
|
<div className="card-body p-3">
|
||||||
|
{/* Header: Template + Status */}
|
||||||
|
<div className="d-flex justify-content-between align-items-start mb-2">
|
||||||
|
<div className="flex-grow-1" style={{ minWidth: 0 }}>
|
||||||
|
<div className="fw-bold text-white mb-1" style={{ fontSize: '0.9rem' }}>
|
||||||
|
{instance.template?.name}
|
||||||
|
</div>
|
||||||
|
<div className="text-muted" style={{ fontSize: '0.7rem' }}>
|
||||||
|
{instance.template?.account?.name} • #{instance.occurrence_number}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className={`badge ${getStatusBadge(instance.status)} ms-2`} style={{ fontSize: '0.65rem' }}>
|
||||||
|
{t(`recurring.status.${instance.status}`)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Data de vencimento */}
|
||||||
|
<div className="mb-2">
|
||||||
|
<div className={`${isOverdue ? 'text-danger' : isDueToday ? 'text-warning' : 'text-slate-300'}`} style={{ fontSize: '0.75rem' }}>
|
||||||
|
<i className="bi bi-calendar me-1"></i>
|
||||||
|
{formatDate(instance.due_date)}
|
||||||
|
{instance.status === 'pending' && (
|
||||||
|
<span className="ms-2">
|
||||||
|
{isOverdue && `(${Math.abs(days)} ${t('recurring.daysOverdue')})`}
|
||||||
|
{isDueToday && `(${t('recurring.dueToday')})`}
|
||||||
|
{!isOverdue && !isDueToday && `(${days} ${t('recurring.daysUntilDue')})`}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Valor + Ações */}
|
||||||
|
<div className="d-flex justify-content-between align-items-center pt-2" style={{ borderTop: '1px solid #334155' }}>
|
||||||
|
<div className="fw-bold text-white" style={{ fontSize: '1rem' }}>
|
||||||
|
{formatCurrency(instance.planned_amount)}
|
||||||
|
</div>
|
||||||
|
<div className="btn-group btn-group-sm">
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-success"
|
||||||
|
onClick={() => handlePayInstance(instance)}
|
||||||
|
title={t('recurring.actions.pay')}
|
||||||
|
>
|
||||||
|
<i className="bi bi-check-lg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-primary"
|
||||||
|
onClick={() => handleReconcileInstance(instance)}
|
||||||
|
title={t('recurring.actions.reconcile')}
|
||||||
|
>
|
||||||
|
<i className="bi bi-link-45deg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-info"
|
||||||
|
onClick={() => handleEditInstance(instance)}
|
||||||
|
title={t('common.edit')}
|
||||||
|
>
|
||||||
|
<i className="bi bi-pencil" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-warning"
|
||||||
|
onClick={() => handleSkipInstance(instance)}
|
||||||
|
title={t('recurring.actions.skip')}
|
||||||
|
>
|
||||||
|
<i className="bi bi-skip-forward" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
// Desktop: Table Layout
|
||||||
<div className="table-responsive">
|
<div className="table-responsive">
|
||||||
<table className="table table-dark table-hover">
|
<table className="table table-dark table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@ -1414,7 +1414,7 @@ export default function Transactions() {
|
|||||||
|
|
||||||
{/* Account + Category */}
|
{/* Account + Category */}
|
||||||
<div className="d-flex flex-wrap gap-2 mb-2">
|
<div className="d-flex flex-wrap gap-2 mb-2">
|
||||||
<span className="badge bg-secondary bg-opacity-25 text-slate-300" style={{ fontSize: '0.7rem' }}>
|
<span className="badge bg-secondary" style={{ fontSize: '0.7rem' }}>
|
||||||
<i className="bi bi-wallet2 me-1"></i>
|
<i className="bi bi-wallet2 me-1"></i>
|
||||||
{transaction.account?.name}
|
{transaction.account?.name}
|
||||||
</span>
|
</span>
|
||||||
@ -2674,7 +2674,7 @@ export default function Transactions() {
|
|||||||
<label className="form-check-label text-slate-300" htmlFor="addKeywordCheck">
|
<label className="form-check-label text-slate-300" htmlFor="addKeywordCheck">
|
||||||
<i className="bi bi-key me-1 text-warning"></i>
|
<i className="bi bi-key me-1 text-warning"></i>
|
||||||
{t('transactions.addAsKeyword')}
|
{t('transactions.addAsKeyword')}
|
||||||
<span className="badge bg-warning text-dark ms-2">
|
<span className="badge bg-warning ms-2">
|
||||||
"{filters.search}"
|
"{filters.search}"
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user