Files
Aissa 5d60d33db1 Platform bundle: marketing publish, IT ops, packaging, agents mesh.
Volledige Foodlinkk Command Center uitbreiding met social automatisering,
reclamefolder filters, Proxmox monitoring en documentatie.
2026-06-09 00:41:27 +00:00

213 lines
8.3 KiB
JavaScript

window.BriefingCharts = (function () {
var charts = {};
function destroyAll() {
Object.keys(charts).forEach(function (k) {
if (charts[k]) { charts[k].destroy(); charts[k] = null; }
});
}
function parseContent(content) {
var parts = (content || '').split(/\n---\n/);
var ai = parts[0] || '';
var summary = '';
var actions = [];
var sm = ai.match(/##\s*Samenvatting\s*\n([\s\S]*?)(?=##\s*Actiepunten|$)/i);
if (sm) summary = sm[1].trim();
var am = ai.match(/##\s*Actiepunten[^\n]*\n([\s\S]*)/i);
if (am) {
actions = am[1].split('\n').map(function (l) { return l.replace(/^[-*]\s*/, '').trim(); }).filter(Boolean);
}
if (!summary && ai.trim()) summary = ai.trim().slice(0, 600);
return { summary: summary, actions: actions };
}
function chartColors() {
return {
gold: 'rgba(232, 168, 56, 0.85)',
goldDim: 'rgba(232, 168, 56, 0.35)',
cyan: 'rgba(56, 189, 248, 0.85)',
green: 'rgba(62, 207, 142, 0.85)',
red: 'rgba(239, 68, 68, 0.85)',
gray: 'rgba(107, 114, 128, 0.85)',
grid: 'rgba(148, 163, 184, 0.12)',
text: '#94a3b8',
};
}
function renderPipeline(canvas, stats) {
if (!canvas) return;
var rows = stats.deals_by_stage || [];
var c = chartColors();
charts.pipeline = new Chart(canvas, {
type: 'bar',
data: {
labels: rows.map(function (r) { return r.stage || '?'; }),
datasets: [{
label: 'Pipeline EUR',
data: rows.map(function (r) { return Number(r.total) || 0; }),
backgroundColor: c.gold,
borderRadius: 6,
}],
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: { legend: { display: false }, title: { display: true, text: 'Pipeline per stage', color: c.text } },
scales: {
y: { ticks: { color: c.text, callback: function (v) { return '€' + v.toLocaleString('nl-NL'); } }, grid: { color: c.grid } },
x: { ticks: { color: c.text }, grid: { display: false } },
},
},
});
}
function renderSentiment(canvas, stats) {
if (!canvas) return;
var files = stats.nas_files || [];
var counts = { positive: 0, neutral: 0, negative: 0 };
files.forEach(function (f) {
var s = (f.sentiment_label || 'neutral').toLowerCase();
if (counts[s] !== undefined) counts[s]++;
});
if (!files.length) { counts.neutral = 1; }
var c = chartColors();
charts.sentiment = new Chart(canvas, {
type: 'doughnut',
data: {
labels: ['Positief', 'Neutraal', 'Negatief'],
datasets: [{ data: [counts.positive, counts.neutral, counts.negative], backgroundColor: [c.green, c.gray, c.red], borderWidth: 0 }],
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: { legend: { position: 'bottom', labels: { color: c.text } }, title: { display: true, text: 'NAS sentiment', color: c.text } },
},
});
}
function renderWords(canvas, stats) {
if (!canvas) return;
var rows = (stats.top_words || []).slice(0, 8);
var c = chartColors();
charts.words = new Chart(canvas, {
type: 'bar',
data: {
labels: rows.map(function (r) { return r.lemma; }),
datasets: [{
label: 'Count',
data: rows.map(function (r) { return Number(r.total) || 0; }),
backgroundColor: c.cyan,
borderRadius: 6,
}],
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: true,
plugins: { legend: { display: false }, title: { display: true, text: 'Top woorden NAS', color: c.text } },
scales: {
x: { ticks: { color: c.text }, grid: { color: c.grid } },
y: { ticks: { color: c.text }, grid: { display: false } },
},
},
});
}
function renderAgents(canvas, stats) {
if (!canvas) return;
var events = stats.recent_events || [];
var map = {};
events.forEach(function (e) {
var a = e.agent_name || 'other';
map[a] = (map[a] || 0) + 1;
});
var labels = Object.keys(map);
var values = labels.map(function (k) { return map[k]; });
var c = chartColors();
charts.agents = new Chart(canvas, {
type: 'polarArea',
data: {
labels: labels,
datasets: [{ data: values, backgroundColor: [c.gold, c.cyan, c.green, c.red, c.gray, c.goldDim] }],
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: { legend: { position: 'bottom', labels: { color: c.text } }, title: { display: true, text: 'Agent activiteit', color: c.text } },
scales: { r: { ticks: { display: false }, grid: { color: c.grid } } },
},
});
}
function renderKpis(container, stats) {
if (!container) return;
var items = [
{ label: 'Pipeline', value: '€' + Math.round(stats.pipeline_eur || 0).toLocaleString('nl-NL'), cls: 'green' },
{ label: 'Klanten', value: stats.clients || 0, cls: 'purple' },
{ label: 'Deals', value: stats.deals || 0, cls: '' },
{ label: 'NAS docs', value: stats.nas_docs || 0, cls: 'cyan' },
{ label: 'Goedkeuringen', value: stats.pending_approvals || 0, cls: 'amber' },
];
container.innerHTML = items.map(function (it) {
return '<div class="briefing-stat ' + it.cls + '"><span class="briefing-stat-label">' + it.label + '</span><span class="briefing-stat-value">' + it.value + '</span></div>';
}).join('');
}
function renderNasList(container, stats) {
if (!container) return;
var files = stats.nas_files || [];
if (!files.length) { container.innerHTML = '<p class="empty-state">Geen NAS documenten.</p>'; return; }
container.innerHTML = '<table class="data-table"><thead><tr><th>Bestand</th><th>Type</th><th>Sentiment</th><th>Woorden</th></tr></thead><tbody>' +
files.map(function (f) {
var badge = 'badge-' + (f.sentiment_label || 'neutral');
return '<tr><td>' + (f.filename || '') + '</td><td>' + (f.doc_type || '') + '</td><td><span class="badge ' + badge + '">' + (f.sentiment_label || '') + '</span></td><td>' + (f.word_count || '—') + '</td></tr>';
}).join('') + '</tbody></table>';
}
function renderPending(container, stats) {
if (!container) return;
var items = stats.pending_items || [];
if (!items.length) { container.innerHTML = '<p class="empty-state">Geen openstaande goedkeuringen 🎉</p>'; return; }
container.innerHTML = items.map(function (it) {
return '<div class="briefing-pending-item"><span class="badge badge-agent-' + (it.agent_name || '').toLowerCase() + '">' + (it.agent_name || '') + '</span> ' + (it.title || it.event_type || '') + '</div>';
}).join('');
}
function render(root, stats, content, createdAt) {
if (!root || !stats) return;
destroyAll();
var parsed = parseContent(content);
root.style.display = 'block';
var empty = document.getElementById('briefing-empty');
if (empty) empty.style.display = 'none';
var meta = document.getElementById('briefing-meta');
if (meta) meta.textContent = createdAt ? ('Laatst bijgewerkt: ' + String(createdAt).substring(0, 19).replace('T', ' ')) : '';
var sumEl = document.getElementById('briefing-summary-text');
if (sumEl) sumEl.textContent = parsed.summary || (content ? 'Geen samenvatting.' : 'Klik op «Genereer dagrapport» voor een AI-samenvatting en actiepunten.');
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>Geen actiepunten — genereer opnieuw.</li>';
}
renderKpis(document.getElementById('briefing-kpis'), stats);
renderPipeline(document.getElementById('chart-pipeline'), stats);
renderSentiment(document.getElementById('chart-sentiment'), stats);
renderWords(document.getElementById('chart-words'), stats);
renderAgents(document.getElementById('chart-agents'), stats);
renderNasList(document.getElementById('briefing-nas-list'), stats);
renderPending(document.getElementById('briefing-pending-list'), stats);
var full = document.getElementById('briefing-full-text');
if (full) full.textContent = content || '';
}
return { render: render, destroyAll: destroyAll, parseContent: parseContent };
})();