118 lines
3.4 KiB
TypeScript
118 lines
3.4 KiB
TypeScript
import { useState, useEffect } from 'react';
|
||
import type { Account } from '@family-budget/shared';
|
||
import { getAccounts, updateAccount } from '../api/accounts';
|
||
|
||
export function AccountsList() {
|
||
const [accounts, setAccounts] = useState<Account[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [editingId, setEditingId] = useState<number | null>(null);
|
||
const [editAlias, setEditAlias] = useState('');
|
||
|
||
useEffect(() => {
|
||
setLoading(true);
|
||
getAccounts()
|
||
.then(setAccounts)
|
||
.catch(() => {})
|
||
.finally(() => setLoading(false));
|
||
}, []);
|
||
|
||
const handleEdit = (account: Account) => {
|
||
setEditingId(account.id);
|
||
setEditAlias(account.alias || '');
|
||
};
|
||
|
||
const handleSave = async (id: number) => {
|
||
try {
|
||
const updated = await updateAccount(id, {
|
||
alias: editAlias.trim(),
|
||
});
|
||
setAccounts((prev) =>
|
||
prev.map((a) => (a.id === id ? updated : a)),
|
||
);
|
||
setEditingId(null);
|
||
} catch {
|
||
// error handled globally
|
||
}
|
||
};
|
||
|
||
if (loading) {
|
||
return <div className="section-loading">Загрузка...</div>;
|
||
}
|
||
|
||
return (
|
||
<div className="settings-section">
|
||
<table className="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Банк</th>
|
||
<th>Номер счёта</th>
|
||
<th>Валюта</th>
|
||
<th>Алиас</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{accounts.map((a) => (
|
||
<tr key={a.id}>
|
||
<td>{a.bank}</td>
|
||
<td>{a.accountNumberMasked}</td>
|
||
<td>{a.currency}</td>
|
||
<td>
|
||
{editingId === a.id ? (
|
||
<input
|
||
type="text"
|
||
value={editAlias}
|
||
onChange={(e) => setEditAlias(e.target.value)}
|
||
maxLength={50}
|
||
autoFocus
|
||
onKeyDown={(e) => {
|
||
if (e.key === 'Enter') handleSave(a.id);
|
||
if (e.key === 'Escape') setEditingId(null);
|
||
}}
|
||
/>
|
||
) : (
|
||
a.alias || (
|
||
<span className="text-muted">не задан</span>
|
||
)
|
||
)}
|
||
</td>
|
||
<td>
|
||
{editingId === a.id ? (
|
||
<div className="btn-group">
|
||
<button
|
||
className="btn btn-sm btn-primary"
|
||
onClick={() => handleSave(a.id)}
|
||
>
|
||
Сохранить
|
||
</button>
|
||
<button
|
||
className="btn btn-sm btn-secondary"
|
||
onClick={() => setEditingId(null)}
|
||
>
|
||
Отмена
|
||
</button>
|
||
</div>
|
||
) : (
|
||
<button
|
||
className="btn btn-sm btn-secondary"
|
||
onClick={() => handleEdit(a)}
|
||
>
|
||
Изменить
|
||
</button>
|
||
)}
|
||
</td>
|
||
</tr>
|
||
))}
|
||
{accounts.length === 0 && (
|
||
<tr>
|
||
<td colSpan={5} className="td-center text-muted">
|
||
Нет счетов. Импортируйте выписку.
|
||
</td>
|
||
</tr>
|
||
)}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
);
|
||
}
|