feat(patching/import): filtre intervenant en dropdown, retire filtre asset texte, ajoute tri asc/desc/none au clic sur entete Asset
This commit is contained in:
parent
488b5a980b
commit
8b6057aef2
@ -108,8 +108,9 @@
|
|||||||
<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 #}
|
{# Filtres client-side #}
|
||||||
<div class="flex gap-2 items-center mb-2 flex-wrap">
|
<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">
|
<select id="filter-intervenant" class="text-xs px-2 py-1">
|
||||||
<input type="text" id="filter-intervenant" placeholder="Filtre intervenant…" class="text-xs px-2 py-1" style="width:160px">
|
<option value="">— Tous intervenants —</option>
|
||||||
|
</select>
|
||||||
<select id="filter-env" class="text-xs px-2 py-1">
|
<select id="filter-env" class="text-xs px-2 py-1">
|
||||||
<option value="">— Tous environnements —</option>
|
<option value="">— Tous environnements —</option>
|
||||||
</select>
|
</select>
|
||||||
@ -133,7 +134,9 @@
|
|||||||
<thead class="text-cyber-accent border-b border-cyber-border">
|
<thead class="text-cyber-accent border-b border-cyber-border">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-left p-1 w-6"><input type="checkbox" id="select-all-head"></th>
|
<th class="text-left p-1 w-6"><input type="checkbox" id="select-all-head"></th>
|
||||||
<th class="text-left p-1">Asset</th>
|
<th class="text-left p-1 cursor-pointer select-none hover:text-cyber-accent" id="th-asset" title="Cliquer pour trier">
|
||||||
|
Asset <span id="th-asset-arrow" class="text-[10px] opacity-50">↕</span>
|
||||||
|
</th>
|
||||||
<th class="text-left p-1">Env</th>
|
<th class="text-left p-1">Env</th>
|
||||||
<th class="text-left p-1">Domaine</th>
|
<th class="text-left p-1">Domaine</th>
|
||||||
<th class="text-left p-1">OS</th>
|
<th class="text-left p-1">OS</th>
|
||||||
@ -172,13 +175,15 @@
|
|||||||
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 fInter = document.getElementById('filter-intervenant');
|
||||||
const fEnv = document.getElementById('filter-env');
|
const fEnv = document.getElementById('filter-env');
|
||||||
const fReset = document.getElementById('filter-reset');
|
const fReset = document.getElementById('filter-reset');
|
||||||
const fCount = document.getElementById('filter-count');
|
const fCount = document.getElementById('filter-count');
|
||||||
|
const thAsset = document.getElementById('th-asset');
|
||||||
|
const thAssetArrow = document.getElementById('th-asset-arrow');
|
||||||
|
|
||||||
let currentRows = [];
|
let currentRows = [];
|
||||||
|
let sortAsset = 0; // 0 = ordre fichier, 1 = asc, -1 = desc
|
||||||
|
|
||||||
function escapeHTML(s){
|
function escapeHTML(s){
|
||||||
if (s === null || s === undefined) return '';
|
if (s === null || s === undefined) return '';
|
||||||
@ -199,17 +204,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyFilters(){
|
function applyFilters(){
|
||||||
const fa = (fAsset.value || '').toLowerCase().trim();
|
const fi = (fInter.value || '').trim();
|
||||||
const fi = (fInter.value || '').toLowerCase().trim();
|
|
||||||
const fe = (fEnv.value || '').trim();
|
const fe = (fEnv.value || '').trim();
|
||||||
let visibleCount = 0;
|
let visibleCount = 0;
|
||||||
tbody.querySelectorAll('tr').forEach(tr => {
|
tbody.querySelectorAll('tr').forEach(tr => {
|
||||||
const a = (tr.dataset.asset || '').toLowerCase();
|
const i = tr.dataset.intervenant || '';
|
||||||
const i = (tr.dataset.intervenant || '').toLowerCase();
|
|
||||||
const e = tr.dataset.env || '';
|
const e = tr.dataset.env || '';
|
||||||
const ok = (!fa || a.includes(fa))
|
const ok = (!fi || i === fi) && (!fe || e === fe);
|
||||||
&& (!fi || i.includes(fi))
|
|
||||||
&& (!fe || e === fe);
|
|
||||||
if (ok) { tr.classList.remove('row-hidden'); tr.style.display=''; visibleCount++; }
|
if (ok) { tr.classList.remove('row-hidden'); tr.style.display=''; visibleCount++; }
|
||||||
else { tr.classList.add('row-hidden'); tr.style.display='none'; }
|
else { tr.classList.add('row-hidden'); tr.style.display='none'; }
|
||||||
});
|
});
|
||||||
@ -217,11 +218,16 @@
|
|||||||
refreshSelection();
|
refreshSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
function rebuildEnvOptions(rows){
|
function rebuildSelectOptions(sel, values, placeholder){
|
||||||
const envs = Array.from(new Set(rows.map(r => r.environnement).filter(x => x))).sort();
|
const cur = sel.value;
|
||||||
const cur = fEnv.value;
|
const opts = Array.from(new Set(values.filter(x => x))).sort((a,b) => a.localeCompare(b, 'fr', {sensitivity:'base'}));
|
||||||
fEnv.innerHTML = '<option value="">— Tous environnements —</option>'
|
sel.innerHTML = '<option value="">' + placeholder + '</option>'
|
||||||
+ envs.map(e => '<option value="' + escapeHTML(e) + '"' + (cur === e ? ' selected' : '') + '>' + escapeHTML(e) + '</option>').join('');
|
+ opts.map(v => '<option value="' + escapeHTML(v) + '"' + (cur === v ? ' selected' : '') + '>' + escapeHTML(v) + '</option>').join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSortArrow(){
|
||||||
|
thAssetArrow.textContent = sortAsset === 1 ? '▲' : (sortAsset === -1 ? '▼' : '↕');
|
||||||
|
thAssetArrow.classList.toggle('opacity-50', sortAsset === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadSheet(name){
|
async function loadSheet(name){
|
||||||
@ -237,8 +243,25 @@
|
|||||||
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;
|
currentRows = j.rows;
|
||||||
rebuildEnvOptions(currentRows);
|
rebuildSelectOptions(fInter, currentRows.map(r => r.intervenant), '— Tous intervenants —');
|
||||||
tbody.innerHTML = j.rows.map(r => {
|
rebuildSelectOptions(fEnv, currentRows.map(r => r.environnement), '— Tous environnements —');
|
||||||
|
sortAsset = 0;
|
||||||
|
updateSortArrow();
|
||||||
|
renderTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTable(){
|
||||||
|
let rows = currentRows.slice();
|
||||||
|
if (sortAsset !== 0) {
|
||||||
|
rows.sort((a, b) => {
|
||||||
|
const av = (a.asset_name || '').toLowerCase();
|
||||||
|
const bv = (b.asset_name || '').toLowerCase();
|
||||||
|
if (av < bv) return -sortAsset;
|
||||||
|
if (av > bv) return sortAsset;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tbody.innerHTML = 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>';
|
||||||
@ -280,11 +303,17 @@
|
|||||||
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));
|
[fInter, fEnv].forEach(el => el.addEventListener('change', applyFilters));
|
||||||
fEnv.addEventListener('change', applyFilters);
|
|
||||||
fReset.addEventListener('click', () => {
|
fReset.addEventListener('click', () => {
|
||||||
fAsset.value = ''; fInter.value = ''; fEnv.value = '';
|
fInter.value = ''; fEnv.value = '';
|
||||||
applyFilters();
|
sortAsset = 0;
|
||||||
|
updateSortArrow();
|
||||||
|
renderTable();
|
||||||
|
});
|
||||||
|
thAsset.addEventListener('click', () => {
|
||||||
|
sortAsset = sortAsset === 1 ? -1 : (sortAsset === -1 ? 0 : 1);
|
||||||
|
updateSortArrow();
|
||||||
|
renderTable();
|
||||||
});
|
});
|
||||||
|
|
||||||
btnPre.addEventListener('click', () => {
|
btnPre.addEventListener('click', () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user