webmoney/frontend/src/pages/Accounts.jsx
marcoitaloesp-ai 9c9d6443e7
v1.57.0: Redesign category modals + i18n updates + demo transactions fix
- Redesigned category create/edit modal with elegant wizard-style UI
- Redesigned batch categorization modal with visual cards and better preview
- Added missing i18n translations (common.continue, creating, remove)
- Added budgets.general and wizard translations for ES, PT-BR, EN
- Fixed 3 demo user transactions that were missing categories
2025-12-18 19:06:07 +00:00

1755 lines
82 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { accountService, liabilityAccountService, assetAccountService } from '../services/api';
import { useToast } from '../components/Toast';
import { ConfirmModal } from '../components/Modal';
import IconSelector from '../components/IconSelector';
import CurrencySelector from '../components/CurrencySelector';
import { useFormatters } from '../hooks';
import AssetWizard from '../components/AssetWizard';
import AccountWizard from '../components/AccountWizard';
const Accounts = () => {
const { t } = useTranslation();
const toast = useToast();
const navigate = useNavigate();
const { currency: formatCurrencyHook } = useFormatters();
const [accounts, setAccounts] = useState([]);
const [liabilityAccounts, setLiabilityAccounts] = useState([]);
const [assetAccounts, setAssetAccounts] = useState([]);
const [loading, setLoading] = useState(true);
const [showModal, setShowModal] = useState(false);
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [showAdjustModal, setShowAdjustModal] = useState(false);
const [showAssetWizard, setShowAssetWizard] = useState(false);
const [showAccountWizard, setShowAccountWizard] = useState(false);
const [editingAccount, setEditingAccount] = useState(null);
const [showAssetDetail, setShowAssetDetail] = useState(false);
const [selectedAsset, setSelectedAsset] = useState(null);
const [editingAsset, setEditingAsset] = useState(null);
const [adjustAccount, setAdjustAccount] = useState(null);
const [targetBalance, setTargetBalance] = useState('');
const [adjusting, setAdjusting] = useState(false);
const [selectedAccount, setSelectedAccount] = useState(null);
const [saving, setSaving] = useState(false);
const [recalculating, setRecalculating] = useState(false);
const [filter, setFilter] = useState({ type: '', is_active: '' });
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
const [activeTab, setActiveTab] = useState('accounts'); // 'accounts', 'liabilities' ou 'assets'
const [formData, setFormData] = useState({
name: '',
type: 'checking',
bank_name: '',
account_number: '',
initial_balance: '0',
credit_limit: '',
currency: 'BRL',
color: '#1E40AF',
icon: 'bi-bank',
description: '',
is_active: true,
include_in_total: true,
});
const accountTypes = accountService.types;
const accountIcons = accountService.icons;
useEffect(() => {
const handleResize = () => setIsMobile(window.innerWidth < 768);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
useEffect(() => {
loadAccounts();
}, [filter]);
const loadAccounts = async () => {
try {
setLoading(true);
// Recalcular saldos automaticamente antes de carregar
await accountService.recalculateAllBalances();
const params = {};
if (filter.type) params.type = filter.type;
if (filter.is_active !== '') params.is_active = filter.is_active;
// Carregar contas normais e passivas em paralelo
const [accountsResponse, liabilityResponse, assetResponse] = await Promise.all([
accountService.getAll(params),
liabilityAccountService.getAll({ is_active: filter.is_active || undefined }),
assetAccountService.getAll({ status: filter.is_active === '1' ? 'active' : undefined })
]);
if (accountsResponse.success) {
setAccounts(accountsResponse.data);
}
if (liabilityResponse.success) {
setLiabilityAccounts(liabilityResponse.data);
}
if (assetResponse.success) {
setAssetAccounts(assetResponse.data);
}
} catch (error) {
toast.error(t('accounts.loadError'));
} finally {
setLoading(false);
}
};
const handleOpenModal = (account = null) => {
if (account) {
setSelectedAccount(account);
setFormData({
name: account.name || '',
type: account.type || 'checking',
bank_name: account.bank_name || '',
account_number: account.account_number || '',
initial_balance: account.initial_balance?.toString() || '0',
credit_limit: account.credit_limit?.toString() || '',
currency: account.currency || 'BRL',
color: account.color || '#1E40AF',
icon: account.icon || 'bi-bank',
description: account.description || '',
is_active: account.is_active ?? true,
include_in_total: account.include_in_total ?? true,
});
} else {
setSelectedAccount(null);
setFormData({
name: '',
type: 'checking',
bank_name: '',
account_number: '',
initial_balance: '0',
credit_limit: '',
currency: 'BRL',
color: '#1E40AF',
icon: 'bi-bank',
description: '',
is_active: true,
include_in_total: true,
});
}
setShowModal(true);
};
const handleCloseModal = () => {
setShowModal(false);
setSelectedAccount(null);
};
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value,
}));
// Auto-selecionar ícone baseado no tipo
if (name === 'type' && accountIcons[value]) {
setFormData(prev => ({
...prev,
icon: accountIcons[value],
}));
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!formData.name.trim()) {
toast.error(t('validation.required'));
return;
}
setSaving(true);
try {
const data = {
...formData,
initial_balance: parseFloat(formData.initial_balance) || 0,
credit_limit: formData.credit_limit ? parseFloat(formData.credit_limit) : null,
};
let response;
if (selectedAccount) {
response = await accountService.update(selectedAccount.id, data);
} else {
response = await accountService.create(data);
}
if (response.success) {
toast.success(selectedAccount ? t('accounts.updateSuccess') : t('accounts.createSuccess'));
handleCloseModal();
loadAccounts();
}
} catch (error) {
toast.error(error.response?.data?.message || t('accounts.createError'));
} finally {
setSaving(false);
}
};
const handleDeleteClick = (account) => {
setSelectedAccount(account);
setShowDeleteModal(true);
};
const handleDeleteConfirm = async () => {
if (!selectedAccount) return;
setSaving(true);
try {
const response = await accountService.delete(selectedAccount.id);
if (response.success) {
toast.success(t('accounts.deleteSuccess'));
setShowDeleteModal(false);
setSelectedAccount(null);
loadAccounts();
}
} catch (error) {
toast.error(error.response?.data?.message || t('accounts.deleteError'));
} finally {
setSaving(false);
}
};
const formatCurrency = (value, currency = 'BRL') => {
return formatCurrencyHook(value, currency);
};
const handleRecalculateBalances = async () => {
setRecalculating(true);
try {
const response = await accountService.recalculateAllBalances();
if (response.success) {
const updated = response.data.filter(acc => acc.difference !== 0);
if (updated.length > 0) {
toast.success(t('accounts.recalculateSuccess', { count: updated.length }));
} else {
toast.info(t('accounts.balancesUpToDate'));
}
loadAccounts();
}
} catch (error) {
toast.error(error.response?.data?.message || t('accounts.recalculateError'));
} finally {
setRecalculating(false);
}
};
// Abrir modal de ajuste de saldo
const openAdjustModal = (account) => {
setAdjustAccount(account);
setTargetBalance(account.current_balance?.toString() || '0');
setShowAdjustModal(true);
};
// Ajustar saldo da conta
const handleAdjustBalance = async () => {
if (!adjustAccount || targetBalance === '') return;
setAdjusting(true);
try {
const response = await accountService.adjustBalance(adjustAccount.id, parseFloat(targetBalance));
if (response.success) {
toast.success(t('accounts.adjustSuccess'));
setShowAdjustModal(false);
loadAccounts();
}
} catch (error) {
toast.error(error.response?.data?.message || t('accounts.adjustError'));
} finally {
setAdjusting(false);
}
};
// Abrir modal de detalhes do ativo
const handleOpenAssetDetail = async (asset) => {
try {
const response = await assetAccountService.getById(asset.id);
if (response.success) {
setSelectedAsset(response.data);
setShowAssetDetail(true);
}
} catch (error) {
toast.error('Erro ao carregar detalhes do ativo');
}
};
// Fechar modal de detalhes do ativo
const handleCloseAssetDetail = () => {
setShowAssetDetail(false);
setSelectedAsset(null);
};
// Editar ativo
const handleEditAsset = () => {
setEditingAsset(selectedAsset);
setShowAssetDetail(false);
setShowAssetWizard(true);
};
// Callback após salvar/criar ativo
const handleAssetSuccess = (assetData) => {
loadAccounts();
setEditingAsset(null);
toast.success(editingAsset ? 'Activo actualizado con éxito' : 'Activo creado con éxito');
};
// Callback após salvar/criar conta via wizard
const handleAccountWizardSuccess = (data, destinationType) => {
loadAccounts();
setEditingAccount(null);
const isEditing = !!editingAccount;
if (destinationType === 'asset') {
toast.success(isEditing ? 'Cuenta de ahorro actualizada' : 'Cuenta de ahorro creada como activo');
} else if (destinationType === 'liability') {
toast.success(isEditing ? 'Tarjeta de crédito actualizada' : 'Tarjeta de crédito creada como pasivo');
} else {
toast.success(isEditing ? t('accounts.updateSuccess') : t('accounts.createSuccess'));
}
};
// Calcula totais agrupados por moeda (incluindo passivos como valor negativo e ativos como positivo)
const getTotalsByCurrency = () => {
const totals = {};
// Contas normais
accounts
.filter(acc => acc.is_active && acc.include_in_total)
.forEach(acc => {
const currency = acc.currency || 'BRL';
if (!totals[currency]) {
totals[currency] = 0;
}
totals[currency] += parseFloat(acc.current_balance || 0);
});
// Passivos (como valor negativo - saldo devedor)
liabilityAccounts
.filter(acc => acc.is_active && acc.status === 'active')
.forEach(acc => {
const currency = acc.currency || 'EUR';
if (!totals[currency]) {
totals[currency] = 0;
}
// Subtrair o saldo devedor (remaining_balance)
totals[currency] -= parseFloat(acc.remaining_balance || 0);
});
// Ativos (como valor positivo - current_value)
assetAccounts
.filter(acc => acc.status === 'active')
.forEach(acc => {
const currency = acc.currency || 'EUR';
if (!totals[currency]) {
totals[currency] = 0;
}
// Somar o valor atual do ativo
totals[currency] += parseFloat(acc.current_value || 0);
});
return totals;
};
// Total de contas ativas (normais + passivas + ativos)
const getTotalActiveAccounts = () => {
const normalActive = accounts.filter(a => a.is_active).length;
const liabilityActive = liabilityAccounts.filter(a => a.is_active).length;
const assetActive = assetAccounts.filter(a => a.status === 'active').length;
return normalActive + liabilityActive + assetActive;
};
// Total de todas as contas
const getTotalAccounts = () => {
return accounts.length + liabilityAccounts.length + assetAccounts.length;
};
// Total de ativos por moeda
const getAssetTotalsByCurrency = () => {
const totals = {};
assetAccounts
.filter(acc => acc.status === 'active')
.forEach(acc => {
const currency = acc.currency || 'EUR';
if (!totals[currency]) {
totals[currency] = { current: 0, acquisition: 0, count: 0 };
}
totals[currency].current += parseFloat(acc.current_value || 0);
totals[currency].acquisition += parseFloat(acc.acquisition_value || 0);
totals[currency].count++;
});
return totals;
};
return (
<div>
{/* Header */}
<div className="d-flex justify-content-between align-items-center mb-4">
<div>
<h2 className={`text-white ${isMobile ? 'mb-0 fs-4' : 'mb-1'}`}>
<i className="bi bi-wallet2 me-2 text-primary"></i>
{t('nav.accounts')}
</h2>
{!isMobile && <p className="text-slate-400 mb-0">{t('accounts.title')}</p>}
</div>
<div className="d-flex gap-2">
{!isMobile && activeTab === 'accounts' && (
<button
className="btn btn-outline-secondary"
onClick={handleRecalculateBalances}
disabled={recalculating}
title={t('accounts.recalculateBalances')}
>
{recalculating ? (
<span className="spinner-border spinner-border-sm me-2" role="status"></span>
) : (
<i className="bi bi-arrow-repeat me-2"></i>
)}
{t('accounts.recalculate')}
</button>
)}
{activeTab === 'accounts' && (
<button className={`btn btn-primary ${isMobile ? 'btn-sm' : ''}`} onClick={() => setShowAccountWizard(true)}>
<i className="bi bi-plus-lg me-2"></i>
{isMobile ? t('common.add') : t('accounts.newAccount')}
</button>
)}
{activeTab === 'liabilities' && (
<button className={`btn btn-warning ${isMobile ? 'btn-sm' : ''}`} onClick={() => navigate('/liabilities')}>
<i className="bi bi-gear me-2"></i>
{isMobile ? t('common.manage') : t('liabilities.manage')}
</button>
)}
{activeTab === 'assets' && (
<button className={`btn btn-success ${isMobile ? 'btn-sm' : ''}`} onClick={() => setShowAssetWizard(true)}>
<i className="bi bi-plus-lg me-2"></i>
{isMobile ? t('common.add') : 'Nuevo Activo'}
</button>
)}
</div>
</div>
{/* Tabs - Mobile Optimized */}
<ul className={`nav nav-tabs mb-4 ${isMobile ? 'nav-fill flex-nowrap overflow-auto' : ''}`} style={{ borderBottom: '1px solid #334155' }}>
<li className="nav-item">
<button
className={`nav-link ${activeTab === 'accounts' ? 'active bg-primary text-white' : 'text-slate-400'} ${isMobile ? 'px-2 py-2' : ''}`}
onClick={() => setActiveTab('accounts')}
style={{
border: 'none',
borderRadius: '0.5rem 0.5rem 0 0',
backgroundColor: activeTab === 'accounts' ? undefined : 'transparent',
fontSize: isMobile ? '0.75rem' : undefined,
whiteSpace: 'nowrap'
}}
>
<i className={`bi bi-wallet2 ${isMobile ? '' : 'me-2'}`}></i>
{isMobile ? '' : t('nav.accounts')} ({accounts.length})
</button>
</li>
<li className="nav-item">
<button
className={`nav-link ${activeTab === 'liabilities' ? 'active bg-warning text-dark' : 'text-slate-400'} ${isMobile ? 'px-2 py-2' : ''}`}
onClick={() => setActiveTab('liabilities')}
style={{
border: 'none',
borderRadius: '0.5rem 0.5rem 0 0',
backgroundColor: activeTab === 'liabilities' ? undefined : 'transparent',
fontSize: isMobile ? '0.75rem' : undefined,
whiteSpace: 'nowrap'
}}
>
<i className={`bi bi-bank ${isMobile ? '' : 'me-2'}`}></i>
{isMobile ? '' : t('nav.liabilities')} ({liabilityAccounts.length})
</button>
</li>
<li className="nav-item">
<button
className={`nav-link ${activeTab === 'assets' ? 'active bg-success text-white' : 'text-slate-400'} ${isMobile ? 'px-2 py-2' : ''}`}
onClick={() => setActiveTab('assets')}
style={{
border: 'none',
borderRadius: '0.5rem 0.5rem 0 0',
backgroundColor: activeTab === 'assets' ? undefined : 'transparent',
fontSize: isMobile ? '0.75rem' : undefined,
whiteSpace: 'nowrap'
}}
>
<i className={`bi bi-graph-up-arrow ${isMobile ? '' : 'me-2'}`}></i>
{isMobile ? '' : 'Activos'} ({assetAccounts.length})
</button>
</li>
</ul>
{activeTab === 'accounts' && (
<>
{/* Summary Cards */}
<div className={`row ${isMobile ? 'g-2 mb-3' : 'mb-4'}`}>
{/* Total por Moeda */}
<div className="col-md-6">
<div className={`card border-0 ${!isMobile ? 'h-100' : ''}`} style={{ background: '#1e293b' }}>
<div className={`card-body ${isMobile ? 'p-3' : ''}`}>
<div className="d-flex align-items-start">
<div className={`rounded-circle bg-primary bg-opacity-25 ${isMobile ? 'p-2 me-2' : 'p-3 me-3'}`}>
<i className={`bi bi-wallet2 text-primary ${isMobile ? 'fs-5' : 'fs-4'}`}></i>
</div>
<div className="flex-grow-1">
<p className={`text-slate-400 mb-2 ${isMobile ? '' : 'small'}`} style={isMobile ? { fontSize: '0.75rem' } : undefined}>{t('dashboard.totalBalance')}</p>
{Object.keys(getTotalsByCurrency()).length > 0 ? (
<div className={`d-flex flex-wrap ${isMobile ? 'gap-2' : 'gap-3'}`}>
{Object.entries(getTotalsByCurrency()).map(([currency, total]) => (
<div key={currency} className="text-center">
<h4 className={`mb-0 ${total >= 0 ? 'text-success' : 'text-danger'} ${isMobile ? 'fs-6' : ''}`} style={isMobile ? { fontSize: '1rem' } : undefined}>
{formatCurrency(total, currency)}
</h4>
<small className="text-slate-500" style={isMobile ? { fontSize: '0.65rem' } : undefined}>{currency}</small>
</div>
))}
</div>
) : (
<h4 className="mb-0 text-slate-500">-</h4>
)}
</div>
</div>
</div>
</div>
</div>
{/* Contas Ativas e Total */}
<div className="col-md-3 col-6">
<div className={`card border-0 ${!isMobile ? 'h-100' : ''}`} style={{ background: '#1e293b' }}>
<div className={`card-body ${isMobile ? 'p-3' : ''}`}>
<div className={`d-flex ${isMobile ? 'flex-column align-items-start' : 'align-items-center'}`}>
<div className={`rounded-circle bg-success bg-opacity-25 ${isMobile ? 'p-2 mb-2' : 'p-3 me-3'}`}>
<i className={`bi bi-check-circle text-success ${isMobile ? 'fs-6' : 'fs-4'}`}></i>
</div>
<div>
<p className={`text-slate-400 mb-0 ${isMobile ? '' : 'small'}`} style={isMobile ? { fontSize: '0.7rem' } : undefined}>{t('common.active')}</p>
<h3 className={`mb-0 text-white ${isMobile ? 'fs-5' : ''}`}>
{getTotalActiveAccounts()}
</h3>
</div>
</div>
</div>
</div>
</div>
<div className="col-md-3 col-6">
<div className={`card border-0 ${!isMobile ? 'h-100' : ''}`} style={{ background: '#1e293b' }}>
<div className={`card-body ${isMobile ? 'p-3' : ''}`}>
<div className={`d-flex ${isMobile ? 'flex-column align-items-start' : 'align-items-center'}`}>
<div className={`rounded-circle bg-info bg-opacity-25 ${isMobile ? 'p-2 mb-2' : 'p-3 me-3'}`}>
<i className={`bi bi-credit-card text-info ${isMobile ? 'fs-6' : 'fs-4'}`}></i>
</div>
<div>
<p className={`text-slate-400 mb-0 ${isMobile ? '' : 'small'}`} style={isMobile ? { fontSize: '0.7rem' } : undefined}>{t('common.total')}</p>
<h3 className={`mb-0 text-white ${isMobile ? 'fs-5' : ''}`}>{getTotalAccounts()}</h3>
</div>
</div>
</div>
</div>
</div>
</div>
{/* Filters */}
<div className="card border-0 mb-4" style={{ background: '#1e293b' }}>
<div className="card-body">
<div className="row g-3">
<div className="col-md-4">
<label className="form-label text-slate-400 small">{t('accounts.filterByType')}</label>
<select
className="form-select bg-dark text-white border-secondary"
value={filter.type}
onChange={(e) => setFilter(prev => ({ ...prev, type: e.target.value }))}
>
<option value="">{t('common.all')}</option>
{Object.entries(accountTypes).map(([key, label]) => (
<option key={key} value={key}>{label}</option>
))}
</select>
</div>
<div className="col-md-4">
<label className="form-label text-slate-400 small">{t('common.status')}</label>
<select
className="form-select bg-dark text-white border-secondary"
value={filter.is_active}
onChange={(e) => setFilter(prev => ({ ...prev, is_active: e.target.value }))}
>
<option value="">{t('common.all')}</option>
<option value="1">{t('common.active')}</option>
<option value="0">{t('common.inactive')}</option>
</select>
</div>
</div>
</div>
</div>
{/* Accounts List */}
<div className="card border-0" style={{ background: '#1e293b' }}>
<div className={`card-body ${isMobile ? 'p-2' : 'p-0'}`}>
{loading ? (
<div className="text-center py-5">
<div className="spinner-border text-primary" role="status">
<span className="visually-hidden">{t('common.loading')}</span>
</div>
</div>
) : accounts.length === 0 ? (
<div className="text-center py-5">
<i className="bi bi-wallet2 display-1 text-slate-600"></i>
<p className="text-slate-400 mt-3">{t('accounts.noAccounts')}</p>
<button className="btn btn-primary" onClick={() => setShowAccountWizard(true)}>
<i className="bi bi-plus-lg me-2"></i>
{t('accounts.newAccount')}
</button>
</div>
) : isMobile ? (
/* Mobile: Cards Layout */
<div className="d-flex flex-column gap-2">
{accounts.map((account) => (
<div
key={account.id}
className="p-3 rounded"
style={{
background: '#0f172a',
border: '1px solid #334155'
}}
>
<div className="d-flex justify-content-between align-items-start mb-2">
<div className="d-flex align-items-center flex-grow-1">
<div
className="rounded-circle d-flex align-items-center justify-content-center me-2"
style={{
width: '36px',
height: '36px',
backgroundColor: account.color + '25',
}}
>
<i className={`bi ${account.icon}`} style={{ color: account.color, fontSize: '1rem' }}></i>
</div>
<div className="flex-grow-1">
<div className="text-white fw-medium" style={{ fontSize: '0.9rem' }}>{account.name}</div>
{account.account_number && (
<small className="text-slate-400" style={{ fontSize: '0.7rem' }}> {account.account_number}</small>
)}
</div>
</div>
<div className="d-flex gap-1">
<button
className="btn btn-link text-success p-1"
onClick={() => openAdjustModal(account)}
style={{ fontSize: '1rem' }}
>
<i className="bi bi-sliders"></i>
</button>
<button
className="btn btn-link text-info p-1"
onClick={() => {
setEditingAccount(account);
setShowAccountWizard(true);
}}
style={{ fontSize: '1rem' }}
>
<i className="bi bi-pencil"></i>
</button>
<button
className="btn btn-link text-danger p-1"
onClick={() => handleDeleteClick(account)}
style={{ fontSize: '1rem' }}
>
<i className="bi bi-trash"></i>
</button>
</div>
</div>
<div className="d-flex justify-content-between align-items-center">
<div className="d-flex gap-2 align-items-center">
<span className="badge" style={{ backgroundColor: '#334155', fontSize: '0.65rem' }}>
{accountTypes[account.type] || account.type}
</span>
{account.is_active ? (
<span className="badge bg-success" style={{ fontSize: '0.65rem' }}>{t('common.active')}</span>
) : (
<span className="badge bg-secondary" style={{ fontSize: '0.65rem' }}>{t('common.inactive')}</span>
)}
</div>
<div className={`fw-bold ${parseFloat(account.current_balance) >= 0 ? 'text-success' : 'text-danger'}`} style={{ fontSize: '0.95rem' }}>
{formatCurrency(account.current_balance, account.currency)}
</div>
</div>
{account.bank_name && (
<div className="mt-2 pt-2" style={{ borderTop: '1px solid #334155' }}>
<small className="text-slate-400" style={{ fontSize: '0.7rem' }}>
<i className="bi bi-bank me-1"></i>{account.bank_name}
</small>
</div>
)}
</div>
))}
</div>
) : (
/* Desktop: Table Layout */
<div className="table-responsive">
<table className="table table-hover mb-0" style={{ '--bs-table-bg': 'transparent', backgroundColor: 'transparent' }}>
<thead style={{ backgroundColor: 'transparent' }}>
<tr style={{ borderBottom: '1px solid #334155', backgroundColor: 'transparent' }}>
<th className="text-slate-400 fw-normal py-3 ps-4" style={{ backgroundColor: 'transparent' }}>{t('accounts.accountName')}</th>
<th className="text-slate-400 fw-normal py-3" style={{ backgroundColor: 'transparent' }}>{t('common.type')}</th>
<th className="text-slate-400 fw-normal py-3" style={{ backgroundColor: 'transparent' }}>{t('accounts.bankName')}</th>
<th className="text-slate-400 fw-normal py-3 text-end" style={{ backgroundColor: 'transparent' }}>{t('accounts.currentBalance')}</th>
<th className="text-slate-400 fw-normal py-3 text-center" style={{ backgroundColor: 'transparent' }}>{t('common.status')}</th>
<th className="text-slate-400 fw-normal py-3 text-end pe-4" style={{ backgroundColor: 'transparent' }}>{t('common.actions')}</th>
</tr>
</thead>
<tbody style={{ backgroundColor: 'transparent' }}>
{accounts.map((account) => (
<tr key={account.id} style={{ borderBottom: '1px solid #334155', backgroundColor: 'transparent' }}>
<td className="py-3 ps-4" style={{ backgroundColor: 'transparent' }}>
<div className="d-flex align-items-center">
<div
className="rounded-circle d-flex align-items-center justify-content-center me-3"
style={{
width: '40px',
height: '40px',
backgroundColor: account.color + '25',
}}
>
<i className={`bi ${account.icon}`} style={{ color: account.color }}></i>
</div>
<div>
<div className="text-white fw-medium">{account.name}</div>
{account.account_number && (
<small className="text-slate-400"> {account.account_number}</small>
)}
</div>
</div>
</td>
<td className="py-3" style={{ backgroundColor: 'transparent' }}>
<span className="badge" style={{ backgroundColor: '#334155' }}>
{accountTypes[account.type] || account.type}
</span>
</td>
<td className="py-3 text-slate-300" style={{ backgroundColor: 'transparent' }}>{account.bank_name || '-'}</td>
<td className={`py-3 text-end fw-medium ${parseFloat(account.current_balance) >= 0 ? 'text-success' : 'text-danger'}`} style={{ backgroundColor: 'transparent' }}>
{formatCurrency(account.current_balance, account.currency)}
</td>
<td className="py-3 text-center" style={{ backgroundColor: 'transparent' }}>
{account.is_active ? (
<span className="badge bg-success">{t('common.active')}</span>
) : (
<span className="badge bg-secondary">{t('common.inactive')}</span>
)}
</td>
<td className="py-3 text-end pe-4" style={{ backgroundColor: 'transparent' }}>
<button
className="btn btn-link text-success p-1 me-1"
onClick={() => openAdjustModal(account)}
title={t('accounts.adjustBalance')}
>
<i className="bi bi-sliders"></i>
</button>
<button
className="btn btn-link text-info p-1 me-1"
onClick={() => {
setEditingAccount(account);
setShowAccountWizard(true);
}}
title={t('common.edit')}
>
<i className="bi bi-pencil"></i>
</button>
<button
className="btn btn-link text-danger p-1"
onClick={() => handleDeleteClick(account)}
title={t('common.delete')}
>
<i className="bi bi-trash"></i>
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
</>
)}
{/* Tab de Passivos */}
{activeTab === 'liabilities' && (
<>
{/* Liability Accounts Section */}
{liabilityAccounts.length > 0 ? (
<div className="card border-0" style={{ background: '#1e293b' }}>
<div className={`card-body ${isMobile ? 'p-2' : 'p-0'}`}>
{isMobile ? (
// Mobile: Cards Layout
<div className="d-flex flex-column gap-2">
{liabilityAccounts.map((liability) => (
<div
key={`liability-${liability.id}`}
className="card border-secondary"
style={{ cursor: 'pointer', background: '#0f172a' }}
onClick={() => navigate('/liabilities')}
>
<div className="card-body p-3">
{/* Header with Icon and Name */}
<div className="d-flex align-items-start gap-2 mb-2">
<div
className="rounded-circle d-flex align-items-center justify-content-center flex-shrink-0"
style={{
width: '35px',
height: '35px',
backgroundColor: (liability.color || '#DC2626') + '25',
}}
>
<i className={`bi ${liability.icon || 'bi-file-earmark-text'} fs-6`} style={{ color: liability.color || '#DC2626' }}></i>
</div>
<div className="flex-grow-1" style={{ minWidth: 0 }}>
<div className="text-white fw-medium mb-1" style={{ fontSize: '0.85rem' }}>
{liability.name}
</div>
{liability.contract_number && (
<div className="text-slate-400 mb-1" style={{ fontSize: '0.65rem' }}>
{liability.contract_number}
</div>
)}
</div>
<div>
{liability.status === 'active' ? (
<span className="badge bg-primary" style={{ fontSize: '0.65rem' }}>
{t('common.active')}
</span>
) : liability.status === 'paid_off' ? (
<span className="badge bg-success" style={{ fontSize: '0.65rem' }}>
{t('liabilities.paid')}
</span>
) : (
<span className="badge bg-secondary" style={{ fontSize: '0.65rem' }}>
{liability.status}
</span>
)}
</div>
</div>
{/* Creditor */}
{liability.creditor && (
<div className="text-slate-400 mb-2" style={{ fontSize: '0.7rem' }}>
<i className="bi bi-building me-1"></i>
{liability.creditor}
</div>
)}
{/* Amounts */}
<div className="d-flex justify-content-between align-items-center mb-2 pt-2" style={{ borderTop: '1px solid #334155' }}>
<div>
<div className="text-slate-400" style={{ fontSize: '0.65rem' }}>Principal</div>
<div className="text-slate-300 fw-medium" style={{ fontSize: '0.75rem' }}>
{formatCurrency(liability.principal_amount, liability.currency)}
</div>
</div>
<div className="text-end">
<div className="text-slate-400" style={{ fontSize: '0.65rem' }}>Saldo Devedor</div>
<div className="text-danger fw-medium" style={{ fontSize: '0.75rem' }}>
-{formatCurrency(liability.remaining_balance, liability.currency)}
</div>
</div>
</div>
{/* Progress Bar */}
<div>
<div className="d-flex justify-content-between align-items-center mb-1">
<span className="text-slate-400" style={{ fontSize: '0.65rem' }}>Progresso</span>
<span className="text-slate-400 fw-medium" style={{ fontSize: '0.7rem' }}>
{liability.progress_percentage || 0}%
</span>
</div>
<div className="progress" style={{ height: '6px', backgroundColor: '#334155' }}>
<div
className="progress-bar bg-success"
style={{ width: `${liability.progress_percentage || 0}%` }}
></div>
</div>
</div>
</div>
</div>
))}
</div>
) : (
// Desktop: Table Layout
<div className="table-responsive">
<table className="table table-hover mb-0" style={{ '--bs-table-bg': 'transparent', backgroundColor: 'transparent' }}>
<thead style={{ backgroundColor: 'transparent' }}>
<tr style={{ borderBottom: '1px solid #334155', backgroundColor: 'transparent' }}>
<th className="text-slate-400 fw-normal py-3 ps-4" style={{ backgroundColor: 'transparent' }}>{t('liabilities.contractName')}</th>
<th className="text-slate-400 fw-normal py-3" style={{ backgroundColor: 'transparent' }}>{t('liabilities.creditor')}</th>
<th className="text-slate-400 fw-normal py-3 text-end" style={{ backgroundColor: 'transparent' }}>{t('liabilities.principal')}</th>
<th className="text-slate-400 fw-normal py-3 text-end" style={{ backgroundColor: 'transparent' }}>{t('liabilities.remaining')}</th>
<th className="text-slate-400 fw-normal py-3 text-center" style={{ backgroundColor: 'transparent' }}>{t('liabilities.progress')}</th>
<th className="text-slate-400 fw-normal py-3 text-center" style={{ backgroundColor: 'transparent' }}>{t('common.status')}</th>
</tr>
</thead>
<tbody style={{ backgroundColor: 'transparent' }}>
{liabilityAccounts.map((liability) => (
<tr
key={`liability-${liability.id}`}
style={{ borderBottom: '1px solid #334155', backgroundColor: 'transparent', cursor: 'pointer' }}
onClick={() => navigate('/liabilities')}
>
<td className="py-3 ps-4" style={{ backgroundColor: 'transparent' }}>
<div className="d-flex align-items-center">
<div
className="rounded-circle d-flex align-items-center justify-content-center me-3"
style={{
width: '40px',
height: '40px',
backgroundColor: (liability.color || '#DC2626') + '25',
}}
>
<i className={`bi ${liability.icon || 'bi-file-earmark-text'}`} style={{ color: liability.color || '#DC2626' }}></i>
</div>
<div>
<div className="text-white fw-medium">{liability.name}</div>
{liability.contract_number && (
<small className="text-slate-400"> {liability.contract_number}</small>
)}
</div>
</div>
</td>
<td className="py-3 text-slate-300" style={{ backgroundColor: 'transparent' }}>{liability.creditor || '-'}</td>
<td className="py-3 text-end text-slate-300" style={{ backgroundColor: 'transparent' }}>
{formatCurrency(liability.principal_amount, liability.currency)}
</td>
<td className="py-3 text-end fw-medium text-danger" style={{ backgroundColor: 'transparent' }}>
-{formatCurrency(liability.remaining_balance, liability.currency)}
</td>
<td className="py-3 text-center" style={{ backgroundColor: 'transparent' }}>
<div className="d-flex align-items-center justify-content-center gap-2">
<div className="progress" style={{ width: '80px', height: '6px', backgroundColor: '#334155' }}>
<div
className="progress-bar bg-success"
style={{ width: `${liability.progress_percentage || 0}%` }}
></div>
</div>
<small className="text-slate-400">{liability.progress_percentage || 0}%</small>
</div>
</td>
<td className="py-3 text-center" style={{ backgroundColor: 'transparent' }}>
{liability.status === 'active' ? (
<span className="badge bg-primary">{t('common.active')}</span>
) : liability.status === 'paid_off' ? (
<span className="badge bg-success">{t('liabilities.paid')}</span>
) : (
<span className="badge bg-secondary">{liability.status}</span>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
) : (
<div className="card border-0" style={{ background: '#1e293b' }}>
<div className="card-body text-center py-5">
<i className="bi bi-bank display-1 text-slate-600"></i>
<p className="text-slate-400 mt-3">{t('liabilities.noLiabilities')}</p>
<button className="btn btn-primary" onClick={() => navigate('/liabilities')}>
<i className="bi bi-plus-lg me-2"></i>
{t('liabilities.import')}
</button>
</div>
</div>
)}
</>
)}
{/* Tab de Ativos */}
{activeTab === 'assets' && (
<>
{/* Summary Cards de Ativos */}
<div className={`row ${isMobile ? 'g-2 mb-3' : 'mb-4'}`}>
{Object.entries(getAssetTotalsByCurrency()).map(([currency, data]) => (
<div className="col-md-4" key={currency}>
<div className={`card border-0 ${!isMobile ? 'h-100' : ''}`} style={{ background: '#1e293b' }}>
<div className={`card-body ${isMobile ? 'p-3' : ''}`}>
<div className="d-flex justify-content-between align-items-start">
<div>
<p className={`text-slate-400 mb-1 ${isMobile ? '' : 'small'}`} style={isMobile ? { fontSize: '0.7rem' } : undefined}>
Activos ({currency}) - {data.count} items
</p>
<h4 className={`mb-0 text-success ${isMobile ? 'fs-5' : ''}`}>
{formatCurrency(data.current, currency)}
</h4>
{data.current !== data.acquisition && (
<small className={`${data.current > data.acquisition ? 'text-success' : 'text-danger'}`}>
<i className={`bi ${data.current > data.acquisition ? 'bi-arrow-up' : 'bi-arrow-down'} me-1`}></i>
{((data.current - data.acquisition) / data.acquisition * 100).toFixed(1)}% desde compra
</small>
)}
</div>
<div className={`rounded-circle bg-success bg-opacity-25 ${isMobile ? 'p-2' : 'p-3'}`}>
<i className={`bi bi-graph-up-arrow text-success ${isMobile ? 'fs-5' : 'fs-4'}`}></i>
</div>
</div>
</div>
</div>
</div>
))}
{Object.keys(getAssetTotalsByCurrency()).length === 0 && (
<div className="col-12">
<div className="card border-0" style={{ background: '#1e293b' }}>
<div className={`card-body ${isMobile ? 'p-3' : ''} text-center`}>
<i className="bi bi-graph-up-arrow text-slate-600 fs-1 mb-2"></i>
<p className="text-slate-400 mb-0">No hay activos registrados</p>
</div>
</div>
</div>
)}
</div>
{/* Lista de Ativos */}
<div className="card border-0" style={{ background: '#1e293b' }}>
<div className={`card-body ${isMobile ? 'p-2' : 'p-0'}`}>
{assetAccounts.length === 0 ? (
<div className="text-center py-5">
<i className="bi bi-graph-up-arrow display-1 text-slate-600"></i>
<p className="text-slate-400 mt-3">No hay activos registrados</p>
<button className="btn btn-success" onClick={() => setShowAssetWizard(true)}>
<i className="bi bi-plus-lg me-2"></i>
Crear Activo
</button>
</div>
) : isMobile ? (
// Mobile: Cards Layout para Ativos
<div className="d-flex flex-column gap-2">
{assetAccounts.map((asset) => (
<div
key={`asset-${asset.id}`}
className="card border-secondary"
style={{ cursor: 'pointer', background: '#0f172a' }}
>
<div className="card-body p-3">
{/* Header with Icon and Name */}
<div className="d-flex align-items-start gap-2 mb-2">
<div
className="rounded-circle d-flex align-items-center justify-content-center flex-shrink-0"
style={{
width: '35px',
height: '35px',
backgroundColor: (asset.color || '#10B981') + '25',
}}
>
<i className={`bi bi-${asset.asset_type === 'real_estate' ? 'house' : asset.asset_type === 'vehicle' ? 'truck' : asset.asset_type === 'investment' ? 'graph-up' : 'box'} fs-6`} style={{ color: asset.color || '#10B981' }}></i>
</div>
<div className="flex-grow-1" style={{ minWidth: 0 }}>
<div className="text-white fw-medium mb-1" style={{ fontSize: '0.85rem' }}>
{asset.name}
</div>
<div className="text-slate-400" style={{ fontSize: '0.65rem' }}>
{assetAccountService.statuses[asset.asset_type] || asset.asset_type}
</div>
</div>
<div>
<span className={`badge ${asset.status === 'active' ? 'bg-success' : 'bg-secondary'}`} style={{ fontSize: '0.65rem' }}>
{asset.status === 'active' ? 'Activo' : asset.status}
</span>
</div>
</div>
{/* Values */}
<div className="d-flex justify-content-between align-items-center pt-2" style={{ borderTop: '1px solid #334155' }}>
<div>
<div className="text-slate-400" style={{ fontSize: '0.65rem' }}>Valor Actual</div>
<div className="text-success fw-bold" style={{ fontSize: '0.9rem' }}>
{formatCurrency(asset.current_value, asset.currency)}
</div>
</div>
<div className="text-end">
<div className="text-slate-400" style={{ fontSize: '0.65rem' }}>Rentabilidad</div>
<div className={`fw-medium ${parseFloat(asset.current_value) >= parseFloat(asset.acquisition_value) ? 'text-success' : 'text-danger'}`} style={{ fontSize: '0.8rem' }}>
{asset.acquisition_value > 0 ? (
<>
<i className={`bi ${parseFloat(asset.current_value) >= parseFloat(asset.acquisition_value) ? 'bi-arrow-up' : 'bi-arrow-down'} me-1`}></i>
{(((parseFloat(asset.current_value) - parseFloat(asset.acquisition_value)) / parseFloat(asset.acquisition_value)) * 100).toFixed(1)}%
</>
) : '-'}
</div>
</div>
</div>
</div>
</div>
))}
</div>
) : (
// Desktop: Table Layout para Ativos
<div className="table-responsive">
<table className="table table-hover mb-0" style={{ '--bs-table-bg': 'transparent', backgroundColor: 'transparent' }}>
<thead style={{ backgroundColor: 'transparent' }}>
<tr style={{ borderBottom: '1px solid #334155', backgroundColor: 'transparent' }}>
<th className="text-slate-400 fw-normal py-3 ps-4" style={{ backgroundColor: 'transparent' }}>Nombre</th>
<th className="text-slate-400 fw-normal py-3" style={{ backgroundColor: 'transparent' }}>Tipo</th>
<th className="text-slate-400 fw-normal py-3 text-end" style={{ backgroundColor: 'transparent' }}>Adquisición</th>
<th className="text-slate-400 fw-normal py-3 text-end" style={{ backgroundColor: 'transparent' }}>Valor Actual</th>
<th className="text-slate-400 fw-normal py-3 text-center" style={{ backgroundColor: 'transparent' }}>Rentabilidad</th>
<th className="text-slate-400 fw-normal py-3 text-center" style={{ backgroundColor: 'transparent' }}>{t('common.status')}</th>
</tr>
</thead>
<tbody style={{ backgroundColor: 'transparent' }}>
{assetAccounts.map((asset) => {
const gainLoss = parseFloat(asset.current_value) - parseFloat(asset.acquisition_value);
const gainLossPercent = asset.acquisition_value > 0 ? (gainLoss / parseFloat(asset.acquisition_value)) * 100 : 0;
return (
<tr
key={`asset-${asset.id}`}
style={{ borderBottom: '1px solid #334155', backgroundColor: 'transparent', cursor: 'pointer' }}
onClick={() => handleOpenAssetDetail(asset)}
className="asset-row-hover"
>
<td className="py-3 ps-4" style={{ backgroundColor: 'transparent' }}>
<div className="d-flex align-items-center">
<div
className="rounded-circle d-flex align-items-center justify-content-center me-3"
style={{
width: '40px',
height: '40px',
backgroundColor: (asset.color || '#10B981') + '25',
}}
>
<i className={`bi bi-${asset.asset_type === 'real_estate' ? 'house' : asset.asset_type === 'vehicle' ? 'truck' : asset.asset_type === 'investment' ? 'graph-up' : 'box'}`} style={{ color: asset.color || '#10B981' }}></i>
</div>
<div>
<div className="text-white fw-medium">{asset.name}</div>
{asset.description && (
<small className="text-slate-400">{asset.description.substring(0, 30)}</small>
)}
</div>
</div>
</td>
<td className="py-3" style={{ backgroundColor: 'transparent' }}>
<span className="badge" style={{ backgroundColor: '#334155' }}>
{asset.asset_type === 'real_estate' ? 'Inmueble' :
asset.asset_type === 'vehicle' ? 'Vehículo' :
asset.asset_type === 'investment' ? 'Inversión' :
asset.asset_type === 'equipment' ? 'Equipamiento' :
asset.asset_type === 'receivable' ? 'Crédito' :
asset.asset_type === 'cash' ? 'Efectivo' :
asset.asset_type}
</span>
</td>
<td className="py-3 text-end text-slate-300" style={{ backgroundColor: 'transparent' }}>
{formatCurrency(asset.acquisition_value, asset.currency)}
</td>
<td className="py-3 text-end fw-medium text-success" style={{ backgroundColor: 'transparent' }}>
{formatCurrency(asset.current_value, asset.currency)}
</td>
<td className="py-3 text-center" style={{ backgroundColor: 'transparent' }}>
<span className={`fw-medium ${gainLoss >= 0 ? 'text-success' : 'text-danger'}`}>
<i className={`bi ${gainLoss >= 0 ? 'bi-arrow-up' : 'bi-arrow-down'} me-1`}></i>
{gainLossPercent.toFixed(1)}%
</span>
</td>
<td className="py-3 text-center" style={{ backgroundColor: 'transparent' }}>
<span className={`badge ${asset.status === 'active' ? 'bg-success' : 'bg-secondary'}`}>
{asset.status === 'active' ? 'Activo' : asset.status === 'disposed' ? 'Vendido' : asset.status}
</span>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
)}
</div>
</div>
</>
)}
{/* Asset Wizard Modal */}
<AssetWizard
isOpen={showAssetWizard}
onClose={() => {
setShowAssetWizard(false);
setEditingAsset(null);
}}
onSuccess={handleAssetSuccess}
asset={editingAsset}
/>
{/* Account Wizard Modal */}
<AccountWizard
isOpen={showAccountWizard}
onClose={() => {
setShowAccountWizard(false);
setEditingAccount(null);
}}
onSuccess={handleAccountWizardSuccess}
account={editingAccount}
/>
{/* Modal de Criar/Editar */}
{showModal && (
<div className="modal show d-block" style={{ backgroundColor: 'rgba(0,0,0,0.7)' }}>
<div className="modal-dialog modal-lg modal-dialog-centered">
<div className="modal-content" style={{ background: '#1e293b' }}>
<div className="modal-header border-bottom" style={{ borderColor: '#334155 !important' }}>
<h5 className="modal-title text-white">
<i className={`bi ${selectedAccount ? 'bi-pencil' : 'bi-plus-circle'} me-2`}></i>
{selectedAccount ? t('accounts.editAccount') : t('accounts.newAccount')}
</h5>
<button type="button" className="btn-close btn-close-white" onClick={handleCloseModal}></button>
</div>
<form onSubmit={handleSubmit}>
<div className="modal-body">
<div className="row g-3">
{/* Nome */}
<div className="col-md-8">
<label className="form-label text-slate-300">{t('accounts.accountName')} *</label>
<input
type="text"
className="form-control bg-dark text-white border-secondary"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Ex: Conta Principal, Cartão Nubank..."
required
/>
</div>
{/* Tipo */}
<div className="col-md-4">
<label className="form-label text-slate-300">{t('common.type')} *</label>
<select
className="form-select bg-dark text-white border-secondary"
name="type"
value={formData.type}
onChange={handleChange}
required
>
{Object.entries(accountTypes).map(([key, label]) => (
<option key={key} value={key}>{label}</option>
))}
</select>
</div>
{/* Banco */}
<div className="col-md-6">
<label className="form-label text-slate-300">{t('accounts.bankName')}</label>
<input
type="text"
className="form-control bg-dark text-white border-secondary"
name="bank_name"
value={formData.bank_name}
onChange={handleChange}
placeholder="Ex: Banco do Brasil, Nubank..."
/>
</div>
{/* Número da Conta */}
<div className="col-md-6">
<label className="form-label text-slate-300">{t('accounts.accountNumber')}</label>
<input
type="text"
className="form-control bg-dark text-white border-secondary"
name="account_number"
value={formData.account_number}
onChange={handleChange}
placeholder="Ex: 12345-6"
/>
</div>
{/* Saldo Inicial */}
<div className="col-md-4">
<label className="form-label text-slate-300">{t('accounts.initialBalance')}</label>
<input
type="number"
step="0.01"
className="form-control bg-dark text-white border-secondary"
name="initial_balance"
value={formData.initial_balance}
onChange={handleChange}
/>
</div>
{/* Limite de Crédito (para cartões) */}
{(formData.type === 'credit_card' || formData.type === 'liability') && (
<div className="col-md-4">
<label className="form-label text-slate-300">{t('accounts.creditLimit')}</label>
<input
type="number"
step="0.01"
className="form-control bg-dark text-white border-secondary"
name="credit_limit"
value={formData.credit_limit}
onChange={handleChange}
/>
</div>
)}
{/* Moeda */}
<div className="col-md-4">
<label className="form-label text-slate-300">{t('accounts.currency')}</label>
<CurrencySelector
value={formData.currency}
onChange={(currency) => setFormData(prev => ({ ...prev, currency }))}
/>
</div>
{/* Cor */}
<div className="col-md-3">
<label className="form-label text-slate-300">{t('common.color')}</label>
<input
type="color"
className="form-control form-control-color bg-dark border-secondary w-100"
name="color"
value={formData.color}
onChange={handleChange}
/>
</div>
{/* Ícone */}
<div className="col-md-5">
<label className="form-label text-slate-300">{t('common.icon')}</label>
<IconSelector
value={formData.icon}
onChange={(icon) => setFormData(prev => ({ ...prev, icon }))}
type="account"
/>
</div>
{/* Descrição */}
<div className="col-12">
<label className="form-label text-slate-300">{t('common.description')}</label>
<textarea
className="form-control bg-dark text-white border-secondary"
name="description"
value={formData.description}
onChange={handleChange}
rows="2"
placeholder={t('accounts.descriptionPlaceholder')}
></textarea>
</div>
{/* Checkboxes */}
<div className="col-md-6">
<div className="form-check">
<input
type="checkbox"
className="form-check-input"
id="is_active"
name="is_active"
checked={formData.is_active}
onChange={handleChange}
/>
<label className="form-check-label text-slate-300" htmlFor="is_active">
{t('common.active')}
</label>
</div>
</div>
<div className="col-md-6">
<div className="form-check">
<input
type="checkbox"
className="form-check-input"
id="include_in_total"
name="include_in_total"
checked={formData.include_in_total}
onChange={handleChange}
/>
<label className="form-check-label text-slate-300" htmlFor="include_in_total">
{t('accounts.includeInTotal')}
</label>
</div>
</div>
</div>
</div>
<div className="modal-footer border-top" style={{ borderColor: '#334155 !important' }}>
<button type="button" className="btn btn-outline-light" onClick={handleCloseModal}>
{t('common.cancel')}
</button>
<button type="submit" className="btn btn-primary" disabled={saving}>
{saving ? (
<>
<span className="spinner-border spinner-border-sm me-2"></span>
{t('common.loading')}
</>
) : (
<>
<i className="bi bi-check-lg me-2"></i>
{selectedAccount ? t('common.save') : t('common.create')}
</>
)}
</button>
</div>
</form>
</div>
</div>
</div>
)}
{/* Modal de Ajuste de Saldo */}
{showAdjustModal && adjustAccount && (
<div className="modal show d-block" style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}>
<div className="modal-dialog modal-dialog-centered">
<div className="modal-content" style={{ background: '#1e293b', border: '1px solid #334155' }}>
<div className="modal-header border-0">
<h5 className="modal-title text-white">
<i className="bi bi-sliders me-2 text-success"></i>
{t('accounts.adjustBalance')}
</h5>
<button
type="button"
className="btn-close btn-close-white"
onClick={() => setShowAdjustModal(false)}
></button>
</div>
<div className="modal-body">
<div className="alert alert-info bg-info bg-opacity-10 border-info">
<i className="bi bi-info-circle me-2"></i>
{t('accounts.adjustInfo')}
</div>
<div className="mb-3">
<label className="form-label text-white">{t('accounts.accountName')}</label>
<input
type="text"
className="form-control bg-dark text-white border-secondary"
value={adjustAccount.name}
disabled
/>
</div>
<div className="mb-3">
<label className="form-label text-white">{t('accounts.currentBalance')}</label>
<input
type="text"
className="form-control bg-dark text-white border-secondary"
value={formatCurrency(adjustAccount.current_balance, adjustAccount.currency)}
disabled
/>
</div>
<div className="mb-3">
<label className="form-label text-white">
{t('accounts.targetBalance')} <span className="text-danger">*</span>
</label>
<input
type="number"
step="0.01"
className="form-control bg-dark text-white border-secondary"
value={targetBalance}
onChange={(e) => setTargetBalance(e.target.value)}
placeholder={t('accounts.targetBalancePlaceholder')}
/>
<small className="text-muted">
{t('accounts.targetBalanceHelp')}
</small>
</div>
</div>
<div className="modal-footer border-0">
<button
type="button"
className="btn btn-secondary"
onClick={() => setShowAdjustModal(false)}
>
{t('common.cancel')}
</button>
<button
type="button"
className="btn btn-success"
onClick={handleAdjustBalance}
disabled={adjusting || targetBalance === ''}
>
{adjusting ? (
<>
<span className="spinner-border spinner-border-sm me-2"></span>
{t('common.loading')}
</>
) : (
<>
<i className="bi bi-check-lg me-2"></i>
{t('accounts.adjust')}
</>
)}
</button>
</div>
</div>
</div>
</div>
)}
{/* Modal de Detalhes do Ativo */}
{showAssetDetail && selectedAsset && (
<div className="modal fade show d-block" style={{ backgroundColor: 'rgba(0,0,0,0.8)' }}>
<div className="modal-dialog modal-lg modal-dialog-centered">
<div className="modal-content" style={{ backgroundColor: '#1e293b', border: '1px solid #334155' }}>
<div className="modal-header border-0">
<div className="d-flex align-items-center">
<div
className="rounded-circle d-flex align-items-center justify-content-center me-3"
style={{
width: '48px',
height: '48px',
backgroundColor: (selectedAsset.color || '#10B981') + '25',
}}
>
<i className={`bi bi-${selectedAsset.asset_type === 'real_estate' ? 'house' : selectedAsset.asset_type === 'vehicle' ? 'truck' : selectedAsset.asset_type === 'investment' ? 'graph-up' : 'box'} fs-4`} style={{ color: selectedAsset.color || '#10B981' }}></i>
</div>
<div>
<h5 className="modal-title text-white mb-0">{selectedAsset.name}</h5>
{selectedAsset.description && (
<small className="text-slate-400">{selectedAsset.description}</small>
)}
</div>
</div>
<button type="button" className="btn-close btn-close-white" onClick={handleCloseAssetDetail}></button>
</div>
<div className="modal-body">
{/* Valores principais */}
<div className="row g-3 mb-4">
<div className="col-md-4">
<div className="p-3 rounded" style={{ backgroundColor: '#0f172a' }}>
<div className="text-slate-400 small mb-1">Valor de Adquisición</div>
<div className="text-white fs-5 fw-bold">
{formatCurrency(selectedAsset.acquisition_value, selectedAsset.currency)}
</div>
</div>
</div>
<div className="col-md-4">
<div className="p-3 rounded" style={{ backgroundColor: '#0f172a' }}>
<div className="text-slate-400 small mb-1">Valor Actual</div>
<div className="text-success fs-5 fw-bold">
{formatCurrency(selectedAsset.current_value, selectedAsset.currency)}
</div>
</div>
</div>
<div className="col-md-4">
<div className="p-3 rounded" style={{ backgroundColor: '#0f172a' }}>
<div className="text-slate-400 small mb-1">Rentabilidad</div>
{(() => {
const gainLoss = parseFloat(selectedAsset.current_value) - parseFloat(selectedAsset.acquisition_value);
const gainLossPercent = selectedAsset.acquisition_value > 0 ? (gainLoss / parseFloat(selectedAsset.acquisition_value)) * 100 : 0;
return (
<div className={`fs-5 fw-bold ${gainLoss >= 0 ? 'text-success' : 'text-danger'}`}>
<i className={`bi ${gainLoss >= 0 ? 'bi-arrow-up' : 'bi-arrow-down'} me-1`}></i>
{formatCurrency(Math.abs(gainLoss), selectedAsset.currency)} ({gainLossPercent.toFixed(1)}%)
</div>
);
})()}
</div>
</div>
</div>
{/* Informações detalhadas */}
<div className="row g-3">
<div className="col-12">
<h6 className="text-white mb-3">
<i className="bi bi-info-circle me-2"></i>
Información del Activo
</h6>
</div>
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Tipo de Activo</span>
<div className="text-white">
{selectedAsset.asset_type === 'real_estate' ? 'Inmueble' :
selectedAsset.asset_type === 'vehicle' ? 'Vehículo' :
selectedAsset.asset_type === 'investment' ? 'Inversión' :
selectedAsset.asset_type === 'equipment' ? 'Equipamiento' :
selectedAsset.asset_type === 'receivable' ? 'Crédito por Cobrar' :
selectedAsset.asset_type === 'cash' ? 'Efectivo' :
selectedAsset.asset_type}
</div>
</div>
</div>
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Estado</span>
<div>
<span className={`badge ${selectedAsset.status === 'active' ? 'bg-success' : 'bg-secondary'}`}>
{selectedAsset.status === 'active' ? 'Activo' : selectedAsset.status === 'disposed' ? 'Vendido' : selectedAsset.status}
</span>
</div>
</div>
</div>
{selectedAsset.acquisition_date && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Fecha de Adquisición</span>
<div className="text-white">
{new Date(selectedAsset.acquisition_date).toLocaleDateString('es-ES', { day: '2-digit', month: 'long', year: 'numeric' })}
</div>
</div>
</div>
)}
{/* Campos específicos por tipo */}
{selectedAsset.asset_type === 'real_estate' && (
<>
{selectedAsset.address && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Dirección</span>
<div className="text-white">{selectedAsset.address}</div>
</div>
</div>
)}
{selectedAsset.city && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Ciudad</span>
<div className="text-white">{selectedAsset.city}</div>
</div>
</div>
)}
{selectedAsset.property_area_m2 && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Área</span>
<div className="text-white">{selectedAsset.property_area_m2} </div>
</div>
</div>
)}
</>
)}
{selectedAsset.asset_type === 'vehicle' && (
<>
{selectedAsset.vehicle_brand && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Marca/Modelo</span>
<div className="text-white">{selectedAsset.vehicle_brand} {selectedAsset.vehicle_model}</div>
</div>
</div>
)}
{selectedAsset.vehicle_year && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Año</span>
<div className="text-white">{selectedAsset.vehicle_year}</div>
</div>
</div>
)}
{selectedAsset.vehicle_plate && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Matrícula</span>
<div className="text-white">{selectedAsset.vehicle_plate}</div>
</div>
</div>
)}
{selectedAsset.vehicle_mileage && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Kilometraje</span>
<div className="text-white">{selectedAsset.vehicle_mileage.toLocaleString()} km</div>
</div>
</div>
)}
</>
)}
{selectedAsset.asset_type === 'investment' && (
<>
{selectedAsset.investment_type && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Tipo de Inversión</span>
<div className="text-white">
{selectedAsset.investment_type === 'stocks' ? 'Acciones' :
selectedAsset.investment_type === 'bonds' ? 'Bonos' :
selectedAsset.investment_type === 'etf' ? 'ETF' :
selectedAsset.investment_type === 'mutual_fund' ? 'Fondo Mutuo' :
selectedAsset.investment_type === 'crypto' ? 'Criptomoneda' :
selectedAsset.investment_type === 'fixed_deposit' ? 'Depósito a Plazo' :
selectedAsset.investment_type}
</div>
</div>
</div>
)}
{selectedAsset.institution && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Institución</span>
<div className="text-white">{selectedAsset.institution}</div>
</div>
</div>
)}
{selectedAsset.ticker && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Ticker/Símbolo</span>
<div className="text-white">{selectedAsset.ticker}</div>
</div>
</div>
)}
{selectedAsset.quantity && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Cantidad</span>
<div className="text-white">{selectedAsset.quantity}</div>
</div>
</div>
)}
{selectedAsset.interest_rate && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Tasa de Interés</span>
<div className="text-white">{selectedAsset.interest_rate}%</div>
</div>
</div>
)}
{selectedAsset.maturity_date && (
<div className="col-md-6">
<div className="mb-3">
<span className="text-slate-400 small">Fecha de Vencimiento</span>
<div className="text-white">
{new Date(selectedAsset.maturity_date).toLocaleDateString('es-ES', { day: '2-digit', month: 'long', year: 'numeric' })}
</div>
</div>
</div>
)}
</>
)}
</div>
</div>
<div className="modal-footer border-0">
<button type="button" className="btn btn-primary me-2" onClick={handleEditAsset}>
<i className="bi bi-pencil me-2"></i>
Editar
</button>
<button type="button" className="btn btn-secondary" onClick={handleCloseAssetDetail}>
Cerrar
</button>
</div>
</div>
</div>
</div>
)}
{/* Modal de Confirmação de Exclusão */}
<ConfirmModal
show={showDeleteModal}
onHide={() => setShowDeleteModal(false)}
onConfirm={handleDeleteConfirm}
title={t('accounts.deleteAccount')}
message={t('accounts.deleteConfirm')}
confirmText={t('common.delete')}
loading={saving}
/>
</div>
);
};
export default Accounts;