5d60d33db1
Volledige Foodlinkk Command Center uitbreiding met social automatisering, reclamefolder filters, Proxmox monitoring en documentatie.
213 lines
8.3 KiB
JavaScript
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 };
|
|
})();
|