feat(patching/import): boutons d'action toujours cliquables + alerte si aucune selection

Avant: les boutons (+ Ajouter, Reporter, Annuler, Pré-patching) etaient grises (disabled)
quand aucune row n'etait selectionnee, et le clic ne provoquait rien -> confusion.

Apres:
- Boutons toujours cliquables (HTML disabled retire)
- Atténuation visuelle (opacity-50) quand selection vide pour feedback
- Au clic sans selection: alerte 'Veuillez selectionner au moins un serveur.'
- Btn Pre-patching: si selection mais aucun eligible, alerte indiquant
  d'utiliser '+ Ajouter au patching' d'abord
This commit is contained in:
Pierre & Lumière 2026-05-07 19:51:32 +02:00
parent e448d8885b
commit 040448696b

View File

@ -124,17 +124,17 @@
<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>
{% if can_import %} {% if can_import %}
<button id="btn-add-eligible" class="btn-sm bg-cyber-green/20 text-cyber-green px-3 py-1 text-xs" disabled title="Marque les lignes comme éligibles au patching"> <button id="btn-add-eligible" class="btn-sm bg-cyber-green/20 text-cyber-green px-3 py-1 text-xs" title="Marque les lignes sélectionnées comme éligibles au patching">
+ Ajouter au patching + Ajouter au patching
</button> </button>
<button id="btn-report" class="btn-sm bg-cyber-blue/20 text-cyber-blue px-3 py-1 text-xs" disabled title="Reporter à une autre semaine"> <button id="btn-report" class="btn-sm bg-cyber-blue/20 text-cyber-blue px-3 py-1 text-xs" title="Reporter les lignes sélectionnées à une autre semaine">
⤳ Reporter ⤳ Reporter
</button> </button>
<button id="btn-unset" class="btn-sm bg-cyber-border text-gray-400 px-3 py-1 text-xs" disabled title="Annuler éligibilité / report"> <button id="btn-unset" class="btn-sm bg-cyber-border text-gray-400 px-3 py-1 text-xs" title="Annuler éligibilité / report sur les lignes sélectionnées">
Annuler Annuler
</button> </button>
{% endif %} {% endif %}
<button id="btn-prepatch" class="btn-sm bg-cyber-yellow/20 text-cyber-yellow px-3 py-1 text-xs" disabled title="Lancer le pré-patching (rows éligibles uniquement)"> <button id="btn-prepatch" class="btn-sm bg-cyber-yellow/20 text-cyber-yellow px-3 py-1 text-xs" title="Lancer le pré-patching (rows éligibles uniquement)">
Pré-patching → Pré-patching →
</button> </button>
</div> </div>
@ -239,12 +239,18 @@
const allVisibleChecked = visibleCb.length > 0 && Array.from(visibleCb).every(cb => cb.checked); const allVisibleChecked = visibleCb.length > 0 && Array.from(visibleCb).every(cb => cb.checked);
selAll.checked = allVisibleChecked; selAll.checked = allVisibleChecked;
selAllHead.checked = allVisibleChecked; selAllHead.checked = allVisibleChecked;
// Boutons toujours cliquables : si rien de sélectionné on alerte au clic
// (au lieu de griser un bouton qu'on ne peut pas atteindre).
// Atténuation visuelle quand pas de sélection pour donner un feedback :
const hasSel = checked > 0; const hasSel = checked > 0;
if (btnAddElig) btnAddElig.disabled = !hasSel; const dimIfEmpty = (btn, active) => {
if (btnReport) btnReport.disabled = !hasSel; if (!btn) return;
if (btnUnset) btnUnset.disabled = !hasSel; btn.classList.toggle('opacity-50', !active);
// Pré-patching : actif uniquement si au moins 1 row éligible sélectionnée };
btnPre.disabled = eligibleSelected === 0; dimIfEmpty(btnAddElig, hasSel);
dimIfEmpty(btnReport, hasSel);
dimIfEmpty(btnUnset, hasSel);
dimIfEmpty(btnPre, eligibleSelected > 0);
} }
function getSelectedRowIds(){ function getSelectedRowIds(){
@ -410,13 +416,13 @@
if (btnAddElig) btnAddElig.addEventListener('click', async () => { if (btnAddElig) btnAddElig.addEventListener('click', async () => {
const ids = getSelectedRowIds(); const ids = getSelectedRowIds();
if (!ids.length) return; if (!ids.length) { alert('Veuillez sélectionner au moins un serveur.'); return; }
if (!confirm('Marquer ' + ids.length + ' ligne(s) comme éligibles au patching ?')) return; if (!confirm('Marquer ' + ids.length + ' ligne(s) comme éligibles au patching ?')) return;
if (await postAction({row_ids: ids, action: 'eligible'})) await reloadCurrentSheet(); if (await postAction({row_ids: ids, action: 'eligible'})) await reloadCurrentSheet();
}); });
if (btnReport) btnReport.addEventListener('click', async () => { if (btnReport) btnReport.addEventListener('click', async () => {
const ids = getSelectedRowIds(); const ids = getSelectedRowIds();
if (!ids.length) return; if (!ids.length) { alert('Veuillez sélectionner au moins un serveur.'); return; }
const target = (prompt('Reporter vers quelle semaine ? (ex: S23)') || '').trim(); const target = (prompt('Reporter vers quelle semaine ? (ex: S23)') || '').trim();
if (!target) return; if (!target) return;
if (!/^S\d{1,2}$/i.test(target)) { alert('Format attendu : Sxx (ex S23)'); return; } if (!/^S\d{1,2}$/i.test(target)) { alert('Format attendu : Sxx (ex S23)'); return; }
@ -425,7 +431,7 @@
}); });
if (btnUnset) btnUnset.addEventListener('click', async () => { if (btnUnset) btnUnset.addEventListener('click', async () => {
const ids = getSelectedRowIds(); const ids = getSelectedRowIds();
if (!ids.length) return; if (!ids.length) { alert('Veuillez sélectionner au moins un serveur.'); return; }
if (!confirm('Annuler éligibilité ET report sur ' + ids.length + ' ligne(s) ?')) return; if (!confirm('Annuler éligibilité ET report sur ' + ids.length + ' ligne(s) ?')) return;
if (await postAction({row_ids: ids, action: 'unset_eligible'})) { if (await postAction({row_ids: ids, action: 'unset_eligible'})) {
await postAction({row_ids: ids, action: 'unset_report'}); await postAction({row_ids: ids, action: 'unset_report'});
@ -433,11 +439,15 @@
} }
}); });
btnPre.addEventListener('click', () => { btnPre.addEventListener('click', () => {
const ids = Array.from(tbody.querySelectorAll('input.row-cb:checked')) const checkedAny = Array.from(tbody.querySelectorAll('input.row-cb:checked'));
if (!checkedAny.length) { alert('Veuillez sélectionner au moins un serveur.'); return; }
const ids = checkedAny
.filter(cb => cb.dataset.eligible === '1') .filter(cb => cb.dataset.eligible === '1')
.map(cb => cb.dataset.id); .map(cb => cb.dataset.id);
if (!ids.length) { alert('Aucune ligne éligible sélectionnée.'); return; } if (!ids.length) {
// Étape B : workflow iexec à brancher alert('Aucun serveur sélectionné n\'est éligible. Marque-les d\'abord avec "+ Ajouter au patching".');
return;
}
window.location.href = '/patching/iexec?row_ids=' + ids.join(','); window.location.href = '/patching/iexec?row_ids=' + ids.join(',');
}); });
})(); })();