feat(snapshots): overlay plein page pendant action async + beforeunload guard
- Overlay 'busy-overlay' plein ecran (z-index 9999) avec spinner anime - Bloque toute interaction (clics) pendant recherche/suppression - setBusy active l'overlay + change le texte du bouton - clearBusy retire l'overlay - beforeunload: avertit l'utilisateur s'il tente de quitter la page pendant une requete en vol (close tab, navigation, refresh)
This commit is contained in:
parent
77e884d620
commit
0a3fde36b7
@ -18,8 +18,32 @@
|
|||||||
.badge-age-warn { background: rgba(245,158,11,.20); color: #f59e0b; border: 1px solid #f59e0b; }
|
.badge-age-warn { background: rgba(245,158,11,.20); color: #f59e0b; border: 1px solid #f59e0b; }
|
||||||
.badge-age-fresh { background: rgba(34,197,94,.20); color: #22c55e; border: 1px solid #22c55e; }
|
.badge-age-fresh { background: rgba(34,197,94,.20); color: #22c55e; border: 1px solid #22c55e; }
|
||||||
.filters-card label { display: flex; align-items: center; gap: 0.5rem; }
|
.filters-card label { display: flex; align-items: center; gap: 0.5rem; }
|
||||||
|
/* Overlay plein page pendant les actions async */
|
||||||
|
#busy-overlay {
|
||||||
|
position: fixed; inset: 0; z-index: 9999;
|
||||||
|
background: rgba(10,14,23,0.75); backdrop-filter: blur(2px);
|
||||||
|
display: none; align-items: center; justify-content: center;
|
||||||
|
flex-direction: column; gap: 1rem;
|
||||||
|
}
|
||||||
|
#busy-overlay.active { display: flex; }
|
||||||
|
.spinner {
|
||||||
|
width: 64px; height: 64px;
|
||||||
|
border: 4px solid rgba(245,158,11,0.2);
|
||||||
|
border-top-color: #f59e0b;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.9s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spin { to { transform: rotate(360deg); } }
|
||||||
|
#busy-text { color: #f59e0b; font-size: 1.05rem; font-weight: 600; text-shadow: 0 0 12px rgba(245,158,11,0.6); }
|
||||||
|
#busy-sub { color: #9ca3af; font-size: 0.85rem; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<div id="busy-overlay">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<div id="busy-text">Action en cours…</div>
|
||||||
|
<div id="busy-sub">Ne ferme pas la page, ne navigue pas ailleurs.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card p-4 mb-4 filters-card">
|
<div class="card p-4 mb-4 filters-card">
|
||||||
<div class="grid grid-cols-12 gap-3 items-end">
|
<div class="grid grid-cols-12 gap-3 items-end">
|
||||||
<div class="col-span-3">
|
<div class="col-span-3">
|
||||||
@ -199,8 +223,19 @@
|
|||||||
btnDelete.textContent = n > 0 ? `✕ SUPPRIMER ${n} SNAPSHOT${n>1?'S':''}` : '✕ SUPPRIMER LA SÉLECTION';
|
btnDelete.textContent = n > 0 ? `✕ SUPPRIMER ${n} SNAPSHOT${n>1?'S':''}` : '✕ SUPPRIMER LA SÉLECTION';
|
||||||
}
|
}
|
||||||
|
|
||||||
function setBusy(msg, btn) {
|
// Overlay plein page : bloque toute interaction pendant les actions async.
|
||||||
|
// Empêche aussi la navigation accidentelle via beforeunload.
|
||||||
|
const overlay = document.getElementById('busy-overlay');
|
||||||
|
const busyText = document.getElementById('busy-text');
|
||||||
|
const busySub = document.getElementById('busy-sub');
|
||||||
|
let busyActive = false;
|
||||||
|
|
||||||
|
function setBusy(msg, btn, sub) {
|
||||||
status.innerHTML = '<span class="text-cyber-yellow">⏳ ' + escapeHTML(msg) + '</span>';
|
status.innerHTML = '<span class="text-cyber-yellow">⏳ ' + escapeHTML(msg) + '</span>';
|
||||||
|
busyText.textContent = '⏳ ' + msg;
|
||||||
|
busySub.textContent = sub || 'Ne ferme pas la page, ne navigue pas ailleurs.';
|
||||||
|
overlay.classList.add('active');
|
||||||
|
busyActive = true;
|
||||||
if (btn) {
|
if (btn) {
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
btn._origText = btn._origText || btn.textContent;
|
btn._origText = btn._origText || btn.textContent;
|
||||||
@ -208,11 +243,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function clearBusy(btn) {
|
function clearBusy(btn) {
|
||||||
|
overlay.classList.remove('active');
|
||||||
|
busyActive = false;
|
||||||
if (btn && btn._origText) {
|
if (btn && btn._origText) {
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.textContent = btn._origText;
|
btn.textContent = btn._origText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Avertit si l'utilisateur tente de quitter la page pendant une action
|
||||||
|
window.addEventListener('beforeunload', (e) => {
|
||||||
|
if (busyActive) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.returnValue = 'Une action est en cours. Quitter maintenant peut interrompre la requête.';
|
||||||
|
return e.returnValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
btnRefresh.addEventListener('click', async () => {
|
btnRefresh.addEventListener('click', async () => {
|
||||||
setBusy('Recherche en cours… (peut prendre 10-30 s selon les vCenters)', btnRefresh);
|
setBusy('Recherche en cours… (peut prendre 10-30 s selon les vCenters)', btnRefresh);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user