feat(snapshots): UI simplifiee - dropdown Intervenant unique (defaut user connecte)
Avant: 3 champs (Auteur input + 2 checkboxes 'Mes snapshots' / 'Format PatchCenter') -> trop. Apres: - Un seul dropdown 'Intervenant' avec liste dynamique des auteurs detectes dans les snapshots PatchCenter charges - Defaut = user connecte (intervenant_default depuis JWT 'sub') - Ajoute '(toi)' a cote de l'option qui matche le user connecte - Si le user connecte n'a pas encore de snapshot, son option apparait quand meme en tete avec le tag '(toi, aucun snap)' - Le filtre 'PatchCenter uniquement' est implicite (toujours actif) -> les snapshots SLPM ou manuels n'apparaissent plus du tout - Les autres users peuvent etre selectionnes pour afficher/supprimer leurs snapshots (pas de restriction par compte au-dela de l'authentification) - updateFilterSummary affiche le filtre actif clairement
This commit is contained in:
parent
c918edb093
commit
46b80474c2
@ -56,10 +56,11 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-span-3">
|
<div class="col-span-3">
|
||||||
<label class="text-xs text-gray-500">Auteur (préfixe nom)</label>
|
<label class="text-xs text-gray-500">Intervenant</label>
|
||||||
<input type="text" id="f-author" value="{{ intervenant_default }}" placeholder="ex: khalid" class="w-full">
|
<select id="f-intervenant" class="w-full">
|
||||||
<label class="text-xs text-gray-400 mt-1"><input type="checkbox" id="f-only-mine" checked> Mes snapshots uniquement (login = auteur)</label>
|
<option value="{{ intervenant_default }}" selected>{{ intervenant_default or '(toi)' }}</option>
|
||||||
<label class="text-xs text-gray-400 mt-1"><input type="checkbox" id="f-only-pc" checked> Snapshots PatchCenter uniquement (exclut SLPM .exe et manuels)</label>
|
</select>
|
||||||
|
<p class="text-xs text-gray-500 mt-1">Liste alimentée après chargement (auteurs détectés dans les snapshots PatchCenter).</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-span-3">
|
<div class="col-span-3">
|
||||||
<label class="text-xs text-gray-500">Âge minimum (jours)</label>
|
<label class="text-xs text-gray-500">Âge minimum (jours)</label>
|
||||||
@ -111,10 +112,9 @@
|
|||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
const fVc = document.getElementById('f-vcenter');
|
const fVc = document.getElementById('f-vcenter');
|
||||||
const fAuthor = document.getElementById('f-author');
|
const fIntervenant = document.getElementById('f-intervenant');
|
||||||
const fOnlyMine = document.getElementById('f-only-mine');
|
|
||||||
const fOnlyPc = document.getElementById('f-only-pc');
|
|
||||||
const fMinAge = document.getElementById('f-min-age');
|
const fMinAge = document.getElementById('f-min-age');
|
||||||
|
const intervenantDefault = "{{ intervenant_default|e }}";
|
||||||
const btnRefresh = document.getElementById('btn-refresh');
|
const btnRefresh = document.getElementById('btn-refresh');
|
||||||
const btnDelete = document.getElementById('btn-delete');
|
const btnDelete = document.getElementById('btn-delete');
|
||||||
const status = document.getElementById('status');
|
const status = document.getElementById('status');
|
||||||
@ -137,26 +137,43 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyFilters() {
|
function applyFilters() {
|
||||||
const author = (fAuthor.value || '').trim().toLowerCase();
|
// Toujours: snapshots PatchCenter uniquement (origin === 'patchcenter')
|
||||||
const onlyMine = fOnlyMine.checked;
|
// + intervenant choisi dans le dropdown
|
||||||
const pcOnly = fOnlyPc.checked;
|
// + age minimum
|
||||||
|
const intervenant = (fIntervenant.value || '').trim().toLowerCase();
|
||||||
const minAge = parseFloat(fMinAge.value) || 0;
|
const minAge = parseFloat(fMinAge.value) || 0;
|
||||||
return allSnaps.filter(s => {
|
return allSnaps.filter(s => {
|
||||||
// Snapshots PatchCenter uniquement (origin === 'patchcenter')
|
if (s.origin !== 'patchcenter') return false;
|
||||||
if (pcOnly && s.origin !== 'patchcenter') return false;
|
if (intervenant && (!s.author || s.author.toLowerCase() !== intervenant)) return false;
|
||||||
// Mes snapshots uniquement
|
|
||||||
if (onlyMine) {
|
|
||||||
if (!author) {
|
|
||||||
if (!s.author) return false;
|
|
||||||
} else if (!s.author || s.author.toLowerCase() !== author) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (minAge > 0 && (s.age_days === null || s.age_days < minAge)) return false;
|
if (minAge > 0 && (s.age_days === null || s.age_days < minAge)) return false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rebuildIntervenantDropdown() {
|
||||||
|
// Liste des auteurs distincts des snapshots PatchCenter trouvés
|
||||||
|
const authors = Array.from(new Set(
|
||||||
|
allSnaps.filter(s => s.origin === 'patchcenter' && s.author).map(s => s.author)
|
||||||
|
)).sort((a,b) => a.localeCompare(b, 'fr', {sensitivity:'base'}));
|
||||||
|
// Conserve la sélection courante (ou défaut user connecté)
|
||||||
|
const current = fIntervenant.value || intervenantDefault;
|
||||||
|
let opts = '';
|
||||||
|
// Si user connecté pas dans la liste (= aucun snap encore), on l'ajoute en tête
|
||||||
|
const haveCurrent = authors.some(a => a.toLowerCase() === current.toLowerCase());
|
||||||
|
if (intervenantDefault && !authors.some(a => a.toLowerCase() === intervenantDefault.toLowerCase())) {
|
||||||
|
opts += `<option value="${intervenantDefault}">${intervenantDefault} (toi, aucun snap)</option>`;
|
||||||
|
}
|
||||||
|
for (const a of authors) {
|
||||||
|
const isYou = a.toLowerCase() === intervenantDefault.toLowerCase();
|
||||||
|
const sel = (a.toLowerCase() === current.toLowerCase()) ? ' selected' : '';
|
||||||
|
opts += `<option value="${a}"${sel}>${a}${isYou ? ' (toi)' : ''}</option>`;
|
||||||
|
}
|
||||||
|
if (!opts) opts = '<option value="">(aucun snap PatchCenter trouvé)</option>';
|
||||||
|
fIntervenant.innerHTML = opts;
|
||||||
|
// Re-sélectionne current ou défaut si présent
|
||||||
|
if (haveCurrent) fIntervenant.value = current;
|
||||||
|
}
|
||||||
|
|
||||||
function fmtDateFR(iso) {
|
function fmtDateFR(iso) {
|
||||||
// ISO 8601 -> 'jj/mm/aaaa HH:MM' (heure locale du navigateur)
|
// ISO 8601 -> 'jj/mm/aaaa HH:MM' (heure locale du navigateur)
|
||||||
if (!iso) return '–';
|
if (!iso) return '–';
|
||||||
@ -169,15 +186,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateFilterSummary(visibleCount) {
|
function updateFilterSummary(visibleCount) {
|
||||||
// Affiche le nombre cachés vs total pour debug
|
// Affiche combien sont cachés et la cible
|
||||||
const total = allSnaps.length;
|
|
||||||
const hidden = total - visibleCount;
|
|
||||||
const baseStatus = status.dataset.baseMsg || '';
|
const baseStatus = status.dataset.baseMsg || '';
|
||||||
if (hidden > 0) {
|
const intervenant = (fIntervenant.value || '').trim();
|
||||||
status.innerHTML = baseStatus + ` <span class="text-cyber-yellow">⚠ ${hidden} snapshot(s) caché(s) par les filtres</span> (décoche "Mes snapshots" ou mets âge=0 pour tout voir).`;
|
const minAge = parseFloat(fMinAge.value) || 0;
|
||||||
} else {
|
const filterDesc = `intervenant=${intervenant || 'tous'}` + (minAge > 0 ? `, âge≥${minAge}j` : '');
|
||||||
status.innerHTML = baseStatus;
|
status.innerHTML = baseStatus + ` <span class="text-cyber-accent">→ ${visibleCount} affiché(s) (filtre : ${escapeHTML(filterDesc)})</span>`;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
@ -277,10 +291,12 @@
|
|||||||
}
|
}
|
||||||
allSnaps = j.snapshots || [];
|
allSnaps = j.snapshots || [];
|
||||||
const errs = (j.errors || []).map(e => `${e.vcenter}: ${e.msg}`).join(' · ');
|
const errs = (j.errors || []).map(e => `${e.vcenter}: ${e.msg}`).join(' · ');
|
||||||
const baseMsg = `${j.snap_count} snapshot(s) trouvé(s) sur ${j.vcenter_count} vCenter(s).` +
|
const pcCount = allSnaps.filter(s => s.origin === 'patchcenter').length;
|
||||||
|
const baseMsg = `${j.snap_count} snapshot(s) trouvé(s) sur ${j.vcenter_count} vCenter(s) (dont ${pcCount} PatchCenter).` +
|
||||||
(errs ? ` <span class="text-cyber-yellow">⚠ ${escapeHTML(errs)}</span>` : '');
|
(errs ? ` <span class="text-cyber-yellow">⚠ ${escapeHTML(errs)}</span>` : '');
|
||||||
status.dataset.baseMsg = baseMsg;
|
status.dataset.baseMsg = baseMsg;
|
||||||
status.innerHTML = baseMsg;
|
status.innerHTML = baseMsg;
|
||||||
|
rebuildIntervenantDropdown();
|
||||||
render();
|
render();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
status.innerHTML = '<span class="text-cyber-red">Erreur réseau : ' + escapeHTML(String(e)) + '</span>';
|
status.innerHTML = '<span class="text-cyber-red">Erreur réseau : ' + escapeHTML(String(e)) + '</span>';
|
||||||
@ -289,8 +305,8 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
[fAuthor, fMinAge, fOnlyMine, fOnlyPc].forEach(el => el.addEventListener('input', render));
|
[fIntervenant, fMinAge].forEach(el => el.addEventListener('input', render));
|
||||||
[fOnlyMine, fOnlyPc].forEach(el => el.addEventListener('change', render));
|
fIntervenant.addEventListener('change', render);
|
||||||
|
|
||||||
selAll.addEventListener('change', () => {
|
selAll.addEventListener('change', () => {
|
||||||
tbody.querySelectorAll('.row-cb').forEach(cb => {
|
tbody.querySelectorAll('.row-cb').forEach(cb => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user