4dd1b8265f
Ship export intel with contact filters and CRM push, Herman live digest with Telegram briefing, halal recommendation engine, revenue cockpit, and dashboard polling/WebSocket fixes.
77 lines
2.6 KiB
JavaScript
77 lines
2.6 KiB
JavaScript
window.CockpitLive = (function () {
|
|
var ws = null;
|
|
var reconnectTimer = null;
|
|
|
|
function startPolling(fn, intervalMs) {
|
|
intervalMs = intervalMs || 30000;
|
|
fn();
|
|
var id = setInterval(fn, intervalMs);
|
|
return function () { clearInterval(id); };
|
|
}
|
|
|
|
function eventFingerprint(data) {
|
|
var ev = (data && data.events) || [];
|
|
if (!ev.length) return '';
|
|
return ev.slice(0, 5).map(function (e) {
|
|
return String(e.created_at || '') + '#' + String(e.agent_name || '') + '#' + String(e.title || e.event_type || '');
|
|
}).join('|');
|
|
}
|
|
|
|
function connectFeed(onMessage) {
|
|
if (typeof WebSocket === 'undefined') return function () {};
|
|
var proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
var lastFp = '';
|
|
var booted = false;
|
|
function connect() {
|
|
try {
|
|
ws = new WebSocket(proto + '//' + location.host + '/ws/feed');
|
|
ws.onmessage = function (ev) {
|
|
try {
|
|
var data = JSON.parse(ev.data);
|
|
var fp = eventFingerprint(data);
|
|
if (!booted) {
|
|
booted = true;
|
|
lastFp = fp;
|
|
if (onMessage) onMessage(data, { reason: 'connect' });
|
|
return;
|
|
}
|
|
if (fp && fp !== lastFp) {
|
|
lastFp = fp;
|
|
if (onMessage) onMessage(data, { reason: 'events' });
|
|
}
|
|
} catch (e) {}
|
|
};
|
|
ws.onclose = function () {
|
|
booted = false;
|
|
lastFp = '';
|
|
reconnectTimer = setTimeout(connect, 5000);
|
|
};
|
|
} catch (e) {
|
|
reconnectTimer = setTimeout(connect, 5000);
|
|
}
|
|
}
|
|
connect();
|
|
return function () {
|
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
if (ws) { try { ws.close(); } catch (e) {} ws = null; }
|
|
};
|
|
}
|
|
|
|
function updateLiveBadge(el, at) {
|
|
if (!el) return;
|
|
el.textContent = 'Live · ' + (at || new Date().toLocaleTimeString('nl-NL', { hour: '2-digit', minute: '2-digit' }));
|
|
}
|
|
|
|
function renderAgentFeed(container, events) {
|
|
if (!container || !events) return;
|
|
container.innerHTML = events.slice(0, 12).map(function (ev) {
|
|
var t = (ev.created_at || '').substring(11, 16);
|
|
return '<div class="hub-feed-item"><span>' + t + '</span>' +
|
|
'<span class="badge badge-agent-' + String(ev.agent_name || '').toLowerCase() + '">' + (ev.agent_name || '') + '</span> ' +
|
|
(ev.title || ev.event_type || '') + '</div>';
|
|
}).join('') || '<p class="empty-state">Nog geen events.</p>';
|
|
}
|
|
|
|
return { startPolling: startPolling, connectFeed: connectFeed, updateLiveBadge: updateLiveBadge, renderAgentFeed: renderAgentFeed };
|
|
})();
|