feat(cloud): dashboard rows link to detail + desk Web Audio on critical
This commit is contained in:
parent
55d99602ce
commit
b0d1241da1
1 changed files with 58 additions and 1 deletions
|
|
@ -55,6 +55,55 @@
|
||||||
return badge;
|
return badge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- desk audio alert: repeating beep while any unacknowledged critical exists ---
|
||||||
|
// No binary asset (air-gapped friendly): a WebAudio oscillator tone. The browser
|
||||||
|
// blocks audio until a user gesture, so the context is created on first click.
|
||||||
|
var audioCtx = null;
|
||||||
|
var alarmTimer = null;
|
||||||
|
var beepedCritical = {}; // event_id -> true; each new critical beeps once immediately
|
||||||
|
|
||||||
|
function unlockAudio() {
|
||||||
|
if (!audioCtx) {
|
||||||
|
var AC = window.AudioContext || window.webkitAudioContext;
|
||||||
|
if (AC) audioCtx = new AC();
|
||||||
|
}
|
||||||
|
document.removeEventListener('click', unlockAudio);
|
||||||
|
}
|
||||||
|
|
||||||
|
function beep() {
|
||||||
|
if (!audioCtx) return;
|
||||||
|
try {
|
||||||
|
var osc = audioCtx.createOscillator();
|
||||||
|
var gain = audioCtx.createGain();
|
||||||
|
osc.type = 'sine';
|
||||||
|
osc.frequency.value = 880;
|
||||||
|
gain.gain.value = 0.2;
|
||||||
|
osc.connect(gain);
|
||||||
|
gain.connect(audioCtx.destination);
|
||||||
|
osc.start();
|
||||||
|
osc.stop(audioCtx.currentTime + 0.4);
|
||||||
|
} catch (e) {
|
||||||
|
// audio unavailable; the visual list still surfaces the critical event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAlarm(events) {
|
||||||
|
var hasUnackedCritical = false;
|
||||||
|
events.forEach(function (ev) {
|
||||||
|
var acked = ev.acknowledged_by !== null && ev.acknowledged_by !== undefined && ev.acknowledged_by !== '';
|
||||||
|
if (ev.severity === 'critical' && !acked) {
|
||||||
|
hasUnackedCritical = true;
|
||||||
|
if (!beepedCritical[ev.event_id]) { beepedCritical[ev.event_id] = true; beep(); }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (hasUnackedCritical && !alarmTimer) {
|
||||||
|
alarmTimer = setInterval(beep, 3000);
|
||||||
|
} else if (!hasUnackedCritical && alarmTimer) {
|
||||||
|
clearInterval(alarmTimer);
|
||||||
|
alarmTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function buildRow(ev) {
|
function buildRow(ev) {
|
||||||
var isAcked = ev.acknowledged_by !== null && ev.acknowledged_by !== undefined && ev.acknowledged_by !== '';
|
var isAcked = ev.acknowledged_by !== null && ev.acknowledged_by !== undefined && ev.acknowledged_by !== '';
|
||||||
|
|
||||||
|
|
@ -68,7 +117,11 @@
|
||||||
|
|
||||||
var type = document.createElement('div');
|
var type = document.createElement('div');
|
||||||
type.className = 'event-type';
|
type.className = 'event-type';
|
||||||
type.textContent = String(ev.event_type || '');
|
var typeLink = document.createElement('a');
|
||||||
|
typeLink.className = 'event-detail-link';
|
||||||
|
typeLink.href = '/events/' + encodeURIComponent(ev.event_id) + '/view';
|
||||||
|
typeLink.textContent = String(ev.event_type || '');
|
||||||
|
type.appendChild(typeLink);
|
||||||
meta.appendChild(type);
|
meta.appendChild(type);
|
||||||
|
|
||||||
var sub = document.createElement('div');
|
var sub = document.createElement('div');
|
||||||
|
|
@ -118,6 +171,8 @@
|
||||||
return aAcked ? 1 : -1; // unacknowledged first
|
return aAcked ? 1 : -1; // unacknowledged first
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateAlarm(sorted); // before any early return, so an emptied list also silences the alarm
|
||||||
|
|
||||||
root.textContent = '';
|
root.textContent = '';
|
||||||
root.removeAttribute('aria-busy');
|
root.removeAttribute('aria-busy');
|
||||||
if (sorted.length === 0) {
|
if (sorted.length === 0) {
|
||||||
|
|
@ -154,6 +209,8 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener('click', unlockAudio);
|
||||||
|
|
||||||
loadCsrfToken();
|
loadCsrfToken();
|
||||||
loadEvents();
|
loadEvents();
|
||||||
setInterval(loadEvents, POLL_INTERVAL_MS);
|
setInterval(loadEvents, POLL_INTERVAL_MS);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue