feat(dashboard): app principale WebSocket + orchestration

This commit is contained in:
Gilles Soulier
2026-05-22 12:43:33 +02:00
parent 182d520675
commit 555ddc3556
+103
View File
@@ -0,0 +1,103 @@
const App = (() => {
let _ws = null;
let _reconnectDelay = 1000;
let _serverConfig = null;
// Tooltip global position:fixed
const tip = document.getElementById('tooltip');
let _tt;
document.addEventListener('mouseover', e => {
const el = e.target.closest('[data-tip]');
if (!el) return;
clearTimeout(_tt);
_tt = setTimeout(() => {
tip.textContent = el.dataset.tip;
tip.classList.add('show');
}, 120);
});
document.addEventListener('mousemove', e => {
if (!tip.classList.contains('show')) return;
const w = tip.offsetWidth, h = tip.offsetHeight;
let x = e.clientX - w / 2, y = e.clientY - h - 10;
x = Math.max(6, Math.min(x, window.innerWidth - w - 6));
if (y < 6) y = e.clientY + 18;
tip.style.left = x + 'px';
tip.style.top = y + 'px';
});
document.addEventListener('mouseout', e => {
if (!e.target.closest('[data-tip]')) return;
clearTimeout(_tt);
tip.classList.remove('show');
});
function toggleTheme() {
const h = document.documentElement;
h.dataset.theme = h.dataset.theme === 'dark' ? 'light' : 'dark';
document.getElementById('theme-icon').className =
h.dataset.theme === 'dark' ? 'fa-solid fa-moon' : 'fa-solid fa-sun';
}
function updateClock() {
document.getElementById('f-time').textContent =
new Date().toLocaleTimeString('fr-FR');
}
function connectWS() {
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
_ws = new WebSocket(`${proto}://${location.host}/ws`);
_ws.onopen = () => {
_reconnectDelay = 1000;
document.querySelector('.logo-led').style.animation = 'blink 2s infinite';
};
_ws.onmessage = (event) => {
try {
const msg = JSON.parse(event.data);
if (msg.type === 'metrics_update') {
Grid.update(msg.agent_id, msg.data);
updateClock();
}
} catch {}
};
_ws.onclose = () => {
setTimeout(connectWS, _reconnectDelay);
_reconnectDelay = Math.min(_reconnectDelay * 2, 30000);
};
}
async function init() {
try {
_serverConfig = await API.getServerConfig();
if (_serverConfig.tile_min_width) {
document.documentElement.style.setProperty('--tile-min', _serverConfig.tile_min_width + 'px');
}
if (_serverConfig.font_size) {
document.body.style.fontSize = _serverConfig.font_size + 'px';
}
if (_serverConfig.popup_detail_w && _serverConfig.popup_detail_h) {
const pd = document.getElementById('popup-detail');
pd.style.width = _serverConfig.popup_detail_w + 'px';
pd.style.height = _serverConfig.popup_detail_h + 'px';
}
} catch {}
try {
const agents = await API.getAgents();
Grid.refresh(agents);
} catch {}
connectWS();
updateClock();
setInterval(updateClock, 1000);
}
document.addEventListener('DOMContentLoaded', init);
return {
toggleTheme,
get serverConfig() { return _serverConfig; },
set serverConfig(v) { _serverConfig = v; },
};
})();