Add live CEO briefing, Wereldexport CRM, halal engine, and realtime cockpit.
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.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
window.BriefingCharts = (function () {
|
||||
var charts = {};
|
||||
var typewriterTimer = null;
|
||||
var typewriterAbort = null;
|
||||
var lastDigestSig = '';
|
||||
|
||||
function destroyAll() {
|
||||
Object.keys(charts).forEach(function (k) {
|
||||
@@ -393,17 +395,207 @@ window.BriefingCharts = (function () {
|
||||
var parsed = parseContent(content);
|
||||
var meta = document.getElementById('briefing-meta');
|
||||
if (meta && createdAt) meta.textContent = 'Laatst bijgewerkt: ' + String(createdAt).substring(0, 19).replace('T', ' ');
|
||||
typewriter(document.getElementById('briefing-summary-text'), parsed.summary, 8);
|
||||
var sumEl = document.getElementById('briefing-summary-text');
|
||||
if (sumEl && parsed.summary) {
|
||||
var lines = parsed.summary.split(/(?<=[.!?])\s+/).filter(Boolean);
|
||||
typewriterLiveDigest(sumEl, {
|
||||
sections: [{ brand: 'platform', title: 'Herman rapport', icon: '📝', lines: lines.length ? lines : [parsed.summary] }],
|
||||
}, { charMs: 10, linePause: 90 });
|
||||
}
|
||||
var actEl = document.getElementById('briefing-actions-list');
|
||||
if (actEl) actEl.innerHTML = parsed.actions.length ? parsed.actions.map(function (a) { return '<li>' + a + '</li>'; }).join('') : '<li>Genereer dagrapport voor actiepunten</li>';
|
||||
if (actEl) typewriterListItems(actEl, parsed.actions.length ? parsed.actions : ['Genereer dagrapport voor actiepunten'], { charMs: 8 });
|
||||
var longEl = document.getElementById('briefing-longterm-list');
|
||||
if (longEl) longEl.innerHTML = parsed.longTerm.length ? parsed.longTerm.map(function (a) { return '<li>' + a + '</li>'; }).join('') : '<li>Halal kant-en-klaar partnerships schalen</li>';
|
||||
if (longEl) setTimeout(function () {
|
||||
typewriterListItems(longEl, parsed.longTerm.length ? parsed.longTerm : ['Halal kant-en-klaar partnerships schalen'], { charMs: 8 });
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function renderLive(root, stats, vizMode, kpiSelection) {
|
||||
function escHtml(s) {
|
||||
return String(s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function digestSignature(digest) {
|
||||
if (!digest) return '';
|
||||
return JSON.stringify({
|
||||
sections: digest.sections || [],
|
||||
actions: digest.actions || [],
|
||||
long_term: digest.long_term || [],
|
||||
});
|
||||
}
|
||||
|
||||
function fillDigestInstant(container, digest) {
|
||||
var sections = (digest && digest.sections) || [];
|
||||
if (!sections.length) {
|
||||
container.innerHTML = '<p class="muted">Live samenvatting laden…</p>';
|
||||
return;
|
||||
}
|
||||
container.innerHTML = sections.map(function (sec) {
|
||||
var lines = (sec.lines || []).filter(Boolean);
|
||||
var lis = lines.length
|
||||
? lines.map(function (l) { return '<li>' + escHtml(l) + '</li>'; }).join('')
|
||||
: '<li class="muted">Geen data</li>';
|
||||
return '<div class="live-digest-section brand-' + escHtml(sec.brand || 'shared') + '">' +
|
||||
'<h5>' + escHtml((sec.icon || '') + ' ' + (sec.title || '')) + '</h5>' +
|
||||
'<ul class="live-digest-list">' + lis + '</ul></div>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function typewriterLiveDigest(container, digest, opts) {
|
||||
if (!container) return;
|
||||
opts = opts || {};
|
||||
var charMs = opts.charMs || 14;
|
||||
var linePause = opts.linePause || 120;
|
||||
var sectionPause = opts.sectionPause || 200;
|
||||
|
||||
if (typewriterAbort) { typewriterAbort(); typewriterAbort = null; }
|
||||
if (typewriterTimer) { clearInterval(typewriterTimer); typewriterTimer = null; }
|
||||
|
||||
var sections = (digest && digest.sections) || [];
|
||||
if (!sections.length) {
|
||||
var summary = (digest && digest.summary) || '';
|
||||
if (summary) {
|
||||
typewriter(container, summary, charMs);
|
||||
} else {
|
||||
container.innerHTML = '<p class="muted">Live samenvatting laden…</p>';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = sections.map(function (sec) {
|
||||
var lines = (sec.lines || []).filter(Boolean);
|
||||
var lis = lines.length
|
||||
? lines.map(function () { return '<li class="tw-line"></li>'; }).join('')
|
||||
: '<li class="muted">Geen data</li>';
|
||||
return '<div class="live-digest-section brand-' + escHtml(sec.brand || 'shared') + '">' +
|
||||
'<h5 class="tw-heading">' + escHtml((sec.icon || '') + ' ' + (sec.title || '')) + '</h5>' +
|
||||
'<ul class="live-digest-list">' + lis + '</ul></div>';
|
||||
}).join('');
|
||||
|
||||
var queue = [];
|
||||
container.querySelectorAll('.live-digest-section').forEach(function (secEl, si) {
|
||||
var lines = (sections[si] && sections[si].lines) || [];
|
||||
var lis = secEl.querySelectorAll('li.tw-line');
|
||||
for (var i = 0; i < lis.length; i++) {
|
||||
queue.push({ el: lis[i], text: lines[i] || '', section: si });
|
||||
}
|
||||
});
|
||||
|
||||
var cancelled = false;
|
||||
typewriterAbort = function () { cancelled = true; };
|
||||
container.classList.add('is-typing');
|
||||
|
||||
function tick(qi, ci, prevSection) {
|
||||
if (cancelled) return;
|
||||
if (qi >= queue.length) {
|
||||
container.classList.remove('is-typing');
|
||||
container.querySelectorAll('.is-typing').forEach(function (n) { n.classList.remove('is-typing'); });
|
||||
return;
|
||||
}
|
||||
var item = queue[qi];
|
||||
var el = item.el;
|
||||
var text = item.text;
|
||||
if (prevSection !== item.section && ci === 0) {
|
||||
setTimeout(function () { if (!cancelled) tick(qi, ci, item.section); }, sectionPause);
|
||||
return;
|
||||
}
|
||||
el.classList.add('is-typing');
|
||||
container.querySelectorAll('li.is-typing').forEach(function (n) { if (n !== el) n.classList.remove('is-typing'); });
|
||||
if (ci < text.length) {
|
||||
el.textContent = text.substring(0, ci + 1);
|
||||
setTimeout(function () { tick(qi, ci + 1, item.section); }, charMs);
|
||||
} else {
|
||||
el.classList.remove('is-typing');
|
||||
setTimeout(function () { tick(qi + 1, 0, item.section); }, linePause);
|
||||
}
|
||||
}
|
||||
tick(0, 0, -1);
|
||||
}
|
||||
|
||||
function typewriterListItems(listEl, items, opts) {
|
||||
if (!listEl || !items || !items.length) return;
|
||||
opts = opts || {};
|
||||
var charMs = opts.charMs || 10;
|
||||
listEl.innerHTML = items.map(function () { return '<li class="tw-line"></li>'; }).join('');
|
||||
var lis = listEl.querySelectorAll('li.tw-line');
|
||||
var qi = 0, ci = 0, cancelled = false;
|
||||
function next() {
|
||||
if (cancelled || qi >= items.length) return;
|
||||
var el = lis[qi];
|
||||
var text = items[qi];
|
||||
el.classList.add('is-typing');
|
||||
if (ci < text.length) {
|
||||
el.textContent = text.substring(0, ci + 1);
|
||||
ci++;
|
||||
setTimeout(next, charMs);
|
||||
} else {
|
||||
el.classList.remove('is-typing');
|
||||
qi++; ci = 0;
|
||||
setTimeout(next, 90);
|
||||
}
|
||||
}
|
||||
next();
|
||||
return function () { cancelled = true; };
|
||||
}
|
||||
|
||||
function fillListInstant(listEl, items) {
|
||||
if (!listEl) return;
|
||||
var rows = (items && items.length) ? items : ['Geen urgente acties — check agents & retail'];
|
||||
listEl.innerHTML = rows.map(function (t) { return '<li>' + escHtml(t) + '</li>'; }).join('');
|
||||
}
|
||||
|
||||
function renderLiveSummary(digest, pollRefresh) {
|
||||
if (!digest) return;
|
||||
var sumEl = document.getElementById('briefing-summary-text');
|
||||
var actEl = document.getElementById('briefing-actions-list');
|
||||
var longEl = document.getElementById('briefing-longterm-list');
|
||||
var meta = document.getElementById('briefing-meta');
|
||||
|
||||
var sig = digestSignature(digest);
|
||||
var unchanged = pollRefresh && sig === lastDigestSig;
|
||||
if (!unchanged) lastDigestSig = sig;
|
||||
|
||||
if (meta) {
|
||||
var ts = String(digest.updated_at || '').substring(0, 19).replace('T', ' ');
|
||||
var n = digest.activity_count != null ? ' · ' + digest.activity_count + ' activiteiten' : '';
|
||||
meta.textContent = 'Live · ververst ' + ts + ' UTC' + n;
|
||||
}
|
||||
|
||||
if (unchanged) {
|
||||
if (actEl) fillListInstant(actEl, digest.actions || []);
|
||||
if (longEl) fillListInstant(longEl, digest.long_term || []);
|
||||
return;
|
||||
}
|
||||
|
||||
var isUpdate = !!lastDigestSig && !pollRefresh;
|
||||
var twOpts = { charMs: isUpdate ? 8 : 12, linePause: isUpdate ? 70 : 100, sectionPause: isUpdate ? 120 : 180 };
|
||||
|
||||
if (sumEl) {
|
||||
if (pollRefresh) fillDigestInstant(sumEl, digest);
|
||||
else typewriterLiveDigest(sumEl, digest, twOpts);
|
||||
}
|
||||
|
||||
var actions = digest.actions || [];
|
||||
if (actEl) {
|
||||
if (pollRefresh) fillListInstant(actEl, actions.length ? actions : ['Geen urgente acties — check agents & retail']);
|
||||
else typewriterListItems(actEl, actions.length ? actions : ['Geen urgente acties — check agents & retail'], { charMs: isUpdate ? 6 : 8 });
|
||||
}
|
||||
|
||||
var lt = digest.long_term || [];
|
||||
if (longEl) {
|
||||
if (pollRefresh) fillListInstant(longEl, lt.length ? lt : ['Halal kant-en-klaar partnerships schalen']);
|
||||
else setTimeout(function () {
|
||||
typewriterListItems(longEl, lt.length ? lt : ['Halal kant-en-klaar partnerships schalen'], { charMs: isUpdate ? 6 : 8 });
|
||||
}, isUpdate ? 200 : 400);
|
||||
}
|
||||
}
|
||||
|
||||
function renderLive(root, stats, vizMode, kpiSelection, liveDigest, opts) {
|
||||
if (!root || !stats) return;
|
||||
opts = opts || {};
|
||||
var mode = vizMode || window._dashboardVizMode || 'neo-bars';
|
||||
var kpis = kpiSelection || window._dashboardKpis;
|
||||
if (liveDigest) renderLiveSummary(liveDigest, !!opts.pollRefresh);
|
||||
else if (stats.live_digest) renderLiveSummary(stats.live_digest, !!opts.pollRefresh);
|
||||
renderKpis(document.getElementById('briefing-kpis'), stats, kpis);
|
||||
renderFoodHighlights(document.getElementById('briefing-food-highlights'), stats);
|
||||
renderExecutiveSummary(document.getElementById('briefing-executive-summary'), stats);
|
||||
@@ -432,7 +624,7 @@ window.BriefingCharts = (function () {
|
||||
}
|
||||
|
||||
return {
|
||||
render: render, renderLive: renderLive, renderText: renderText,
|
||||
render: render, renderLive: renderLive, renderText: renderText, renderLiveSummary: renderLiveSummary,
|
||||
renderExecutiveSummary: renderExecutiveSummary, renderRssFeed: renderRssFeed,
|
||||
renderKpis: renderKpis, destroyAll: destroyAll,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user