234 lines
9.1 KiB
JavaScript
234 lines
9.1 KiB
JavaScript
/* ── Filter + grouped view ─────────────────────────────────────────────── */
|
|
(function(){
|
|
var today = new Date().toISOString().slice(0,10);
|
|
var sevenAgo = new Date(Date.now() - 7*86400000).toISOString().slice(0,10);
|
|
document.getElementById('f-da').value = sevenAgo;
|
|
document.getElementById('f-a').value = today;
|
|
|
|
var table = document.querySelector('.tw table');
|
|
var tbody = table.querySelector('tbody');
|
|
var thead = table.querySelector('thead');
|
|
|
|
var allFlatRows = Array.prototype.slice.call(tbody.querySelectorAll('tr'));
|
|
var flatTheadHTML = thead.innerHTML;
|
|
var groupedTheadHTML = '<tr>'
|
|
+ '<th>Tipo</th><th>ID Sessione</th><th>Corso</th>'
|
|
+ '<th>Azienda</th><th>Articolo</th><th>Prima Lezione</th><th>Modifiche</th><th>Rilevato</th><th></th>'
|
|
+ '</tr>';
|
|
|
|
var isGrouped = false;
|
|
|
|
function esc(s) {
|
|
return String(s == null ? '' : s)
|
|
.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
}
|
|
|
|
function badgeHtml(tipo) {
|
|
if (tipo === 'nuovo') return '<span class="badge ok">Nuovo</span>';
|
|
if (tipo === 'modificato') return '<span class="badge warn">Modificato</span>';
|
|
return esc(tipo);
|
|
}
|
|
|
|
function getRowData(row) {
|
|
var c = row.cells;
|
|
return {
|
|
tipo: row.getAttribute('data-tipo') || '',
|
|
sessione_id: c[1] ? c[1].textContent.trim() : '',
|
|
corso: c[2] ? c[2].textContent.trim() : '',
|
|
azienda: c[3] ? c[3].textContent.trim() : '',
|
|
articolo: c[4] ? c[4].textContent.trim() : '',
|
|
prima_lezione: c[5] ? c[5].textContent.trim() : '',
|
|
campo: c[6] ? c[6].textContent.trim() : '',
|
|
valore_prec: c[7] ? c[7].textContent.trim() : '',
|
|
valore_nuovo: c[8] ? c[8].textContent.trim() : '',
|
|
rilevato: c[9] ? c[9].textContent.trim() : '',
|
|
};
|
|
}
|
|
|
|
function toIso(rilevato) {
|
|
var m = rilevato.match(/(\d{2})\/(\d{2})\/(\d{4})/);
|
|
return m ? m[3] + '-' + m[2] + '-' + m[1] : '';
|
|
}
|
|
|
|
function getFilters() {
|
|
var tipi = [];
|
|
document.querySelectorAll('.tipo-chk:checked').forEach(function(c){ tipi.push(c.value); });
|
|
return {
|
|
da: document.getElementById('f-da').value,
|
|
a: document.getElementById('f-a').value,
|
|
sid: document.getElementById('f-sid').value.trim(),
|
|
tipi: tipi,
|
|
};
|
|
}
|
|
|
|
function passes(d, f) {
|
|
var pd = toIso(d.prima_lezione);
|
|
return (!f.da || pd >= f.da)
|
|
&& (!f.a || pd <= f.a)
|
|
&& (!f.sid || d.sessione_id === f.sid)
|
|
&& f.tipi.indexOf(d.tipo) !== -1;
|
|
}
|
|
|
|
function updateCount(n) {
|
|
document.getElementById('visible-count').textContent = n + ' righe';
|
|
}
|
|
|
|
function applyFlat(f) {
|
|
tbody.innerHTML = '';
|
|
allFlatRows.forEach(function(r) { tbody.appendChild(r); });
|
|
var n = 0;
|
|
allFlatRows.forEach(function(row) {
|
|
var ok = passes(getRowData(row), f);
|
|
row.style.display = ok ? '' : 'none';
|
|
if (ok) n++;
|
|
});
|
|
updateCount(n);
|
|
}
|
|
|
|
function applyGrouped(f) {
|
|
var visible = allFlatRows.filter(function(r) { return passes(getRowData(r), f); });
|
|
|
|
var groups = {}, order = [];
|
|
visible.forEach(function(row) {
|
|
var d = getRowData(row);
|
|
var key = d.sessione_id || '__?__';
|
|
if (!groups[key]) {
|
|
groups[key] = { base: d, tipi: [], changes: [], latestIso: '', latestRilevato: '' };
|
|
order.push(key);
|
|
}
|
|
var g = groups[key];
|
|
if (g.tipi.indexOf(d.tipo) === -1) g.tipi.push(d.tipo);
|
|
var iso = toIso(d.rilevato);
|
|
if (iso > g.latestIso) { g.latestIso = iso; g.latestRilevato = d.rilevato; }
|
|
if (d.campo && d.campo !== '-') {
|
|
g.changes.push({ campo: d.campo, prec: d.valore_prec, nuovo: d.valore_nuovo });
|
|
}
|
|
});
|
|
|
|
var html = '';
|
|
order.forEach(function(key) {
|
|
var g = groups[key];
|
|
var b = g.base;
|
|
var tipiHtml = g.tipi.map(badgeHtml).join(' ');
|
|
var modHtml = g.changes.length
|
|
? g.changes.map(function(c) {
|
|
return '<div class="mod-row">'
|
|
+ '<span class="mod-campo">' + esc(c.campo) + '</span>: '
|
|
+ '<span class="val-prec">' + esc(c.prec) + '</span>'
|
|
+ ' → '
|
|
+ '<span class="val-nuovo">' + esc(c.nuovo) + '</span>'
|
|
+ '</div>';
|
|
}).join('')
|
|
: '<span class="mod-none">\u2014</span>';
|
|
|
|
html += '<tr>'
|
|
+ '<td>' + tipiHtml + '</td>'
|
|
+ '<td>' + esc(b.sessione_id) + '</td>'
|
|
+ '<td>' + esc(b.corso) + '</td>'
|
|
+ '<td>' + esc(b.azienda) + '</td>'
|
|
+ '<td>' + esc(b.articolo) + '</td>'
|
|
+ '<td class="col-prima-lez">' + esc(b.prima_lezione) + '</td>'
|
|
+ '<td class="mod-cell">' + modHtml + '</td>'
|
|
+ '<td>' + esc(g.latestRilevato) + '</td>'
|
|
+ '<td><button class="doc-btn" data-sid="' + esc(b.sessione_id) + '" data-corso="' + esc(b.corso) + '" title="Documenti caricati">📄</button></td>'
|
|
+ '</tr>';
|
|
});
|
|
|
|
tbody.innerHTML = html || '<tr><td colspan="9" class="empty">Nessun dato</td></tr>';
|
|
updateCount(order.length);
|
|
}
|
|
|
|
function applyFilters() {
|
|
var f = getFilters();
|
|
if (isGrouped) applyGrouped(f); else applyFlat(f);
|
|
}
|
|
|
|
document.getElementById('btn-toggle-view').addEventListener('click', function() {
|
|
isGrouped = !isGrouped;
|
|
this.textContent = isGrouped ? '\u229e Tabella' : '\u2a27 Raggruppa corso';
|
|
this.classList.toggle('active', isGrouped);
|
|
thead.innerHTML = isGrouped ? groupedTheadHTML : flatTheadHTML;
|
|
applyFilters();
|
|
});
|
|
|
|
document.getElementById('btn-filter').addEventListener('click', applyFilters);
|
|
document.getElementById('btn-reset').addEventListener('click', function(){
|
|
document.getElementById('f-da').value = sevenAgo;
|
|
document.getElementById('f-a').value = today;
|
|
document.getElementById('f-sid').value = '';
|
|
document.querySelectorAll('.tipo-chk').forEach(function(c){ c.checked = true; });
|
|
applyFilters();
|
|
});
|
|
document.querySelectorAll('.tipo-chk').forEach(function(c){
|
|
c.addEventListener('change', applyFilters);
|
|
});
|
|
|
|
applyFilters();
|
|
})();
|
|
|
|
/* ── Docs modal ────────────────────────────────────────────────────────── */
|
|
(function(){
|
|
var modal = document.getElementById('docs-modal');
|
|
var modalBody = document.getElementById('docs-modal-body');
|
|
var modalSid = document.getElementById('docs-modal-sid');
|
|
var modalCorso = document.getElementById('docs-modal-corso');
|
|
|
|
function esc(s) {
|
|
return String(s == null ? '' : s)
|
|
.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
}
|
|
|
|
function openDocsModal(sid, corso) {
|
|
modalSid.textContent = sid;
|
|
modalCorso.textContent = corso || '';
|
|
modalBody.innerHTML = '<p class="docs-loading">Caricamento\u2026</p>';
|
|
modal.style.display = 'flex';
|
|
|
|
fetch('/api/docs?sessione_id=' + encodeURIComponent(sid))
|
|
.then(function(r){ return r.json(); })
|
|
.then(function(docs) {
|
|
if (!docs || docs.length === 0) {
|
|
modalBody.innerHTML = '<p class="docs-empty">Nessun documento caricato.</p>';
|
|
return;
|
|
}
|
|
var rows = docs.map(function(d) {
|
|
var st = d.doc_status || '';
|
|
var badge = '<span class="doc-status-badge doc-status-' + esc(st) + '">' + esc(st) + '</span>';
|
|
var fn = d.filename || '';
|
|
var fnHtml = esc(fn);
|
|
if (fn.charAt(0) === '[' && fn.charAt(fn.length - 1) === ']')
|
|
fnHtml = '<span style="color:#f97316;">' + fnHtml + '</span>';
|
|
var name = d.sp_web_url
|
|
? '<a href="' + d.sp_web_url + '" target="_blank" rel="noopener">' + fnHtml + '</a>'
|
|
: fnHtml;
|
|
var ts = d.uploaded_at ? d.uploaded_at.slice(0,16).replace('T',' ') : '-';
|
|
return '<tr>'
|
|
+ '<td class="doc-subfolder">' + esc(d.subfolder) + '</td>'
|
|
+ '<td class="doc-filename">' + name + '</td>'
|
|
+ '<td>' + badge + '</td>'
|
|
+ '<td class="doc-date">' + ts + '</td>'
|
|
+ '</tr>';
|
|
}).join('');
|
|
modalBody.innerHTML =
|
|
'<table class="docs-table">'
|
|
+ '<thead><tr><th>Cartella</th><th>File</th><th>Doc</th><th>Caricato SP</th></tr></thead>'
|
|
+ '<tbody>' + rows + '</tbody>'
|
|
+ '</table>';
|
|
})
|
|
.catch(function(err) {
|
|
modalBody.innerHTML = '<p class="docs-error">Errore: ' + esc(String(err)) + '</p>';
|
|
});
|
|
}
|
|
|
|
function closeDocsModal() { modal.style.display = 'none'; }
|
|
|
|
document.getElementById('docs-modal-close').addEventListener('click', closeDocsModal);
|
|
modal.addEventListener('click', function(ev) { if (ev.target === modal) closeDocsModal(); });
|
|
document.addEventListener('keydown', function(ev) { if (ev.key === 'Escape') closeDocsModal(); });
|
|
|
|
document.querySelector('.tw').addEventListener('click', function(ev) {
|
|
var btn = ev.target.closest('.doc-btn');
|
|
if (btn) openDocsModal(btn.getAttribute('data-sid'), btn.getAttribute('data-corso'));
|
|
});
|
|
})();
|