SysOps: deploy-all — 2026-06-09 10:59 UTC
This commit is contained in:
@@ -232,19 +232,65 @@ window.BriefingCharts = (function () {
|
||||
});
|
||||
}
|
||||
|
||||
function renderKpis(container, stats) {
|
||||
if (!container) return;
|
||||
function kpiValue(id, stats) {
|
||||
var summary = stats.market_summary || {};
|
||||
var avgPct = Number(summary.avg_change_pct || 0);
|
||||
var best = summary.best_performer || {};
|
||||
var items = [
|
||||
{ label: 'Pipeline', sub: 'actieve deals', icon: '💰', color: '#00e5ff', pct: '72%', val: '€' + Math.round(stats.pipeline_eur || 0).toLocaleString('nl-NL') },
|
||||
{ label: 'Actieve klanten', sub: 'CRM · zaken mee', icon: '🤝', color: '#22c55e', pct: Math.min(95, Math.round(((stats.clients_active || 0) / Math.max(stats.clients_total || 1, 1)) * 100)) + '%', val: (stats.clients_active || 0) + ' / ' + (stats.clients_total || 0), link: '/clients' },
|
||||
{ label: 'CRM partnerships', sub: 'actieve filialen', icon: '🏪', color: '#ff9f43', pct: '45%', val: stats.crm_partnerships || 0, link: '/retail' },
|
||||
{ label: 'Supermarkten', sub: 'Retail 360 DB', icon: '🏪', color: '#b8ff3c', pct: '88%', val: (stats.supermarkets || 0).toLocaleString('nl-NL') },
|
||||
{ label: 'Food trends', sub: 'RSS live', icon: '📰', color: '#38bdf8', pct: Math.min(95, ((stats.trending_food || stats.food_market_highlights || []).length * 10)) + '%', val: (stats.trending_food || stats.food_market_highlights || []).length || 0, link: '/marketing' },
|
||||
{ label: 'Goedkeuringen', sub: 'wacht op OK', icon: '✓', color: '#ffd700', pct: '30%', val: stats.pending_approvals || 0 },
|
||||
];
|
||||
var trendCount = (stats.trending_food || stats.food_market_highlights || []).length;
|
||||
switch (id) {
|
||||
case 'pipeline': return '€' + Math.round(stats.pipeline_eur || 0).toLocaleString('nl-NL');
|
||||
case 'clients_active': return (stats.clients_active || 0) + ' / ' + (stats.clients_total || stats.clients || 0);
|
||||
case 'clients_total': return String(stats.clients_total || stats.clients || 0);
|
||||
case 'crm_partnerships': return String(stats.crm_partnerships || 0);
|
||||
case 'supermarkets': return (stats.supermarkets || 0).toLocaleString('nl-NL');
|
||||
case 'wholesalers': return String(stats.wholesalers || 0);
|
||||
case 'trending_food': return String(trendCount || 0);
|
||||
case 'rss_items': return String(stats.rss_items || (stats.rss_live || []).length || 0);
|
||||
case 'rss_bookmarks': return String((stats.rss_bookmarks || []).length || stats.rss_bookmarks_count || 0);
|
||||
case 'pending_approvals': return String(stats.pending_approvals || 0);
|
||||
case 'deals': return String(stats.deals || 0);
|
||||
case 'products': return String(stats.products || 0);
|
||||
case 'suppliers': return String(stats.suppliers || 0);
|
||||
case 'nas_docs': return String(stats.nas_docs || 0);
|
||||
case 'promo_campaigns': return String(stats.promo_campaigns || 0);
|
||||
case 'market_best': return best.symbol ? (best.symbol + ' ' + Number(best.change_pct || 0).toFixed(1) + '%') : '—';
|
||||
default: return '—';
|
||||
}
|
||||
}
|
||||
|
||||
function kpiPct(id, stats) {
|
||||
var summary = stats.market_summary || {};
|
||||
var trendCount = (stats.trending_food || stats.food_market_highlights || []).length;
|
||||
switch (id) {
|
||||
case 'pipeline': return '72%';
|
||||
case 'clients_active':
|
||||
return Math.min(95, Math.round(((stats.clients_active || 0) / Math.max(stats.clients_total || 1, 1)) * 100)) + '%';
|
||||
case 'crm_partnerships': return '45%';
|
||||
case 'supermarkets': return '88%';
|
||||
case 'trending_food': return Math.min(95, trendCount * 10) + '%';
|
||||
case 'rss_items': return Math.min(95, ((stats.rss_items || 0) / 10)) + '%';
|
||||
case 'pending_approvals': return '30%';
|
||||
case 'market_best': return Math.min(95, Math.abs(Number(summary.avg_change_pct || 0)) * 10) + '%';
|
||||
default: return '50%';
|
||||
}
|
||||
}
|
||||
|
||||
function renderKpis(container, stats, selectedKpis) {
|
||||
if (!container) return;
|
||||
var meta = (window.DashboardLayout && window.DashboardLayout.KPI_META) || {};
|
||||
var order = selectedKpis || window._dashboardKpis ||
|
||||
(window.DashboardLayout && window.DashboardLayout.DEFAULT_KPIS) ||
|
||||
['pipeline', 'clients_active', 'crm_partnerships', 'supermarkets', 'trending_food', 'pending_approvals'];
|
||||
var items = order.map(function (id) {
|
||||
var m = meta[id] || { label: id, sub: '', icon: '▪', color: '#94a3b8' };
|
||||
return {
|
||||
label: m.label, sub: m.sub, icon: m.icon, color: m.color || '#94a3b8',
|
||||
pct: kpiPct(id, stats), val: kpiValue(id, stats), link: m.link,
|
||||
};
|
||||
});
|
||||
if (!items.length) {
|
||||
container.innerHTML = '<p class="empty-state">Geen KPI\'s geselecteerd — klik <strong>Dashboard instellen</strong>.</p>';
|
||||
return;
|
||||
}
|
||||
container.className = 'hm-neo-kpi-row';
|
||||
container.innerHTML = items.map(function (it) {
|
||||
var inner = '<div class="hm-neo-kpi"><div class="hm-neo-kpi-top">' +
|
||||
@@ -255,6 +301,35 @@ window.BriefingCharts = (function () {
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderRssFeed(container, stats) {
|
||||
if (!container) return;
|
||||
var items = stats.rss_live || stats.trending_food || stats.food_market_highlights || [];
|
||||
var count = stats.rss_items || items.length;
|
||||
var head = '<div class="hm-rss-head"><span class="hm-live-dot"></span> ' + count + ' items in database' +
|
||||
' · <button type="button" class="btn btn-sm" id="btn-rss-refresh-dash">↻ RSS ophalen</button>' +
|
||||
' · <a href="/marketing">Marketing Hub →</a></div>';
|
||||
if (!items.length) {
|
||||
container.innerHTML = head + '<p class="empty-state">Geen RSS items — klik <strong>RSS ophalen</strong> of ga naar <a href="/marketing">Marketing Hub</a>.</p>';
|
||||
var btn = container.querySelector('#btn-rss-refresh-dash');
|
||||
if (btn) btn.addEventListener('click', function () {
|
||||
if (window._refreshRssDash) window._refreshRssDash();
|
||||
});
|
||||
return;
|
||||
}
|
||||
container.innerHTML = head + '<div class="hm-rss-list">' + items.slice(0, 20).map(function (r) {
|
||||
var when = (r.published_at || '').substring(0, 16).replace('T', ' ');
|
||||
var cat = (r.category || 'feed').toUpperCase();
|
||||
return '<a href="' + (r.link || '#') + '" target="_blank" rel="noopener" class="hm-rss-item">' +
|
||||
'<span class="hm-rss-cat">' + cat + '</span>' +
|
||||
'<span class="hm-rss-body"><strong>' + (r.title || '') + '</strong>' +
|
||||
'<small>' + (r.feed_name || 'RSS') + (when ? ' · ' + when : '') + '</small></span></a>';
|
||||
}).join('') + '</div>';
|
||||
var refreshBtn = container.querySelector('#btn-rss-refresh-dash');
|
||||
if (refreshBtn) refreshBtn.addEventListener('click', function () {
|
||||
if (window._refreshRssDash) window._refreshRssDash();
|
||||
});
|
||||
}
|
||||
|
||||
function renderRetail(container, stats) {
|
||||
if (!container) return;
|
||||
var items = (stats.trending_food || stats.food_market_highlights || []).slice(0, 6);
|
||||
@@ -343,10 +418,12 @@ window.BriefingCharts = (function () {
|
||||
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>';
|
||||
}
|
||||
|
||||
function renderLive(root, stats, vizMode) {
|
||||
function renderLive(root, stats, vizMode, kpiSelection) {
|
||||
if (!root || !stats) return;
|
||||
var mode = vizMode || window._dashboardVizMode || 'neo-bars';
|
||||
renderKpis(document.getElementById('briefing-kpis'), stats);
|
||||
var kpis = kpiSelection || window._dashboardKpis;
|
||||
renderKpis(document.getElementById('briefing-kpis'), stats, kpis);
|
||||
renderRssFeed(document.getElementById('briefing-rss-feed'), stats);
|
||||
renderFoodHighlights(document.getElementById('briefing-food-highlights'), stats);
|
||||
renderExecutiveSummary(document.getElementById('briefing-executive-summary'), stats);
|
||||
renderRetail(document.getElementById('briefing-retail'), stats);
|
||||
@@ -373,5 +450,9 @@ window.BriefingCharts = (function () {
|
||||
renderText(root, content, createdAt);
|
||||
}
|
||||
|
||||
return { render: render, renderLive: renderLive, renderText: renderText, renderExecutiveSummary: renderExecutiveSummary, destroyAll: destroyAll };
|
||||
return {
|
||||
render: render, renderLive: renderLive, renderText: renderText,
|
||||
renderExecutiveSummary: renderExecutiveSummary, renderRssFeed: renderRssFeed,
|
||||
renderKpis: renderKpis, destroyAll: destroyAll,
|
||||
};
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user