feat: opções de visualização de transações (semanal, mensal, todas)
- Adicionado seletor de viewMode: 'week', 'month', 'all' - Backend ajustado para trazer todos os dados quando viewMode é month/all - Agrupamento por mês no frontend - Lista flat para visualização 'todas' - Traduções i18n para pt-BR, en, es
This commit is contained in:
parent
44bc999840
commit
27d7e91896
@ -397,8 +397,13 @@ public function byWeek(Request $request): JsonResponse
|
||||
// Verificar se há filtros ativos (além de date_field e currency)
|
||||
$hasActiveFilters = $request->hasAny(['account_id', 'category_id', 'cost_center_id', 'type', 'status', 'search', 'start_date', 'end_date']);
|
||||
|
||||
// Se há filtros, trazer mais semanas para mostrar todos os resultados
|
||||
$perPage = $hasActiveFilters ? 100 : $request->get('per_page', 10); // Mais semanas quando filtrado
|
||||
// Verificar modo de visualização
|
||||
$viewMode = $request->get('view_mode', 'week'); // 'week', 'month', 'all'
|
||||
|
||||
// Determinar quantidade de semanas por página
|
||||
// Se há filtros OU viewMode é 'month' ou 'all', trazer todas as semanas
|
||||
$shouldFetchAll = $hasActiveFilters || in_array($viewMode, ['month', 'all']);
|
||||
$perPage = $shouldFetchAll ? 1000 : $request->get('per_page', 10);
|
||||
$page = $request->get('page', 1);
|
||||
$currency = $request->get('currency'); // Filtro de divisa opcional
|
||||
$dateField = $request->get('date_field', 'planned_date');
|
||||
|
||||
@ -569,6 +569,13 @@
|
||||
"amount": "Amount",
|
||||
"leaveEmptyForPlanned": "Leave empty to use planned amount",
|
||||
"week": "Week",
|
||||
"weekly": "Weekly",
|
||||
"monthly": "Monthly",
|
||||
"all": "All",
|
||||
"viewWeekly": "Weekly view",
|
||||
"viewMonthly": "Monthly view",
|
||||
"viewAll": "View all",
|
||||
"allTransactions": "All Transactions",
|
||||
"type": {
|
||||
"label": "Type",
|
||||
"credit": "Credit",
|
||||
|
||||
@ -577,6 +577,13 @@
|
||||
"amount": "Valor",
|
||||
"leaveEmptyForPlanned": "Dejar vacío para usar el valor previsto",
|
||||
"week": "Semana",
|
||||
"weekly": "Semanal",
|
||||
"monthly": "Mensual",
|
||||
"all": "Todas",
|
||||
"viewWeekly": "Vista semanal",
|
||||
"viewMonthly": "Vista mensual",
|
||||
"viewAll": "Ver todas",
|
||||
"allTransactions": "Todas las Transacciones",
|
||||
"type": {
|
||||
"label": "Tipo",
|
||||
"credit": "Crédito",
|
||||
|
||||
@ -579,6 +579,13 @@
|
||||
"amount": "Valor",
|
||||
"leaveEmptyForPlanned": "Deixe vazio para usar o valor previsto",
|
||||
"week": "Semana",
|
||||
"weekly": "Semanal",
|
||||
"monthly": "Mensal",
|
||||
"all": "Todas",
|
||||
"viewWeekly": "Visualização semanal",
|
||||
"viewMonthly": "Visualização mensal",
|
||||
"viewAll": "Ver todas",
|
||||
"allTransactions": "Todas as Transações",
|
||||
"type": {
|
||||
"label": "Tipo",
|
||||
"credit": "Crédito",
|
||||
|
||||
@ -40,6 +40,9 @@ export default function Transactions() {
|
||||
// Estados de paginação
|
||||
const [page, setPage] = useState(1);
|
||||
const [perPage] = useState(5); // Semanas por página
|
||||
|
||||
// Estado de visualização: 'week', 'month', 'all'
|
||||
const [viewMode, setViewMode] = useState('week');
|
||||
|
||||
// Estados de filtro
|
||||
const [filters, setFilters] = useState({
|
||||
@ -212,6 +215,7 @@ export default function Transactions() {
|
||||
page,
|
||||
per_page: perPage,
|
||||
currency: selectedCurrency,
|
||||
view_mode: viewMode, // Enviar modo de visualização para o backend
|
||||
};
|
||||
|
||||
// Remover params vazios
|
||||
@ -253,7 +257,7 @@ export default function Transactions() {
|
||||
|
||||
useEffect(() => {
|
||||
loadWeeklyData();
|
||||
}, [filters, page, selectedCurrency]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [filters, page, selectedCurrency, viewMode]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// Mobile resize detection
|
||||
useEffect(() => {
|
||||
@ -962,6 +966,12 @@ export default function Transactions() {
|
||||
}
|
||||
return `${startDay} ${startMonth} - ${endDay} ${endMonth}`;
|
||||
};
|
||||
|
||||
// Formatar nome do mês
|
||||
const formatMonthName = (month) => {
|
||||
const date = new Date(month.year, month.month - 1, 1);
|
||||
return date.toLocaleDateString(getLocale(), { month: 'long', year: 'numeric' });
|
||||
};
|
||||
|
||||
// Funções de seleção de transações
|
||||
const getAllVisibleTransactionIds = () => {
|
||||
@ -1060,8 +1070,56 @@ export default function Transactions() {
|
||||
const pagination = currentCurrencyData?.pagination;
|
||||
const weeks = currentCurrencyData?.weeks || [];
|
||||
|
||||
// Criar lista flat de todas as transações para modo filtrado
|
||||
// Criar lista flat de todas as transações para modo filtrado ou "todas"
|
||||
const allTransactions = weeks.flatMap(week => week.transactions || []);
|
||||
|
||||
// Agrupar transações por mês para viewMode 'month'
|
||||
const months = React.useMemo(() => {
|
||||
if (viewMode !== 'month' || hasActiveFilters) return [];
|
||||
|
||||
const monthMap = {};
|
||||
allTransactions.forEach(txn => {
|
||||
const date = txn.effective_date || txn.planned_date;
|
||||
const monthKey = date.substring(0, 7); // "YYYY-MM"
|
||||
if (!monthMap[monthKey]) {
|
||||
monthMap[monthKey] = {
|
||||
month_key: monthKey,
|
||||
year: parseInt(monthKey.split('-')[0]),
|
||||
month: parseInt(monthKey.split('-')[1]),
|
||||
transactions: [],
|
||||
summary: {
|
||||
credits: { total: 0, count: 0 },
|
||||
debits: { total: 0, count: 0 },
|
||||
pending: { total: 0, count: 0 },
|
||||
total_transactions: 0,
|
||||
balance: 0,
|
||||
}
|
||||
};
|
||||
}
|
||||
monthMap[monthKey].transactions.push(txn);
|
||||
monthMap[monthKey].summary.total_transactions++;
|
||||
|
||||
const amount = txn.amount || txn.planned_amount;
|
||||
if (txn.type === 'credit') {
|
||||
monthMap[monthKey].summary.credits.total += amount;
|
||||
monthMap[monthKey].summary.credits.count++;
|
||||
} else {
|
||||
monthMap[monthKey].summary.debits.total += amount;
|
||||
monthMap[monthKey].summary.debits.count++;
|
||||
}
|
||||
if (txn.status === 'pending') {
|
||||
monthMap[monthKey].summary.pending.total += amount;
|
||||
monthMap[monthKey].summary.pending.count++;
|
||||
}
|
||||
});
|
||||
|
||||
// Calcular balance e ordenar
|
||||
Object.values(monthMap).forEach(m => {
|
||||
m.summary.balance = m.summary.credits.total - m.summary.debits.total;
|
||||
});
|
||||
|
||||
return Object.values(monthMap).sort((a, b) => b.month_key.localeCompare(a.month_key));
|
||||
}, [allTransactions, viewMode, hasActiveFilters]);
|
||||
|
||||
// Calcular totais gerais
|
||||
const totalStats = weeks.reduce((acc, week) => ({
|
||||
@ -1119,6 +1177,43 @@ export default function Transactions() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* View Mode Selector + Stats */}
|
||||
<div className="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
|
||||
{/* View Mode Buttons */}
|
||||
{!hasActiveFilters && (
|
||||
<div className="btn-group" role="group">
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm ${viewMode === 'week' ? 'btn-primary' : 'btn-outline-secondary'}`}
|
||||
onClick={() => setViewMode('week')}
|
||||
title={t('transactions.viewWeekly')}
|
||||
>
|
||||
<i className="bi bi-calendar-week me-1"></i>
|
||||
<span className="d-none d-sm-inline">{t('transactions.weekly')}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm ${viewMode === 'month' ? 'btn-primary' : 'btn-outline-secondary'}`}
|
||||
onClick={() => setViewMode('month')}
|
||||
title={t('transactions.viewMonthly')}
|
||||
>
|
||||
<i className="bi bi-calendar-month me-1"></i>
|
||||
<span className="d-none d-sm-inline">{t('transactions.monthly')}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm ${viewMode === 'all' ? 'btn-primary' : 'btn-outline-secondary'}`}
|
||||
onClick={() => setViewMode('all')}
|
||||
title={t('transactions.viewAll')}
|
||||
>
|
||||
<i className="bi bi-list-ul me-1"></i>
|
||||
<span className="d-none d-sm-inline">{t('transactions.all')}</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{hasActiveFilters && <div></div>}
|
||||
</div>
|
||||
|
||||
{/* Stats Cards */}
|
||||
<div className="txn-stats">
|
||||
<div className="txn-stat-card">
|
||||
@ -1554,8 +1649,8 @@ export default function Transactions() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Weeks List (grouped view when NO filters are active) */}
|
||||
{!loading && !hasActiveFilters && weeks.length > 0 && (
|
||||
{/* Weeks List (grouped view when NO filters are active and viewMode is 'week') */}
|
||||
{!loading && !hasActiveFilters && viewMode === 'week' && weeks.length > 0 && (
|
||||
<div className="txn-weeks-container">
|
||||
{weeks.map((week) => (
|
||||
<div key={week.year_week} className={`txn-week ${expandedWeeks[week.year_week] ? 'expanded' : ''}`}>
|
||||
@ -2201,6 +2296,309 @@ export default function Transactions() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Monthly List (grouped view when NO filters and viewMode is 'month') */}
|
||||
{!loading && !hasActiveFilters && viewMode === 'month' && months.length > 0 && (
|
||||
<div className="txn-weeks-container">
|
||||
{months.map((month) => (
|
||||
<div key={month.month_key} className={`txn-week ${expandedWeeks[month.month_key] ? 'expanded' : ''}`}>
|
||||
{/* Month Header */}
|
||||
<div className="txn-week-header" onClick={() => toggleWeekExpansion(month.month_key)}>
|
||||
<div className="txn-week-left">
|
||||
<div className="txn-week-chevron">
|
||||
<i className="bi bi-chevron-right"></i>
|
||||
</div>
|
||||
<div className="txn-week-info">
|
||||
<h3>
|
||||
{formatMonthName(month)}
|
||||
<div className="txn-week-badges">
|
||||
<span className="txn-week-badge count">{month.summary.total_transactions}</span>
|
||||
</div>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Month Summary */}
|
||||
<div className="txn-week-summary">
|
||||
<div className="txn-week-stat">
|
||||
<div className="txn-week-stat-label">{t('transactions.credits')}</div>
|
||||
<div className="txn-week-stat-value credit">+{formatCurrency(month.summary.credits.total, selectedCurrency)}</div>
|
||||
</div>
|
||||
<div className="txn-week-stat">
|
||||
<div className="txn-week-stat-label">{t('transactions.debits')}</div>
|
||||
<div className="txn-week-stat-value debit">-{formatCurrency(month.summary.debits.total, selectedCurrency)}</div>
|
||||
</div>
|
||||
<div className="txn-week-stat">
|
||||
<div className="txn-week-stat-label">{t('transactions.balance')}</div>
|
||||
<div className={`txn-week-stat-value ${month.summary.balance >= 0 ? 'balance-pos' : 'balance-neg'}`}>
|
||||
{month.summary.balance >= 0 ? '+' : ''}{formatCurrency(month.summary.balance, selectedCurrency)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Month Transactions */}
|
||||
{expandedWeeks[month.month_key] && (
|
||||
<div className="txn-week-body">
|
||||
{isMobile ? (
|
||||
// Mobile: Cards Layout
|
||||
<div className="d-flex flex-column gap-2 p-2">
|
||||
{month.transactions.map(transaction => (
|
||||
<div
|
||||
key={transaction.id}
|
||||
ref={transaction.id === highlightedTransactionId ? highlightedRef : null}
|
||||
className={`card border-secondary ${transaction.is_overdue ? 'border-danger' : ''} ${transaction.id === highlightedTransactionId ? 'border-primary' : ''}`}
|
||||
style={{ background: '#0f172a', cursor: 'pointer' }}
|
||||
>
|
||||
<div className="card-body p-3">
|
||||
<div className="d-flex justify-content-between align-items-start mb-2">
|
||||
<span className="text-slate-400" style={{ fontSize: '0.75rem' }}>
|
||||
{formatDate(transaction.effective_date || transaction.planned_date)}
|
||||
</span>
|
||||
<div className="d-flex gap-1">
|
||||
<span className={`badge ${transaction.type === 'credit' ? 'bg-success' : 'bg-danger'} bg-opacity-25 ${transaction.type === 'credit' ? 'text-success' : 'text-danger'}`} style={{ fontSize: '0.65rem' }}>
|
||||
{transaction.type === 'credit' ? t('transactions.type.credit') : t('transactions.type.debit')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-2" onClick={() => openDetailModal(transaction)}>
|
||||
<span className="text-white fw-medium" style={{ fontSize: '0.85rem' }}>
|
||||
{transaction.description}
|
||||
</span>
|
||||
</div>
|
||||
<div className="d-flex flex-wrap gap-2 mb-2">
|
||||
<span className="badge bg-secondary" style={{ fontSize: '0.7rem' }}>
|
||||
<i className="bi bi-wallet2 me-1"></i>
|
||||
{transaction.account?.name}
|
||||
</span>
|
||||
{transaction.category && (
|
||||
<span
|
||||
className="badge"
|
||||
style={{
|
||||
backgroundColor: transaction.category.color + '20',
|
||||
color: transaction.category.color,
|
||||
fontSize: '0.7rem'
|
||||
}}
|
||||
>
|
||||
<i className={`bi ${transaction.category.icon} me-1`}></i>
|
||||
{transaction.category.name}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="d-flex justify-content-between align-items-center pt-2" style={{ borderTop: '1px solid #334155' }}>
|
||||
<div className={`fw-bold ${transaction.type === 'credit' ? 'text-success' : 'text-danger'}`} style={{ fontSize: '1rem' }}>
|
||||
{transaction.type === 'credit' ? '+' : '-'}
|
||||
{formatCurrency(transaction.amount || transaction.planned_amount, selectedCurrency)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
// Desktop: Table Layout
|
||||
<table className="txn-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width: '90px' }} className="col-date">{t('transactions.date')}</th>
|
||||
<th className="col-description">{t('transactions.description')}</th>
|
||||
<th style={{ width: '120px' }} className="col-account">{t('transactions.account')}</th>
|
||||
<th style={{ width: '140px' }} className="col-category">{t('transactions.category')}</th>
|
||||
<th style={{ width: '110px' }} className="text-end col-amount">{t('transactions.amount')}</th>
|
||||
<th style={{ width: '70px' }} className="text-center col-type">{t('transactions.type.label')}</th>
|
||||
<th style={{ width: '80px' }} className="text-center col-status">{t('transactions.status.label')}</th>
|
||||
<th style={{ width: '40px' }} className="text-center col-actions"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{month.transactions.map(transaction => (
|
||||
<tr
|
||||
key={transaction.id}
|
||||
ref={transaction.id === highlightedTransactionId ? highlightedRef : null}
|
||||
className={`${transaction.is_overdue ? 'overdue' : ''} ${transaction.id === highlightedTransactionId ? 'highlighted-transaction' : ''}`}
|
||||
>
|
||||
<td className="col-date">
|
||||
<span className="txn-date">
|
||||
{formatDate(transaction.effective_date || transaction.planned_date)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="col-description">
|
||||
<span className="txn-description" onClick={() => openDetailModal(transaction)}>
|
||||
{transaction.description}
|
||||
</span>
|
||||
</td>
|
||||
<td className="col-account"><span className="txn-account">{transaction.account?.name}</span></td>
|
||||
<td className="col-category">
|
||||
{transaction.category && (
|
||||
<span
|
||||
className="txn-category-badge"
|
||||
style={{ backgroundColor: transaction.category.color + '20', color: transaction.category.color }}
|
||||
>
|
||||
<i className={`bi ${transaction.category.icon}`}></i>
|
||||
{transaction.category.name}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="col-amount">
|
||||
<span className={`txn-amount ${transaction.type}`}>
|
||||
{transaction.type === 'credit' ? '+' : '-'}
|
||||
{formatCurrency(transaction.amount || transaction.planned_amount, selectedCurrency)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="text-center col-type">
|
||||
<span className={`txn-type-badge ${transaction.type}`}>
|
||||
{transaction.type === 'credit' ? t('transactions.type.credit') : t('transactions.type.debit')}
|
||||
</span>
|
||||
</td>
|
||||
<td className="text-center col-status">
|
||||
<span className={`txn-status-badge ${transaction.status}`}>
|
||||
{t(`transactions.status.${transaction.status}`)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="text-center col-actions">
|
||||
<div className="dropdown">
|
||||
<button className="txn-actions-btn" type="button" data-bs-toggle="dropdown">
|
||||
<i className="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul className="dropdown-menu dropdown-menu-end shadow-sm">
|
||||
<li>
|
||||
<button className="dropdown-item" onClick={() => openQuickCategorizeModal(transaction)}>
|
||||
<i className="bi bi-tags text-success me-2"></i>
|
||||
{t('transactions.quickCategorize')}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button className="dropdown-item" onClick={() => openEditModal(transaction)}>
|
||||
<i className="bi bi-pencil text-primary me-2"></i>
|
||||
{t('common.edit')}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* All Transactions List (flat view when NO filters and viewMode is 'all') */}
|
||||
{!loading && !hasActiveFilters && viewMode === 'all' && allTransactions.length > 0 && (
|
||||
<div className="txn-weeks-container">
|
||||
<div className="txn-week expanded">
|
||||
<div className="txn-week-header" style={{ cursor: 'default' }}>
|
||||
<div className="txn-week-left">
|
||||
<div className="txn-week-chevron">
|
||||
<i className="bi bi-list-ul text-info"></i>
|
||||
</div>
|
||||
<div className="txn-week-info">
|
||||
<h3>
|
||||
{t('transactions.allTransactions') || 'Todas as Transações'}
|
||||
<div className="txn-week-badges">
|
||||
<span className="txn-week-badge count">{allTransactions.length}</span>
|
||||
</div>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="txn-week-summary">
|
||||
<div className="txn-week-stat">
|
||||
<div className="txn-week-stat-label">{t('transactions.credits')}</div>
|
||||
<div className="txn-week-stat-value credit">+{formatCurrency(totalStats.credits, selectedCurrency)}</div>
|
||||
</div>
|
||||
<div className="txn-week-stat">
|
||||
<div className="txn-week-stat-label">{t('transactions.debits')}</div>
|
||||
<div className="txn-week-stat-value debit">-{formatCurrency(totalStats.debits, selectedCurrency)}</div>
|
||||
</div>
|
||||
<div className="txn-week-stat">
|
||||
<div className="txn-week-stat-label">{t('transactions.balance')}</div>
|
||||
<div className={`txn-week-stat-value ${(totalStats.credits - totalStats.debits) >= 0 ? 'balance-pos' : 'balance-neg'}`}>
|
||||
{(totalStats.credits - totalStats.debits) >= 0 ? '+' : ''}{formatCurrency(totalStats.credits - totalStats.debits, selectedCurrency)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="txn-week-body">
|
||||
{isMobile ? (
|
||||
<div className="d-flex flex-column gap-2 p-2">
|
||||
{allTransactions.map(transaction => (
|
||||
<div
|
||||
key={transaction.id}
|
||||
className={`card border-secondary ${transaction.is_overdue ? 'border-danger' : ''}`}
|
||||
style={{ background: '#0f172a', cursor: 'pointer' }}
|
||||
>
|
||||
<div className="card-body p-3">
|
||||
<div className="d-flex justify-content-between align-items-start mb-2">
|
||||
<span className="text-slate-400" style={{ fontSize: '0.75rem' }}>
|
||||
{formatDate(transaction.effective_date || transaction.planned_date)}
|
||||
</span>
|
||||
<span className={`badge ${transaction.type === 'credit' ? 'bg-success' : 'bg-danger'} bg-opacity-25`} style={{ fontSize: '0.65rem' }}>
|
||||
{transaction.type === 'credit' ? t('transactions.type.credit') : t('transactions.type.debit')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mb-2" onClick={() => openDetailModal(transaction)}>
|
||||
<span className="text-white fw-medium">{transaction.description}</span>
|
||||
</div>
|
||||
<div className="d-flex justify-content-between align-items-center pt-2" style={{ borderTop: '1px solid #334155' }}>
|
||||
<div className={`fw-bold ${transaction.type === 'credit' ? 'text-success' : 'text-danger'}`}>
|
||||
{transaction.type === 'credit' ? '+' : '-'}
|
||||
{formatCurrency(transaction.amount || transaction.planned_amount, selectedCurrency)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<table className="txn-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width: '90px' }}>{t('transactions.date')}</th>
|
||||
<th>{t('transactions.description')}</th>
|
||||
<th style={{ width: '120px' }}>{t('transactions.account')}</th>
|
||||
<th style={{ width: '140px' }}>{t('transactions.category')}</th>
|
||||
<th style={{ width: '110px' }} className="text-end">{t('transactions.amount')}</th>
|
||||
<th style={{ width: '70px' }} className="text-center">{t('transactions.type.label')}</th>
|
||||
<th style={{ width: '80px' }} className="text-center">{t('transactions.status.label')}</th>
|
||||
<th style={{ width: '40px' }} className="text-center"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{allTransactions.map(transaction => (
|
||||
<tr key={transaction.id} className={transaction.is_overdue ? 'overdue' : ''}>
|
||||
<td><span className="txn-date">{formatDate(transaction.effective_date || transaction.planned_date)}</span></td>
|
||||
<td><span className="txn-description" onClick={() => openDetailModal(transaction)}>{transaction.description}</span></td>
|
||||
<td><span className="txn-account">{transaction.account?.name}</span></td>
|
||||
<td>
|
||||
{transaction.category && (
|
||||
<span className="txn-category-badge" style={{ backgroundColor: transaction.category.color + '20', color: transaction.category.color }}>
|
||||
<i className={`bi ${transaction.category.icon}`}></i>
|
||||
{transaction.category.name}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td><span className={`txn-amount ${transaction.type}`}>{transaction.type === 'credit' ? '+' : '-'}{formatCurrency(transaction.amount || transaction.planned_amount, selectedCurrency)}</span></td>
|
||||
<td className="text-center"><span className={`txn-type-badge ${transaction.type}`}>{transaction.type === 'credit' ? t('transactions.type.credit') : t('transactions.type.debit')}</span></td>
|
||||
<td className="text-center"><span className={`txn-status-badge ${transaction.status}`}>{t(`transactions.status.${transaction.status}`)}</span></td>
|
||||
<td className="text-center">
|
||||
<button className="txn-actions-btn" onClick={() => openQuickCategorizeModal(transaction)}><i className="bi bi-tags"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Modal de Criar/Editar */}
|
||||
<Modal
|
||||
show={showModal}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user