Loading overlay, fix specifics edit, fix quotes, data-loading

- Overlay plein écran avec spinner pendant les actions longues
- data-loading attribute au lieu de onclick (évite problèmes quotes)
- Auto-attach JS sur tous les boutons data-loading
- Fix panel édition spécifiques (déplacé en haut)
- Fix double display:none sur overlay
- Messages descriptifs par action (resync, bulk, audit, prereqs)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Khalid MOUTAOUAKIL 2026-04-05 01:07:12 +02:00
parent 8e62b1fb11
commit a8f7329a48
7 changed files with 37 additions and 9 deletions

View File

@ -60,7 +60,7 @@
<p class="text-xs text-gray-600 mt-2">EMV exclu par défaut (zone PCI-DSS)</p> <p class="text-xs text-gray-600 mt-2">EMV exclu par défaut (zone PCI-DSS)</p>
</div> </div>
</div> </div>
<button type="submit" class="btn-primary px-4 py-2 text-sm" onclick="this.textContent='Audit global en cours...'; this.disabled=true; this.form.submit()">Lancer l'audit général</button> <button type="submit" class="btn-primary px-4 py-2 text-sm" data-loading="Audit global en cours...|Connexion SSH - plusieurs minutes">Lancer l'audit général</button>
</form> </form>
</div> </div>
</div> </div>

View File

@ -31,7 +31,7 @@
</div> </div>
</div> </div>
<p class="text-xs text-gray-600">Si plus de 10 serveurs, le parallélisme est recommandé.</p> <p class="text-xs text-gray-600">Si plus de 10 serveurs, le parallélisme est recommandé.</p>
<button type="submit" class="btn-primary px-4 py-2 text-sm" onclick="this.textContent='Audit en cours...'; this.disabled=true; this.form.submit()">Lancer l'audit</button> <button type="submit" class="btn-primary px-4 py-2 text-sm" data-loading="Audit en cours...|Connexion SSH - peut prendre quelques minutes">Lancer l'audit</button>
</form> </form>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -92,5 +92,31 @@
{% block fullpage %}{% endblock %} {% block fullpage %}{% endblock %}
{% endif %} {% endif %}
<div id="toast-container"></div> <div id="toast-container"></div>
<!-- Overlay chargement -->
<div id="loading-overlay" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(10,14,23,0.85); z-index:9999; justify-content:center; align-items:center;">
<div style="text-align:center">
<div style="border:3px solid #1e3a5f; border-top:3px solid #00d4ff; border-radius:50%; width:40px; height:40px; animation:spin 1s linear infinite; margin:0 auto 16px"></div>
<div id="loading-msg" style="color:#00d4ff; font-size:14px; font-weight:600">Opération en cours...</div>
<div id="loading-sub" style="color:#94a3b8; font-size:12px; margin-top:6px"></div>
</div>
</div>
<style>@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }</style>
<script>
function showLoading(msg, sub) {
document.getElementById('loading-msg').textContent = msg || 'Opération en cours...';
document.getElementById('loading-sub').textContent = sub || '';
document.getElementById('loading-overlay').style.display = 'flex';
}
function hideLoading() { document.getElementById('loading-overlay').style.display = 'none'; }
// Auto-attach: tout bouton avec data-loading affiche l'overlay au clic
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('[data-loading]').forEach(function(btn) {
btn.addEventListener('click', function(e) {
var parts = (btn.dataset.loading || 'Opération en cours...|').split('|');
showLoading(parts[0], parts[1] || '');
});
});
});
</script>
</body> </body>
</html> </html>

View File

@ -93,7 +93,7 @@
<div class="flex justify-between items-center mb-2"> <div class="flex justify-between items-center mb-2">
<h3 class="text-sm font-bold text-cyber-accent">Prérequisuis ({{ prereq.prereq_ok }}/{{ prereq.total_pending }} valides)</h3> <h3 class="text-sm font-bold text-cyber-accent">Prérequisuis ({{ prereq.prereq_ok }}/{{ prereq.total_pending }} valides)</h3>
<form method="POST" action="/campaigns/{{ c.id }}/check-prereqs"> <form method="POST" action="/campaigns/{{ c.id }}/check-prereqs">
<button class="btn-primary px-3 py-1 text-sm">Vérifier prereqs</button> <button class="btn-primary px-3 py-1 text-sm" data-loading="Vérification des prérequis...|Test SSH + disque + satellite"Vérification des prérequis...", "Test SSH + espace disque + satellite pour chaque serveur")">Vérifier prereqs</button>
</form> </form>
</div> </div>
<div class="grid grid-cols-5 gap-3 text-sm"> <div class="grid grid-cols-5 gap-3 text-sm">

View File

@ -63,7 +63,7 @@
onclick="document.getElementById('bulk-add-tag-id').value='{{ t.qualys_tag_id }}'; document.getElementById('bulk-add-input').value='{{ t.name }}'; document.getElementById('bulk-add-dd').style.display='none'"> onclick="document.getElementById('bulk-add-tag-id').value='{{ t.qualys_tag_id }}'; document.getElementById('bulk-add-input').value='{{ t.name }}'; document.getElementById('bulk-add-dd').style.display='none'">
<span class="font-mono">{{ t.name }}</span></div>{% endfor %} <span class="font-mono">{{ t.name }}</span></div>{% endfor %}
</div> </div>
<button type="submit" class="btn-sm bg-cyber-accent text-black">+ Ajouter</button> <button type="submit" class="btn-sm bg-cyber-accent text-black" data-loading="Ajout de tag en masse...|Appel API Qualys pour chaque asset">+ Ajouter</button>
</form> </form>
<form method="POST" action="/qualys/bulk/remove-tag" class="flex gap-2 items-center relative"> <form method="POST" action="/qualys/bulk/remove-tag" class="flex gap-2 items-center relative">
<input type="hidden" name="asset_ids" id="bulk-tag-ids2"> <input type="hidden" name="asset_ids" id="bulk-tag-ids2">
@ -75,13 +75,13 @@
<div id="bulk-rm-dd" class="absolute top-8 left-0 bg-cyber-card border border-cyber-border rounded overflow-y-auto text-xs z-50" style="display:none; max-height:150px; width:250px"> <div id="bulk-rm-dd" class="absolute top-8 left-0 bg-cyber-card border border-cyber-border rounded overflow-y-auto text-xs z-50" style="display:none; max-height:150px; width:250px">
<div class="px-2 py-1 text-gray-500">Chargement...</div> <div class="px-2 py-1 text-gray-500">Chargement...</div>
</div> </div>
<button type="submit" class="btn-sm bg-red-900/30 text-cyber-red">- Retirer</button> <button type="submit" class="btn-sm bg-red-900/30 text-cyber-red" data-loading="Retrait de tag en masse...|Appel API Qualys pour chaque asset">- Retirer</button>
</form> </form>
<form method="POST" action="/qualys/resync-assets" class="flex gap-2 items-center"> <form method="POST" action="/qualys/resync-assets" class="flex gap-2 items-center">
<input type="hidden" name="asset_ids" id="bulk-tag-ids3"> <input type="hidden" name="asset_ids" id="bulk-tag-ids3">
<input type="hidden" name="return_search" value="{{ search or '' }}"> <input type="hidden" name="return_search" value="{{ search or '' }}">
<input type="hidden" name="return_field" value="{{ field or 'hostname' }}"> <input type="hidden" name="return_field" value="{{ field or 'hostname' }}">
<button type="submit" class="btn-sm bg-cyber-border text-cyber-accent">Resync Qualys</button> <button type="submit" class="btn-sm bg-cyber-border text-cyber-accent" data-loading="Resynchronisation Qualys...|Mise à jour depuis API">Resync Qualys</button>
</form> </form>
</div> </div>
<script> <script>

View File

@ -6,7 +6,7 @@
<div class="flex gap-2"> <div class="flex gap-2">
{% if can_edit_qualys %} {% if can_edit_qualys %}
<form method="POST" action="/qualys/tags/resync" style="display:inline"> <form method="POST" action="/qualys/tags/resync" style="display:inline">
<button class="btn-sm bg-cyber-border text-cyber-accent">Resync API</button> <button class="btn-sm bg-cyber-border text-cyber-accent" data-loading="Synchronisation des tags Qualys...|Cette opération peut prendre 1 à 2 minutes">Resync API</button>
</form> </form>
{% endif %} {% endif %}
<a href="/qualys/tags/export" class="btn-sm bg-cyber-green text-black">Export CSV</a> <a href="/qualys/tags/export" class="btn-sm bg-cyber-green text-black">Export CSV</a>

View File

@ -8,7 +8,7 @@
{% set msg = request.query_params.get('msg') %} {% set msg = request.query_params.get('msg') %}
{% if msg %} {% if msg %}
<div class="mb-4 p-2 rounded text-sm {% if msg in ('not_found','exists') %}bg-red-900/30 text-cyber-red{% else %}bg-green-900/30 text-cyber-green{% endif %}"> <div class="mb-4 p-2 rounded text-sm {% if msg in ('not_found','exists') %}bg-red-900/30 text-cyber-red{% else %}bg-green-900/30 text-cyber-green{% endif %}">
{% if msg == 'saved' %}Spécificités sauvegardées.{% elif msg == 'added' %}Serveur ajouté.{% elif msg == 'not_found' %}Hostname non trouvé en base.{% elif msg == 'exists' %}Ce serveur a déjà des spécificités.{% endif %} {% if msg == 'saved' %}Specificites sauvegardees.{% elif msg == 'added' %}Serveur ajoute.{% elif msg == 'not_found' %}Hostname non trouve en base.{% elif msg == 'exists' %}Ce serveur a deja des specificites.{% endif %}
</div> </div>
{% endif %} {% endif %}
@ -24,6 +24,9 @@
</form> </form>
</div> </div>
<!-- Panel édition -->
<div id="edit-panel" class="card mb-4 p-5" style="display:none"></div>
<!-- Table --> <!-- Table -->
<div class="card overflow-x-auto"> <div class="card overflow-x-auto">
<table class="w-full table-cyber"> <table class="w-full table-cyber">
@ -73,7 +76,6 @@
</div> </div>
<!-- Panel edition --> <!-- Panel edition -->
<div id="edit-panel" class="card mt-4 p-5" style="display:none"></div>
<!-- Ajouter --> <!-- Ajouter -->
<div class="card p-4 mt-4"> <div class="card p-4 mt-4">