fix: remove inline JS handlers (onclick/onchange/onsubmit) -> event delegation + bump 0.4.4

This commit is contained in:
2026-05-31 06:26:47 +03:00
parent a6177c4cef
commit c19480336d
2 changed files with 52 additions and 37 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ipwhitelist",
"version": "0.4.3",
"version": "0.4.4",
"description": "IP WhiteList microservice for cloud provider",
"main": "server.js",
"scripts": {
+51 -36
View File
@@ -185,7 +185,7 @@
<div class="card-body" style="padding:.75rem 1rem;">
<form method="GET" action="/" style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap;">
<span style="font-size:.85rem;color:var(--muted);font-weight:500;">Компания:</span>
<select name="company" onchange="this.form.submit()"
<select name="company" id="company-select"
style="padding:.4rem .75rem;border:1px solid var(--border);border-radius:6px;font-size:.85rem;background:#fff;">
<% companies.forEach(c => { %>
<option value="<%= c.id %>" <%= selectedCompany && c.id === selectedCompany.id ? 'selected' : '' %>>
@@ -194,6 +194,7 @@
<% }) %>
</select>
<!-- Быстрый доступ к admin-страницам прямо из главной -->
<button type="submit" class="btn btn-sm">Выбрать</button>
<a href="/audit<%= selectedCompany ? '?company=' + selectedCompany.id : '' %>"
style="font-size:.82rem;color:var(--muted);text-decoration:none;">→ Аудит этой компании</a>
<a href="/admin"
@@ -296,19 +297,19 @@
// Но в data-атрибуте JSON::stringify безопаснее.
%>
<!-- Кнопка редактирования: открывает modal и заполняет его данными из data-* -->
<button class="btn"
<button class="btn btn-edit"
data-id="<%= e.id %>"
data-value="<%= e.value_cidr %>"
data-comment="<%= e.comment || '' %>"
data-company="<%= companyPk || '' %>"
onclick="openEditModal(this)"
style="margin-right:.25rem;">
Изменить
</button>
<!-- Форма удаления: company_id нужен для admin-ветки в server.js -->
<form method="POST" action="/delete/<%= e.id %>"
style="display:inline"
onsubmit="return confirm('Удалить запись <%= e.value_cidr %>?')">
class="delete-form"
data-cidr="<%= e.value_cidr %>"
style="display:inline">
<input type="hidden" name="company_id" value="<%= companyPk || '' %>">
<%# CSRF-токен: одно значение на всю страницу, сгенерировано сервером %>
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
@@ -361,7 +362,7 @@
style="padding:.5rem .75rem;border:1px solid var(--border);border-radius:6px;font-size:.9rem;">
</div>
<div style="display:flex;gap:.75rem;justify-content:flex-end;">
<button type="button" class="btn" onclick="closeEditModal()">Отмена</button>
<button type="button" class="btn" id="edit-cancel">Отмена</button>
<button type="submit" class="btn btn-primary">Сохранить</button>
</div>
</form>
@@ -369,39 +370,53 @@
</div>
<script>
/**
* Открыть modal редактирования.
* Вызывается из onclick кнопки "Изменить" в таблице.
* @param {HTMLElement} btn — кнопка с data-id, data-value, data-comment, data-company
*/
function openEditModal(btn) {
// Заполняем поля из data-атрибутов (EJS уже HTML-экранировал значения)
document.getElementById('edit-value').value = btn.dataset.value;
document.getElementById('edit-comment').value = btn.dataset.comment;
document.getElementById('edit-company-id').value = btn.dataset.company;
// Устанавливаем action формы на конкретный /edit/:id
document.getElementById('edit-form').action = '/edit/' + btn.dataset.id;
// Показываем modal (display:flex для центрирования)
const modal = document.getElementById('edit-modal');
modal.style.display = 'flex';
// Фокус на поле ввода для удобства клавиатурных пользователей
setTimeout(() => document.getElementById('edit-value').focus(), 50);
}
(function () {
var modal = document.getElementById('edit-modal');
/** Закрыть modal без сохранения */
function closeEditModal() {
document.getElementById('edit-modal').style.display = 'none';
}
function openModal(btn) {
document.getElementById('edit-value').value = btn.getAttribute('data-value');
document.getElementById('edit-comment').value = btn.getAttribute('data-comment');
document.getElementById('edit-company-id').value = btn.getAttribute('data-company');
document.getElementById('edit-form').action = '/edit/' + btn.getAttribute('data-id');
modal.style.display = 'flex';
setTimeout(function() { document.getElementById('edit-value').focus(); }, 50);
}
// Закрытие кликом по backdrop (за пределами карточки)
document.getElementById('edit-modal').addEventListener('click', function(e) {
if (e.target === this) closeEditModal();
});
function closeModal() {
modal.style.display = 'none';
}
// Закрытие клавишей Escape
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') closeEditModal();
});
// Кнопки «Изменить» — event delegation на document
document.addEventListener('click', function (e) {
var btn = e.target.closest('.btn-edit');
if (btn) { openModal(btn); return; }
var cancel = e.target.closest('#edit-cancel');
if (cancel) { closeModal(); return; }
// Клик по backdrop
if (e.target === modal) { closeModal(); return; }
});
// Подтверждение удаления — event delegation
document.addEventListener('submit', function (e) {
var form = e.target.closest('.delete-form');
if (!form) return;
var cidr = form.getAttribute('data-cidr');
if (!confirm('Удалить запись ' + cidr + '?')) e.preventDefault();
});
// Смена компании — авто-сабмит при выборе
var sel = document.getElementById('company-select');
if (sel) {
sel.addEventListener('change', function () { this.form.submit(); });
}
// Escape закрывает modal
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') closeModal();
});
}());
</script>
<footer style="text-align:right;padding:.5rem 1.5rem;font-size:.72rem;color:var(--muted);">v<%= appVersion %></footer>