feat(patching/import): filtres client-side asset/intervenant/env sur tableau semaine
This commit is contained in:
parent
557015325b
commit
13a5625710
@ -106,9 +106,19 @@
|
|||||||
|
|
||||||
{# Tableau dynamique #}
|
{# Tableau dynamique #}
|
||||||
<div id="sheet-table-wrap" class="overflow-x-auto" style="display:none;">
|
<div id="sheet-table-wrap" class="overflow-x-auto" style="display:none;">
|
||||||
|
{# Filtres client-side #}
|
||||||
|
<div class="flex gap-2 items-center mb-2 flex-wrap">
|
||||||
|
<input type="text" id="filter-asset" placeholder="Filtre asset…" class="text-xs px-2 py-1" style="width:160px">
|
||||||
|
<input type="text" id="filter-intervenant" placeholder="Filtre intervenant…" class="text-xs px-2 py-1" style="width:160px">
|
||||||
|
<select id="filter-env" class="text-xs px-2 py-1">
|
||||||
|
<option value="">— Tous environnements —</option>
|
||||||
|
</select>
|
||||||
|
<button id="filter-reset" class="text-xs text-gray-500 hover:text-cyber-accent">Reset</button>
|
||||||
|
<span class="text-xs text-gray-400" id="filter-count"></span>
|
||||||
|
</div>
|
||||||
<div class="flex gap-2 items-center mb-2 flex-wrap">
|
<div class="flex gap-2 items-center mb-2 flex-wrap">
|
||||||
<label class="text-xs">
|
<label class="text-xs">
|
||||||
<input type="checkbox" id="select-all" class="mr-1"> Tout sélectionner
|
<input type="checkbox" id="select-all" class="mr-1"> Tout sélectionner (visibles)
|
||||||
</label>
|
</label>
|
||||||
<span class="text-xs text-gray-400" id="selection-count">0 sélectionné(s)</span>
|
<span class="text-xs text-gray-400" id="selection-count">0 sélectionné(s)</span>
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
@ -156,6 +166,13 @@
|
|||||||
const selCount = document.getElementById('selection-count');
|
const selCount = document.getElementById('selection-count');
|
||||||
const btnPre = document.getElementById('btn-prepatch');
|
const btnPre = document.getElementById('btn-prepatch');
|
||||||
const btnPatch = document.getElementById('btn-patch');
|
const btnPatch = document.getElementById('btn-patch');
|
||||||
|
const fAsset = document.getElementById('filter-asset');
|
||||||
|
const fInter = document.getElementById('filter-intervenant');
|
||||||
|
const fEnv = document.getElementById('filter-env');
|
||||||
|
const fReset = document.getElementById('filter-reset');
|
||||||
|
const fCount = document.getElementById('filter-count');
|
||||||
|
|
||||||
|
let currentRows = [];
|
||||||
|
|
||||||
function escapeHTML(s){
|
function escapeHTML(s){
|
||||||
if (s === null || s === undefined) return '';
|
if (s === null || s === undefined) return '';
|
||||||
@ -163,18 +180,44 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function refreshSelection(){
|
function refreshSelection(){
|
||||||
|
const visible = tbody.querySelectorAll('tr:not(.row-hidden)');
|
||||||
|
const visibleCb = tbody.querySelectorAll('tr:not(.row-hidden) input.row-cb');
|
||||||
const checked = tbody.querySelectorAll('input.row-cb:checked').length;
|
const checked = tbody.querySelectorAll('input.row-cb:checked').length;
|
||||||
const total = tbody.querySelectorAll('input.row-cb').length;
|
selCount.textContent = checked + ' sélectionné(s) · ' + visible.length + ' visible(s)';
|
||||||
selCount.textContent = checked + ' / ' + total + ' sélectionné(s)';
|
const allVisibleChecked = visibleCb.length > 0 && Array.from(visibleCb).every(cb => cb.checked);
|
||||||
selAll.checked = (checked > 0 && checked === total);
|
selAll.checked = allVisibleChecked;
|
||||||
selAllHead.checked = selAll.checked;
|
selAllHead.checked = allVisibleChecked;
|
||||||
// Pré-patching et patch désactivés tant que les étapes 2/3 ne sont pas faites
|
|
||||||
// mais on prépare la condition pour la suite :
|
|
||||||
const hasSel = checked > 0;
|
const hasSel = checked > 0;
|
||||||
btnPre.disabled = !hasSel;
|
btnPre.disabled = !hasSel;
|
||||||
btnPatch.disabled = !hasSel;
|
btnPatch.disabled = !hasSel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyFilters(){
|
||||||
|
const fa = (fAsset.value || '').toLowerCase().trim();
|
||||||
|
const fi = (fInter.value || '').toLowerCase().trim();
|
||||||
|
const fe = (fEnv.value || '').trim();
|
||||||
|
let visibleCount = 0;
|
||||||
|
tbody.querySelectorAll('tr').forEach(tr => {
|
||||||
|
const a = (tr.dataset.asset || '').toLowerCase();
|
||||||
|
const i = (tr.dataset.intervenant || '').toLowerCase();
|
||||||
|
const e = tr.dataset.env || '';
|
||||||
|
const ok = (!fa || a.includes(fa))
|
||||||
|
&& (!fi || i.includes(fi))
|
||||||
|
&& (!fe || e === fe);
|
||||||
|
if (ok) { tr.classList.remove('row-hidden'); tr.style.display=''; visibleCount++; }
|
||||||
|
else { tr.classList.add('row-hidden'); tr.style.display='none'; }
|
||||||
|
});
|
||||||
|
fCount.textContent = visibleCount + ' / ' + currentRows.length + ' affichées';
|
||||||
|
refreshSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebuildEnvOptions(rows){
|
||||||
|
const envs = Array.from(new Set(rows.map(r => r.environnement).filter(x => x))).sort();
|
||||||
|
const cur = fEnv.value;
|
||||||
|
fEnv.innerHTML = '<option value="">— Tous environnements —</option>'
|
||||||
|
+ envs.map(e => '<option value="' + escapeHTML(e) + '"' + (cur === e ? ' selected' : '') + '>' + escapeHTML(e) + '</option>').join('');
|
||||||
|
}
|
||||||
|
|
||||||
async function loadSheet(name){
|
async function loadSheet(name){
|
||||||
if (!name) { wrap.style.display='none'; empty.style.display='none'; return; }
|
if (!name) { wrap.style.display='none'; empty.style.display='none'; return; }
|
||||||
summary.textContent = 'Chargement…';
|
summary.textContent = 'Chargement…';
|
||||||
@ -187,12 +230,17 @@
|
|||||||
}
|
}
|
||||||
empty.style.display='none'; wrap.style.display='';
|
empty.style.display='none'; wrap.style.display='';
|
||||||
summary.textContent = j.count + ' lignes';
|
summary.textContent = j.count + ' lignes';
|
||||||
|
currentRows = j.rows;
|
||||||
|
rebuildEnvOptions(currentRows);
|
||||||
tbody.innerHTML = j.rows.map(r => {
|
tbody.innerHTML = j.rows.map(r => {
|
||||||
const linkSrv = r.server_id
|
const linkSrv = r.server_id
|
||||||
? '<a href="/qualys/search?field=hostname&q=' + encodeURIComponent(r.resolved_hostname || r.asset_name) + '" class="text-cyber-blue hover:underline">' + escapeHTML(r.resolved_hostname || r.asset_name) + '</a>'
|
? '<a href="/qualys/search?field=hostname&q=' + encodeURIComponent(r.resolved_hostname || r.asset_name) + '" class="text-cyber-blue hover:underline">' + escapeHTML(r.resolved_hostname || r.asset_name) + '</a>'
|
||||||
: '<span class="text-cyber-yellow" title="Pas matché en base PatchCenter">' + escapeHTML(r.asset_name || '') + ' ⚠</span>';
|
: '<span class="text-cyber-yellow" title="Pas matché en base PatchCenter">' + escapeHTML(r.asset_name || '') + ' ⚠</span>';
|
||||||
const pb = r.pb_espace_disque === true ? '<span class="text-cyber-red">⚠ Oui</span>' : (r.pb_espace_disque === false ? 'Non' : '');
|
const pb = r.pb_espace_disque === true ? '<span class="text-cyber-red">⚠ Oui</span>' : (r.pb_espace_disque === false ? 'Non' : '');
|
||||||
return '<tr class="border-b border-cyber-border/20 hover:bg-cyber-border/10">'
|
return '<tr class="border-b border-cyber-border/20 hover:bg-cyber-border/10"'
|
||||||
|
+ ' data-asset="' + escapeHTML(r.asset_name||'') + '"'
|
||||||
|
+ ' data-intervenant="' + escapeHTML(r.intervenant||'') + '"'
|
||||||
|
+ ' data-env="' + escapeHTML(r.environnement||'') + '">'
|
||||||
+ '<td class="p-1"><input type="checkbox" class="row-cb" data-id="' + r.id + '" data-asset="' + escapeHTML(r.asset_name||'') + '" data-server-id="' + (r.server_id||'') + '"></td>'
|
+ '<td class="p-1"><input type="checkbox" class="row-cb" data-id="' + r.id + '" data-asset="' + escapeHTML(r.asset_name||'') + '" data-server-id="' + (r.server_id||'') + '"></td>'
|
||||||
+ '<td class="p-1 font-mono">' + escapeHTML(r.asset_name||'') + '</td>'
|
+ '<td class="p-1 font-mono">' + escapeHTML(r.asset_name||'') + '</td>'
|
||||||
+ '<td class="p-1">' + escapeHTML(r.environnement||'') + '</td>'
|
+ '<td class="p-1">' + escapeHTML(r.environnement||'') + '</td>'
|
||||||
@ -209,17 +257,24 @@
|
|||||||
+ '</tr>';
|
+ '</tr>';
|
||||||
}).join('');
|
}).join('');
|
||||||
tbody.querySelectorAll('input.row-cb').forEach(cb => cb.addEventListener('change', refreshSelection));
|
tbody.querySelectorAll('input.row-cb').forEach(cb => cb.addEventListener('change', refreshSelection));
|
||||||
refreshSelection();
|
applyFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
sel.addEventListener('change', () => loadSheet(sel.value));
|
sel.addEventListener('change', () => loadSheet(sel.value));
|
||||||
function toggleAll(state){
|
function toggleAll(state){
|
||||||
tbody.querySelectorAll('input.row-cb').forEach(cb => cb.checked = state);
|
tbody.querySelectorAll('tr:not(.row-hidden) input.row-cb').forEach(cb => cb.checked = state);
|
||||||
refreshSelection();
|
refreshSelection();
|
||||||
}
|
}
|
||||||
selAll.addEventListener('change', () => toggleAll(selAll.checked));
|
selAll.addEventListener('change', () => toggleAll(selAll.checked));
|
||||||
selAllHead.addEventListener('change', () => toggleAll(selAllHead.checked));
|
selAllHead.addEventListener('change', () => toggleAll(selAllHead.checked));
|
||||||
|
|
||||||
|
[fAsset, fInter, fEnv].forEach(el => el.addEventListener('input', applyFilters));
|
||||||
|
fEnv.addEventListener('change', applyFilters);
|
||||||
|
fReset.addEventListener('click', () => {
|
||||||
|
fAsset.value = ''; fInter.value = ''; fEnv.value = '';
|
||||||
|
applyFilters();
|
||||||
|
});
|
||||||
|
|
||||||
btnPre.addEventListener('click', () => {
|
btnPre.addEventListener('click', () => {
|
||||||
const ids = Array.from(tbody.querySelectorAll('input.row-cb:checked')).map(cb => cb.dataset.serverId).filter(x => x);
|
const ids = Array.from(tbody.querySelectorAll('input.row-cb:checked')).map(cb => cb.dataset.serverId).filter(x => x);
|
||||||
alert('Pré-patching à brancher (étape 2) — ' + ids.length + ' serveur(s) résolu(s) en base.');
|
alert('Pré-patching à brancher (étape 2) — ' + ids.length + ' serveur(s) résolu(s) en base.');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user