feat(dashboard): app principale WebSocket + orchestration
This commit is contained in:
@@ -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; },
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user