feat: Implement payment editing functionality in BillingManager for enhanced payment management
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m37s

This commit is contained in:
2025-08-08 11:50:11 +07:00
parent fdad8ce5e1
commit 560120f5b8
+62 -38
View File
@@ -54,6 +54,7 @@ function BillingManager() {
currency: 'USD',
note: ''
});
const [editingPayment, setEditingPayment] = useState(null); // { serverId, index }
// Состояние для курсов валют
const [exchangeRates, setExchangeRates] = useState({
@@ -866,11 +867,12 @@ function BillingManager() {
<th>Сумма</th>
<th>Валюта</th>
<th>Комментарий</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
{(() => {
const rows = billingData.flatMap(server => (server.payments || []).map((p, idx) => ({ server, ...p, _key: `${server.id}-${idx}` }))).sort((a,b) => new Date(b.date) - new Date(a.date));
const rows = billingData.flatMap(server => (server.payments || []).map((p, idx) => ({ server, ...p, _key: `${server.id}-${idx}`, _index: idx }))).sort((a,b) => new Date(b.date) - new Date(a.date));
if (rows.length === 0) {
return null;
}
@@ -887,6 +889,27 @@ function BillingManager() {
</td>
<td><span className="badge bg-blue-lt text-blue">{r.currency || 'USD'}</span></td>
<td className="text-muted small">{r.note || ''}</td>
<td>
<div className="btn-list">
<button
className="btn btn-outline-primary btn-icon"
title="Редактировать платеж"
onClick={() => {
setPaymentDraft({
serverId: r.server.id,
date: r.date,
amount: r.amount,
currency: r.currency || 'USD',
note: r.note || ''
});
setEditingPayment({ serverId: r.server.id, index: r._index });
setShowPaymentModal(true);
}}
>
<IconEdit size={18} />
</button>
</div>
</td>
</tr>
));
})()}
@@ -970,11 +993,46 @@ function BillingManager() {
setTimeout(() => setError(''), 3000);
return;
}
// Режим редактирования платежа
if (editingPayment && editingPayment.serverId) {
setBillingData(prev => prev.map(s => {
if (s.id !== editingPayment.serverId) return s;
const payments = Array.isArray(s.payments) ? [...s.payments] : [];
if (editingPayment.index != null && payments[editingPayment.index]) {
payments[editingPayment.index] = {
date: paymentDraft.date,
amount: paymentDraft.amount,
currency: paymentDraft.currency || 'USD',
note: paymentDraft.note || ''
};
}
// обновляем lastPayment* по последней дате
const last = payments
.slice()
.sort((a,b) => new Date(b.date) - new Date(a.date))[0];
return {
...s,
payments,
lastPaymentDate: last?.date || '',
lastPaymentAmount: last?.amount || 0,
lastPaymentCurrency: last?.currency || s.monthlyCostCurrency || 'USD'
};
}));
setEditingPayment(null);
setShowPaymentModal(false);
setSuccess('Платеж обновлен');
setTimeout(() => setSuccess(''), 3000);
return;
}
// Режим добавления платежа
setBillingData(prev => prev.map(s => {
if (s.id !== paymentDraft.serverId) return s;
const payments = Array.isArray(s.payments) ? [...s.payments] : [];
payments.push({ date: paymentDraft.date, amount: paymentDraft.amount, currency: paymentDraft.currency || 'USD', note: paymentDraft.note || '' });
return { ...s, payments, lastPaymentDate: paymentDraft.date, lastPaymentAmount: paymentDraft.amount, lastPaymentCurrency: paymentDraft.currency || s.monthlyCostCurrency || 'USD' };
const last = payments
.slice()
.sort((a,b) => new Date(b.date) - new Date(a.date))[0];
return { ...s, payments, lastPaymentDate: last?.date || '', lastPaymentAmount: last?.amount || 0, lastPaymentCurrency: last?.currency || s.monthlyCostCurrency || 'USD' };
}));
setShowPaymentModal(false);
setSuccess('Платеж добавлен');
@@ -1111,42 +1169,8 @@ function AddBillingModal({ show, item, onItemChange, onSubmit, onClose }) {
/>
</div>
{/* Исторические платежи */}
<div className="col-12">
<h6 className="text-muted mb-3 mt-4">Исторические платежи</h6>
</div>
<div className="col-md-6">
<label className="form-label">Дата последнего платежа</label>
<input
type="date"
className="form-control"
value={item.lastPaymentDate}
onChange={(e) => onItemChange({ ...item, lastPaymentDate: e.target.value })}
/>
</div>
<div className="col-md-6">
<label className="form-label">Сумма последнего платежа</label>
<input
type="number"
step="0.01"
className="form-control"
value={item.lastPaymentAmount}
onChange={(e) => onItemChange({ ...item, lastPaymentAmount: parseFloat(e.target.value) || 0 })}
/>
</div>
<div className="col-md-6">
<label className="form-label">Валюта последнего платежа</label>
<select
className="form-select"
value={item.lastPaymentCurrency}
onChange={(e) => onItemChange({ ...item, lastPaymentCurrency: e.target.value })}
>
<option value="USD">USD</option>
<option value="EUR">EUR</option>
<option value="RUB">RUB</option>
</select>
</div>
<div className="col-md-6">
{/* Дополнительно */}
<div className="col-md-12">
<label className="form-label">Заметки</label>
<input
type="text"