feat: Replace anchor tags with button elements for close actions in multiple managers to improve accessibility and semantic HTML
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m56s

This commit is contained in:
2025-08-08 12:16:09 +07:00
parent ebfc5a9fab
commit 389725c023
6 changed files with 91 additions and 14 deletions
+78 -1
View File
@@ -55,6 +55,8 @@ function BillingManager() {
note: ''
});
const [editingPayment, setEditingPayment] = useState(null); // { serverId, index }
const [showDeletePaymentModal, setShowDeletePaymentModal] = useState(false);
const [paymentToDelete, setPaymentToDelete] = useState(null); // { serverId, index, data }
// Состояние для курсов валют
const [exchangeRates, setExchangeRates] = useState({
@@ -349,6 +351,36 @@ function BillingManager() {
}
}
// Удаление платежа из истории
const handleDeletePayment = () => {
if (!paymentToDelete || !paymentToDelete.serverId || paymentToDelete.index == null) {
setShowDeletePaymentModal(false);
return;
}
const { serverId, index } = paymentToDelete;
setBillingData(prev => prev.map(s => {
if (s.id !== serverId) return s;
const payments = Array.isArray(s.payments) ? [...s.payments] : [];
if (index >= 0 && index < payments.length) {
payments.splice(index, 1);
}
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'
};
}));
setShowDeletePaymentModal(false);
setPaymentToDelete(null);
setSuccess('Платеж удалён');
setTimeout(() => setSuccess(''), 3000);
};
function getCountryFlag(countryRaw) {
if (!countryRaw) return '';
const value = String(countryRaw).trim();
@@ -893,7 +925,7 @@ function BillingManager() {
</div>
<div className="card-body">
<div className="table-responsive">
<table className="table table-vcenter">
<table className="table card-table table-vcenter table-nowrap mb-0">
<thead>
<tr>
<th>Имя хостера</th>
@@ -954,6 +986,16 @@ function BillingManager() {
>
<IconEdit size={18} />
</button>
<button
className="btn btn-outline-danger btn-icon"
title="Удалить платеж"
onClick={() => {
setPaymentToDelete({ serverId: r.server.id, index: r._index, data: r });
setShowDeletePaymentModal(true);
}}
>
<IconTrash size={18} />
</button>
</div>
</td>
</tr>
@@ -1086,6 +1128,41 @@ function BillingManager() {
}}
/>
)}
{/* Модалка удаления платежа */}
{showDeletePaymentModal && (
<div className="modal show d-block" tabIndex="-1" style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Удалить платеж</h5>
<button type="button" className="btn-close" onClick={() => setShowDeletePaymentModal(false)}></button>
</div>
<div className="modal-body">
<p>Вы уверены, что хотите удалить выбранный платеж?</p>
{paymentToDelete && (
<div className="alert alert-warning">
<div><strong>Сервер:</strong> {paymentToDelete.data?.server?.hostName}</div>
<div><strong>Дата:</strong> {formatDate(paymentToDelete.data?.date)}</div>
<div><strong>Сумма:</strong> {formatCurrency(paymentToDelete.data?.amount, paymentToDelete.data?.currency)}</div>
{paymentToDelete.data?.note && (
<div><strong>Комментарий:</strong> {paymentToDelete.data?.note}</div>
)}
</div>
)}
</div>
<div className="modal-footer">
<button type="button" className="btn btn-secondary" onClick={() => setShowDeletePaymentModal(false)}>
Отмена
</button>
<button type="button" className="btn btn-danger" onClick={handleDeletePayment}>
Удалить
</button>
</div>
</div>
</div>
</div>
)}
</div>
);
}