Files
autoban/static/js/bans.js
2026-01-01 02:13:34 +01:00

147 lines
6.0 KiB
JavaScript

(function () {
const $ = (sel, root = document) => root.querySelector(sel);
const $$ = (sel, root = document) => Array.from(root.querySelectorAll(sel));
// --- SEARCH / FILTER (client-side) ---
const searchInput = $('#search-input');
const rows = () => $$('#bans-tbody tr.ban-row');
const noDataRow = () => $('#bans-tbody tr[data-empty]');
function applySearch() {
const q = (searchInput?.value || '').trim().toLowerCase();
let visible = 0;
rows().forEach(r => {
const text = r.textContent.toLowerCase();
const show = !q || text.includes(q);
r.classList.toggle('d-none', !show);
if (show) visible++;
});
if (!visible && !noDataRow()) {
const tr = document.createElement('tr');
tr.setAttribute('data-empty', '');
tr.innerHTML = '<td colspan="6" class="text-center py-5 text-secondary">Brak wyników dla podanego filtra</td>';
$('#bans-tbody').appendChild(tr);
} else if (visible && noDataRow()) {
noDataRow().remove();
}
}
let t;
searchInput?.addEventListener('input', () => { clearTimeout(t); t = setTimeout(applySearch, 120); });
// --- BULK SELECT ---
const selectAll = $('#select-all');
const counter = $('#selection-counter');
const delBtn = $('#delete-selected');
const bulkForm = $('#bulk-form');
function currentChecks() { return $$('.row-check'); }
function selectedCount() { return currentChecks().filter(c => c.checked).length; }
function updateState() {
const total = currentChecks().length;
const sel = selectedCount();
if (counter) counter.textContent = sel + ' zaznaczonych';
if (delBtn) delBtn.disabled = sel === 0;
if (selectAll) {
selectAll.checked = sel > 0 && sel === total;
selectAll.indeterminate = sel > 0 && sel < total;
}
}
selectAll?.addEventListener('change', () => {
currentChecks().forEach(c => (c.checked = selectAll.checked));
updateState();
});
document.addEventListener('change', (e) => {
if (e.target.classList?.contains('row-check')) updateState();
});
// Kliknięcie w wiersz przełącza zaznaczenie (poza elementami interaktywnymi)
document.addEventListener('click', (e) => {
const row = e.target.closest('tr.ban-row');
if (!row) return;
if (e.target.closest('input,button,a,label,select,textarea')) return;
const cb = row.querySelector('.row-check');
if (cb) { cb.checked = !cb.checked; updateState(); }
});
updateState();
// Potwierdzenia akcji
bulkForm?.addEventListener('submit', (e) => {
const submitter = e.submitter;
if (!submitter) return;
const sel = selectedCount();
const isDeleteAll = submitter.name === 'delete_all';
const msg = isDeleteAll ? 'Usunąć WSZYSTKIE bany?' : `Usunąć ${sel} wybrane bany?`;
if (!confirm(msg)) e.preventDefault();
});
// --- ADD BAN: walidacja klientowa ---
const addForm = $('#add-ban-form');
addForm?.addEventListener('submit', (e) => {
if (!addForm.checkValidity()) {
e.preventDefault();
addForm.classList.add('was-validated');
}
});
// --- DETAILS MODAL ---
function formatBanDetails(data) {
let html = '<div class="table-responsive"><table class="table table-bordered table-dark">';
html += '<thead><tr><th>Klucz</th><th>Wartość</th></tr></thead><tbody>';
for (let key in data) {
let value = data[key];
if (key === 'attack_details' || key === 'geo') {
try {
const parsed = typeof value === 'string' ? JSON.parse(value) : value;
let nested = '<table class="table table-sm table-bordered table-dark mb-0">';
for (let subKey in parsed) nested += `<tr><td>${subKey}</td><td>${parsed[subKey]}</td></tr>`;
nested += '</table>';
value = nested;
} catch (_) { }
}
html += `<tr><td>${key}</td><td>${value}</td></tr>`;
}
html += '</tbody></table></div>';
return html;
}
// Otwórz modal po kliknięciu IP
document.addEventListener('click', (e) => {
const btn = e.target.closest('.ban-ip');
if (!btn) return;
const ip = btn.dataset.ip;
const modalEl = document.getElementById('banModal');
const bodyEl = document.getElementById('banModalBody');
const titleEl = document.getElementById('banModalLabel');
if (titleEl) titleEl.textContent = 'Szczegóły bana dla ' + ip;
if (bodyEl) {
bodyEl.innerHTML = '<div class="placeholder-glow"><span class="placeholder col-12"></span><span class="placeholder col-10"></span></div>';
}
fetch('/api/banned/' + encodeURIComponent(ip) + '?full_info=1')
.then(r => r.ok ? r.json() : Promise.reject(r.status))
.then(data => { bodyEl.innerHTML = formatBanDetails(data); })
.catch(() => { bodyEl.innerHTML = '<div class="text-danger">Nie udało się pobrać szczegółów.</div>'; })
.finally(() => {
const m = new bootstrap.Modal(modalEl);
m.show();
});
});
document.getElementById('delete-all-btn')?.addEventListener('click', async () => {
if (!confirm('Usunąć WSZYSTKIE bany?')) return;
try {
const res = await fetch('/api/banned/all', { method: 'DELETE' });
if (!res.ok) throw new Error('HTTP ' + res.status);
window.showToast?.({ text: 'Wszystkie bany usunięte', variant: 'success' });
location.reload();
} catch (e) {
window.showToast?.({ text: 'Nie udało się usunąć wszystkich banów', variant: 'danger' });
}
});
const url = new URL(location.href);
if (url.searchParams.get('created') === '1') {
window.showToast?.({ text: 'Ban został dodany.', variant: 'success' });
}
})();