feat: Implement payment editing functionality in BillingManager for enhanced payment management
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m37s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m37s
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user