dragonflight/services/web-ui/public/js/topbar-strip.js

60 lines
2.6 KiB
JavaScript

// Operator status strip mounted at the top of every .main pane.
(function () {
function esc(s) {
if (!s) return '';
return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
function mount() {
const main = document.querySelector('.main');
if (!main || main.querySelector('.topbar-strip')) return;
const strip = document.createElement('div');
strip.className = 'topbar-strip';
strip.innerHTML =
'<span class="ts-dot" id="tsDot" title="System pulse"></span>' +
'<span class="ts-label">Z-AMPP</span>' +
'<span class="ts-now" id="tsNow">00:00:00</span>' +
'<span class="ts-sep"></span>' +
'<span class="ts-label">Page</span>' +
'<span class="ts-value" id="tsPage">' + esc(pageName()) + '</span>' +
'<span class="ts-sep"></span>' +
'<span class="ts-label">API</span>' +
'<span class="ts-value" id="tsApi">--</span>';
main.insertBefore(strip, main.firstChild);
tick(); setInterval(tick, 1000);
ping(); setInterval(ping, 10000);
}
function pageName() {
const p = location.pathname.replace(/\.html$/, '').replace(/^\//, '');
if (!p || p === 'index') return 'Library';
return p.charAt(0).toUpperCase() + p.slice(1);
}
function tick() {
const n = new Date();
const hh = String(n.getHours()).padStart(2, '0');
const mm = String(n.getMinutes()).padStart(2, '0');
const ss = String(n.getSeconds()).padStart(2, '0');
const el = document.getElementById('tsNow');
if (el) el.textContent = hh + ':' + mm + ':' + ss;
}
async function ping() {
const dot = document.getElementById('tsDot');
const apiEl = document.getElementById('tsApi');
const t0 = performance.now();
try {
const r = await fetch('/api/v1/projects', { credentials: 'include', signal: AbortSignal.timeout(4000) });
const ms = Math.round(performance.now() - t0);
if (r.ok) {
if (dot) { dot.style.background = 'var(--signal-good)'; dot.style.boxShadow = '0 0 8px var(--signal-good)'; }
if (apiEl) apiEl.textContent = ms + ' ms';
} else {
if (dot) { dot.style.background = 'var(--signal-warn)'; dot.style.boxShadow = '0 0 8px var(--signal-warn)'; }
if (apiEl) apiEl.textContent = 'HTTP ' + r.status;
}
} catch (e) {
if (dot) { dot.style.background = 'var(--signal-bad)'; dot.style.boxShadow = '0 0 8px var(--signal-bad)'; }
if (apiEl) apiEl.textContent = 'offline';
}
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', mount);
else mount();
})();