restored pagination + logs webpage

This commit is contained in:
Yan Xell
2026-05-13 12:26:03 +02:00
parent 044e954bda
commit 35dfc4e55f
4 changed files with 108 additions and 24 deletions
+45 -6
View File
@@ -101,14 +101,53 @@ def _page_pec(db_path: str) -> str:
def _page_logs(db_path: str, run_id: int = None) -> str:
logs = q_logs.get_logs(db_path)
pagination = (
'<div class="log-pagination">'
'<div id="filter-process-names" class="pg-process-filter"></div>'
'</div>'
runs = q_logs.get_last_run_ids(db_path)
run_ids = [r['rpa_process_id'] for r in runs]
run_names = {r['rpa_process_id']: r.get('process_name', 'unknown') for r in runs}
run_dates = {r['rpa_process_id']: str(r.get('start_run', ''))[:10] for r in runs}
if not run_ids:
content = _tmpl('logs.html').substitute(
run_info='nessun log', pagination='', tbl_logs='<p class="empty">Nessun log trovato.</p>'
)
return _base('Log DB', content, 'nav_logs', db_path, _page_css('logs'), _page_js('logs'))
if run_id is None or run_id not in run_ids:
run_id = run_ids[0]
idx = run_ids.index(run_id)
prev_id = run_ids[idx + 1] if idx + 1 < len(run_ids) else None
next_id = run_ids[idx - 1] if idx > 0 else None
prev_btn = (f'<a class="pg-btn" href="/logs?run={prev_id}">&#8592; Precedente</a>'
if prev_id else '<span class="pg-btn disabled">&#8592; Precedente</span>')
next_btn = (f'<a class="pg-btn" href="/logs?run={next_id}">Successivo &#8594;</a>'
if next_id else '<span class="pg-btn disabled">Successivo &#8594;</span>')
options = ''.join(
f'<option value="{rid}"'
f' data-process-name="{_e(run_names.get(rid, ""))}"'
f' data-date="{_e(run_dates.get(rid, ""))}"'
f' style="color:#1d4ed8"'
f'{" selected" if rid == run_id else ""}>'
f'Run #{rid}{_e(run_names.get(rid, ""))}</option>'
for rid in run_ids
)
current_name = run_names.get(run_id, 'unknown')
pagination = (
f'<div class="log-pagination">'
f'{prev_btn}'
f'<select id="pg-run-select" class="pg-select" onchange="location.href=\'/logs?run=\'+this.value">{options}</select>'
f'{next_btn}'
f'<span class="pg-sep"></span>'
f'<div id="filter-process-names" class="pg-process-filter"></div>'
f'</div>'
)
run_info = f'<span class="proc-name-badge">{_e(current_name)}</span> Run #{run_id}'
logs = q_logs.get_logs_by_run(db_path, run_id)
content = _tmpl('logs.html').substitute(
run_info='',
run_info=run_info,
pagination=pagination,
tbl_logs=q_logs.render_table(logs),
)
+1 -1
View File
@@ -42,7 +42,7 @@ def render_table(rows: list) -> str:
return '<p class="empty">Nessun log trovato.</p>'
def _badge(tipo):
m = {'log': ('log', 'Log'), 'warning': ('warn', 'Warning'), 'error': ('err', 'Error')}
m = {'info': ('log', 'Info'), 'warning': ('warn', 'Warning'), 'error': ('err', 'Error')}
cls, lbl = m.get(tipo, ('', ''))
return badge(cls, lbl) if cls else e(tipo)
+60 -15
View File
@@ -2,6 +2,7 @@
var search = document.getElementById('log-search');
var typeChecks = document.querySelectorAll('.type-filter');
var pnContainer = document.getElementById('filter-process-names');
var pgSelect = document.getElementById('pg-run-select');
var dateFrom = document.getElementById('filter-date-from');
var dateTo = document.getElementById('filter-date-to');
var filterBtn = document.getElementById('filter-date-btn');
@@ -41,13 +42,15 @@
if (dateTo) dateTo.value = toIsoDate(today);
}
// ── Process-name checkboxes (built from table rows) ───────────────────────
// ── Process-name checkboxes (built from pagination select options) ─────────
var names = [];
document.querySelectorAll('tbody tr[data-process-name]').forEach(function(tr) {
var n = tr.getAttribute('data-process-name');
if (n && names.indexOf(n) === -1) names.push(n);
});
names.sort();
if (pgSelect) {
Array.from(pgSelect.options).forEach(function(opt) {
var n = opt.getAttribute('data-process-name');
if (n && names.indexOf(n) === -1) names.push(n);
});
names.sort();
}
function loadSaved() {
try { return JSON.parse(sessionStorage.getItem(STORAGE_KEY) || '{}'); } catch(e) { return {}; }
@@ -66,7 +69,7 @@
var chk = document.createElement('input');
chk.type = 'checkbox';
chk.checked = (saved[name] !== undefined) ? saved[name] : true;
chk.addEventListener('change', function(){ saveState(); applyFilter(); });
chk.addEventListener('change', function(){ console.log('[logs] process filter changed:', name, this.checked); saveState(); filterSelect(); });
pnChecks[name] = chk;
lbl.appendChild(chk);
lbl.appendChild(document.createTextNode(' ' + name));
@@ -74,7 +77,38 @@
});
}
// ── Table row filter (type + search + date + process_name) ────────────────
// Store all options once at init — source of truth for rebuilding the select
var allOptions = pgSelect
? Array.from(pgSelect.options).map(function(o) { return o; })
: [];
// ── Pagination select filter (process_name + date) ────────────────────────
function filterSelect() {
if (!pgSelect) return;
var from = dateActive && dateFrom ? dateFrom.value : '';
var to = dateActive && dateTo ? dateTo.value : '';
var currentVal = pgSelect.value;
while (pgSelect.options.length) pgSelect.remove(0);
var currentVisible = false;
allOptions.forEach(function(opt) {
var pn = opt.getAttribute('data-process-name') || '';
var date = opt.getAttribute('data-date') || '';
var pnOk = pnChecks[pn] ? pnChecks[pn].checked : true;
var dateOk = (!from || !date || (date >= from && date <= to));
if (pnOk && dateOk) {
pgSelect.appendChild(opt);
if (opt.value === currentVal) currentVisible = true;
}
});
if (!currentVisible && pgSelect.options.length > 0) {
location.href = '/logs?run=' + pgSelect.options[0].value;
}
}
// ── Table row filter (type + search + date) ───────────────────────────────
function applyFilter() {
var q = search ? search.value.toLowerCase() : '';
var from = dateActive && dateFrom ? dateFrom.value : '';
@@ -83,27 +117,38 @@
typeChecks.forEach(function(c){ if (c.checked) active[c.value] = 1; });
document.querySelectorAll('tbody tr').forEach(function(tr) {
var type = tr.dataset.type || '';
var pn = tr.getAttribute('data-process-name') || '';
var text = tr.textContent.toLowerCase();
var date = tr.getAttribute('data-date') || '';
var dateOk = (!from || date >= from) && (!to || date <= to);
var pnOk = pnChecks[pn] ? pnChecks[pn].checked : true;
tr.style.display = (active[type] && pnOk && text.indexOf(q) >= 0 && dateOk) ? '' : 'none';
tr.style.display = (active[type] && text.indexOf(q) >= 0 && dateOk) ? '' : 'none';
});
}
// ── Filter button — saves dates then applies filter ───────────────────────
// ── Filter button — saves dates then applies both filters ─────────────────
if (filterBtn) {
filterBtn.addEventListener('click', function() {
console.log('[logs] filtra clicked — from:', dateFrom && dateFrom.value, 'to:', dateTo && dateTo.value);
dateActive = true;
saveDates();
applyFilter();
filterSelect();
});
}
if (search) search.addEventListener('input', applyFilter);
typeChecks.forEach(function(c){ c.addEventListener('change', applyFilter); });
if (search) search.addEventListener('input', function() {
console.log('[logs] search input:', search.value);
applyFilter();
});
typeChecks.forEach(function(c){ c.addEventListener('change', function() {
console.log('[logs] type filter changed:', c.value, '->', c.checked);
applyFilter();
}); });
// Initial apply
if (pgSelect) pgSelect.addEventListener('change', function() {
console.log('[logs] run selected:', pgSelect.value);
});
// Initial apply (uses restored dates)
applyFilter();
filterSelect();
})();
+2 -2
View File
@@ -1,7 +1,7 @@
<section class="page-header">
<div class="page-filter-wrap">
<div class="page-filter-header">
<h2>Log DB</h2>
<h2>Log DB &mdash; ${run_info}</h2>
<button id="btn-toggle-filters" class="btn-toggle-filters" title="Mostra/nascondi filtri">
<svg id="filters-arrow" class="filters-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 6 15 12 9 18"/></svg>
<span class="filters-btn-text">Filtri</span>
@@ -17,7 +17,7 @@
<hr class="filter-hr" />
<div class="log-filters">
<input type="search" id="log-search" placeholder="Cerca in questo log&hellip;" />
<label><input type="checkbox" class="type-filter" value="log" checked> Log</label>
<label><input type="checkbox" class="type-filter" value="info" checked> Info</label>
<label><input type="checkbox" class="type-filter" value="warning" checked> Warning</label>
<label><input type="checkbox" class="type-filter" value="error" checked> Error</label>
</div>