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
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m56s
This commit is contained in:
@@ -217,7 +217,7 @@ function ASNsNewManager() {
|
||||
{error}
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn-close" onClick={() => setError('')}></a>
|
||||
<button type="button" className="btn-close" onClick={() => setError('')}></button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -231,7 +231,7 @@ function ASNsNewManager() {
|
||||
{success}
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn-close" onClick={() => setSuccess('')}></a>
|
||||
<button type="button" className="btn-close" onClick={() => setSuccess('')}></button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ function DataManager({ entityName, entityKey, placeholder }) {
|
||||
{error}
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn-close" onClick={() => setError('')}></a>
|
||||
<button type="button" className="btn-close" onClick={() => setError('')}></button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -204,7 +204,7 @@ function DataManager({ entityName, entityKey, placeholder }) {
|
||||
{success}
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn-close" onClick={() => setSuccess('')}></a>
|
||||
<button type="button" className="btn-close" onClick={() => setSuccess('')}></button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ function DomainsNewManager() {
|
||||
{error}
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn-close" onClick={() => setError('')}></a>
|
||||
<button type="button" className="btn-close" onClick={() => setError('')}></button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -236,7 +236,7 @@ function DomainsNewManager() {
|
||||
{success}
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn-close" onClick={() => setSuccess('')}></a>
|
||||
<button type="button" className="btn-close" onClick={() => setSuccess('')}></button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ function IPRangesManager() {
|
||||
{error}
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn-close" onClick={() => setError('')}></a>
|
||||
<button type="button" className="btn-close" onClick={() => setError('')}></button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -246,7 +246,7 @@ function IPRangesManager() {
|
||||
{success}
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn-close" onClick={() => setSuccess('')}></a>
|
||||
<button type="button" className="btn-close" onClick={() => setSuccess('')}></button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -428,7 +428,7 @@ function ServerManager() {
|
||||
{error}
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn-close" onClick={() => setError('')}></a>
|
||||
<button type="button" className="btn-close" onClick={() => setError('')}></button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -442,7 +442,7 @@ function ServerManager() {
|
||||
{success}
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn-close" onClick={() => setSuccess('')}></a>
|
||||
<button type="button" className="btn-close" onClick={() => setSuccess('')}></button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -760,9 +760,9 @@ function ServerManager() {
|
||||
<div className="card-header">
|
||||
<h3 className="card-title mb-0">Существующие связи ({connections.length})</h3>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<div className="table-responsive">
|
||||
<table className="table table-vcenter">
|
||||
<div className="card-body">
|
||||
<div className="table-responsive">
|
||||
<table className="table card-table table-vcenter table-nowrap mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Сервер A</th>
|
||||
|
||||
Reference in New Issue
Block a user