Improve CEO cockpit clarity and Revenue visuals, with Hermes briefing.

Tighten overview KPIs/pipeline height, add selectable Revenue views and a resizable sidebar, and keep supporting Hermes/Wereldexport updates.
This commit is contained in:
Aissa
2026-07-30 11:53:43 +00:00
parent 5dd69a1138
commit 62239f0a4e
14 changed files with 1876 additions and 647 deletions
+71 -104
View File
@@ -2,7 +2,7 @@
{% block extra_head %}
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<link rel="stylesheet" href="/static/css/hermes.css" />
<link rel="stylesheet" href="/static/css/herman-dashboard.css?v=23" />
<link rel="stylesheet" href="/static/css/herman-dashboard.css?v=33" />
{% endblock %}
{% block content %}
<div class="herman-shell hm-neo">
@@ -12,7 +12,7 @@
<div>
<p class="hm-hero-eyebrow">Foodlinkk Command Center</p>
<h1>CEO Dashboard <span class="live-badge hm-live-badge">Live</span></h1>
<p class="hero-sub">Cucina B2C · retail · pipeline · Herman briefing</p>
<p class="hero-sub">Foodlinkk B2B · Cucina B2C</p>
</div>
<div class="hm-hero-status" id="hero-live-status">
<span class="hm-live-dot"></span>
@@ -20,14 +20,9 @@
</div>
</div>
<div class="hm-hero-links beurs-mini-row">
<a href="/clients" class="beurs-mini-chip">👥 Klanten</a>
<a href="/deals" class="beurs-mini-chip">💼 Deals</a>
<a href="/packaging" class="beurs-mini-chip">📐 Packaging</a>
<a href="/marketing?tab=publish" class="beurs-mini-chip">🚀 Automatisering</a>
<a href="/marketing?tab=reclame" class="beurs-mini-chip">📁 Reclame folders</a>
<a href="/projects" class="beurs-mini-chip">📁 Projecten</a>
<a href="/retail" class="beurs-mini-chip">🛒 Retail</a>
<a href="/analytics" class="beurs-mini-chip">📊 Analytics</a>
<a href="/clients" class="beurs-mini-chip">Klanten</a>
<a href="/deals" class="beurs-mini-chip">Deals</a>
<a href="/revenue-cockpit" class="beurs-mini-chip">Revenue</a>
</div>
</div>
@@ -49,12 +44,9 @@
<div class="briefing-meta" id="briefing-meta"></div>
</div>
<div class="herman-briefing-actions">
<button type="button" class="btn btn-sm" :class="editLayout ? 'btn-pulse' : ''" @click="toggleEditLayout()" x-text="editLayout ? '✓ Klaar' : '⚙ Dashboard instellen'"></button>
<button type="button" class="btn btn-pulse" @click="refreshLive()" :disabled="loading">↻ Live data</button>
<button type="button" class="btn btn-pulse-green" @click="generate()" :disabled="loading">✨ Genereer dagrapport</button>
<a href="/clients" class="btn btn-pulse-green">CRM Clients</a>
<a href="/marketing?tab=reclame" class="btn btn-pulse-purple">Reclame folders</a>
<a href="/analytics" class="btn">Analytics</a>
<button type="button" class="btn btn-pulse-green" @click="generate()" :disabled="loading">Genereer briefing</button>
<button type="button" class="btn btn-pulse" @click="refreshLive()" :disabled="loading">Live data</button>
<button type="button" class="btn btn-sm" :class="editLayout ? 'btn-pulse' : ''" @click="toggleEditLayout()" x-text="editLayout ? 'Klaar' : 'Indeling'"></button>
</div>
</header>
@@ -76,105 +68,79 @@
</ol>
</div>
<div id="viz-toolbar" class="viz-toolbar"></div>
<div id="viz-toolbar" class="viz-toolbar" x-show="editLayout" x-cloak></div>
<div id="dashboard-customize" class="hm-customize-wrap" x-show="editLayout" x-cloak></div>
<div id="briefing-dashboard" class="hm-dash-layout" :class="editLayout ? 'hm-dash-editing' : ''" x-show="hermanTab === 'overview'">
<div id="briefing-dashboard" class="hm-dash-layout hm-ceo-clean hm-ceo-pro" :class="editLayout ? 'hm-dash-editing' : ''" x-show="hermanTab === 'overview'">
<section class="hm-dash-section hm-dash-full" data-widget="kpis">
<div class="hm-widget-handle" x-show="editLayout">⋮⋮ Sleep · CEO KPI's</div>
<h3 class="hm-section-title"><span class="hm-live-dot"></span> CEO KPI's <small class="hm-widget-hint" x-show="editLayout">— kies KPI's hieronder</small></h3>
<section class="hm-dash-section hm-dash-full hm-ceo-kpis" data-widget="kpis">
<div class="hm-widget-handle" x-show="editLayout">⋮⋮ Sleep · KPI's</div>
<h3 class="hm-section-title"><span class="hm-live-dot"></span> Kerncijfers</h3>
<div id="briefing-kpis" class="hm-neo-kpi-row"></div>
</section>
<section class="hm-dash-section hm-dash-full" data-widget="executive">
<div class="hm-widget-handle" x-show="editLayout">⋮⋮ Sleep · Executive</div>
<h3 class="hm-section-title"><span class="hm-live-dot"></span> Alles op een rij</h3>
<div class="hm-chart-panel">
<div class="hm-chart-head"><h4>Executive samenvatting</h4><small>live database</small></div>
<div class="hm-widget-handle" x-show="editLayout">⋮⋮ Sleep · Overzicht</div>
<h3 class="hm-section-title"><span class="hm-live-dot"></span> Overzicht</h3>
<div class="hm-chart-panel hm-overview-panel">
<div class="hm-chart-head"><h4>Waar staan we</h4><small>live context</small></div>
<div id="briefing-executive-summary"></div>
</div>
<div id="briefing-clients-mini" class="beurs-mini-row" style="margin-top:0.75rem"></div>
</section>
<section class="hm-dash-section hm-dash-full" data-widget="briefing">
<div class="hm-widget-handle" x-show="editLayout">⋮⋮ Sleep · Briefing</div>
<h3 class="hm-section-title">Herman briefing</h3>
<div class="briefing-summary-grid">
<div class="briefing-card summary">
<h4>Samenvatting · live</h4>
<div id="briefing-summary-text" class="live-digest-body"></div>
</div>
<div class="briefing-card hm-activity-card">
<div class="hm-card-head">
<h4>Activiteitenlog</h4>
<a href="/agents" class="hm-card-link">Agents →</a>
</div>
<ul id="briefing-activity-log" class="hm-activity-timeline"></ul>
</div>
<div class="briefing-horizon-col">
<div class="horizon-card short">
<h4>⚡ Korte termijn</h4>
<ul id="briefing-actions-list"></ul>
</div>
<div class="horizon-card long">
<h4>🎯 Lange termijn</h4>
<ul id="briefing-longterm-list"></ul>
</div>
</div>
</div>
</section>
<section class="hm-dash-section" data-widget="retail">
<div class="hm-widget-handle" x-show="editLayout">⋮⋮ Sleep · Retail</div>
<h3 class="hm-section-title">Retail operatie</h3>
<div class="hm-dash-two-col">
<div class="hm-chart-panel">
<div class="hm-chart-head"><h4>Trending food retail</h4><small><a href="/marketing">Marketing Hub →</a></small></div>
<div id="briefing-retail"></div>
<div class="hm-eq"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
</div>
<div class="hm-chart-panel">
<div class="hm-chart-head"><h4>Sales milestones</h4><small>live DB</small></div>
<div id="briefing-milestones"></div>
</div>
</div>
<div class="hm-chart-panel hm-dash-trends-only" style="margin-top:1rem">
<div class="hm-chart-head"><h4>Markt highlights</h4><small>M&A · retail</small></div>
<div id="briefing-food-highlights" class="hm-highlight-grid"></div>
<div class="hm-ceo-brand-grid" style="margin-top:0.9rem">
<article class="hm-ceo-brand-card brand-foodlinkk" id="ceo-b2b">
<header><span class="hm-ceo-brand-icon">🌍</span><h3>Foodlinkk B2B</h3></header>
<ul class="hm-ceo-brand-list"><li class="muted">Laden…</li></ul>
</article>
<article class="hm-ceo-brand-card brand-cucina" id="ceo-b2c">
<header><span class="hm-ceo-brand-icon">🍽️</span><h3>Cucina B2C</h3></header>
<ul class="hm-ceo-brand-list"><li class="muted">Laden…</li></ul>
</article>
</div>
</section>
<section class="hm-dash-section hm-dash-full hm-dash-analytics" data-widget="analytics">
<div class="hm-widget-handle" x-show="editLayout">⋮⋮ Sleep · Analytics</div>
<h3 class="hm-section-title"><span class="hm-live-dot"></span> Data & analytics</h3>
<div class="hm-widget-handle" x-show="editLayout">⋮⋮ Sleep · Pipeline</div>
<div class="hm-neo-charts">
<div class="hm-chart-panel hm-analytics-panel">
<div class="hm-chart-head"><h4>Pipeline</h4><small>live deals</small></div>
<div class="hm-chart-wrap"><canvas id="chart-pipeline"></canvas></div>
<div class="hm-eq"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
</div>
<div class="hm-chart-panel hm-analytics-panel page-viz-panel viz-mode-chart">
<div class="hm-chart-head"><h4>NAS sentiment</h4><small id="viz-label-sentiment">neo-bars</small></div>
<div class="page-viz-body">
<div class="hm-chart-wrap page-viz-canvas"><canvas id="chart-sentiment"></canvas></div>
<div id="viz-sentiment-alt" class="viz-alt"></div>
</div>
</div>
<div class="hm-chart-panel hm-analytics-panel">
<div class="hm-chart-head"><h4>NAS top woorden</h4><small>corpus</small></div>
<div class="hm-chart-wrap"><canvas id="chart-words"></canvas></div>
<div class="hm-eq hm-eq-cyan"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
<div class="hm-chart-head"><h4>Pipeline</h4><small id="ceo-pipeline-sub">revenue + CRM</small></div>
<div class="hm-chart-wrap hm-pipeline-host"><canvas id="chart-pipeline" hidden></canvas></div>
</div>
</div>
<!-- hidden stubs so older renderers don't throw -->
<canvas id="chart-sentiment" hidden></canvas>
<div id="viz-sentiment-alt" hidden></div>
<canvas id="chart-words" hidden></canvas>
</section>
<div class="hub-grid hm-dash-feed" data-widget="feed">
<div class="hm-widget-handle" x-show="editLayout">⋮⋮ Sleep · Feed</div>
<section class="panel hm-dash-full">
<h2>Projecten <a href="/projects" class="btn btn-sm" style="float:right">Alle projecten →</a></h2>
<div id="dash-projects-mini"><p class="muted">Laden…</p></div>
</section>
<section class="hm-dash-section hm-dash-full hm-ceo-mid" data-widget="briefing">
<div class="hm-widget-handle" x-show="editLayout">⋮⋮ Sleep · Briefing</div>
<h3 class="hm-section-title">Herman briefing</h3>
<div class="hm-ceo-mid-grid">
<article class="hm-ceo-panel hm-ceo-focus">
<header><h3>🎯 Focus vandaag</h3></header>
<ul id="ceo-focus-list" class="hm-ceo-action-list"><li class="muted">Laden…</li></ul>
</article>
<article class="hm-ceo-panel hm-ceo-trends">
<header><h3>📈 Trends</h3></header>
<ul id="ceo-trends-list" class="hm-ceo-trend-list"><li class="muted">Laden…</li></ul>
</article>
</div>
<details class="hm-ceo-memo" id="ceo-memo-wrap">
<summary class="hm-ceo-memo-label">Dagmemo <small>klik om te openen</small></summary>
<div id="briefing-summary-text" class="hm-ceo-memo-body"></div>
</details>
</section>
<section class="hm-dash-section hm-dash-full hm-ceo-news" data-widget="news">
<details class="hm-ceo-news-details">
<summary>📰 Laatste nieuws <small>max 5 · <a href="/?tab=rss" @click.prevent="setHermanTab('rss')">alle RSS →</a></small></summary>
<div id="ceo-news-list" class="hm-ceo-news-list"></div>
</details>
</section>
<div class="hub-grid hm-dash-feed" data-widget="feed" hidden>
<div id="dash-projects-mini"></div>
</div>
</div>
@@ -263,8 +229,8 @@
{% endblock %}
{% block scripts %}
<script src="/static/js/viz-engine.js?v=3"></script>
<script src="/static/js/dashboard-layout.js?v=4"></script>
<script src="/static/js/briefing-charts.js?v=19"></script>
<script src="/static/js/dashboard-layout.js?v=11"></script>
<script src="/static/js/briefing-charts.js?v=29"></script>
<script>
localStorage.setItem('foodlinkk-persona', 'ceo');
const INITIAL_BRIEFING = {{ briefing_payload | tojson }};
@@ -282,7 +248,11 @@ function dashboardBriefing() {
_prefs: null, _setDragMode: null, _saveTimer: null,
applyDashboardPrefs(prefs) {
this._prefs = prefs;
window._dashboardKpis = prefs.dashboard_kpis || DashboardLayout.DEFAULT_KPIS;
window._dashboardKpis = (DashboardLayout.ceoKpis ? DashboardLayout.ceoKpis(prefs) : (prefs.dashboard_kpis || DashboardLayout.DEFAULT_KPIS));
if (!localStorage.getItem('rc-kpi-migrated-v2')) {
DashboardLayout.saveDashboardConfig(null, null, window._dashboardKpis);
localStorage.setItem('rc-kpi-migrated-v2', '1');
}
const dash = document.getElementById('briefing-dashboard');
const order = DashboardLayout.visibleLayout(prefs);
DashboardLayout.applyOrder(dash, order);
@@ -359,7 +329,7 @@ function dashboardBriefing() {
this._prefs = prefs;
this._vizMode = prefs.global_viz_mode || 'neo-bars';
window._dashboardVizMode = this._vizMode;
window._dashboardKpis = prefs.dashboard_kpis || DashboardLayout.DEFAULT_KPIS;
window._dashboardKpis = (DashboardLayout.ceoKpis ? DashboardLayout.ceoKpis(prefs) : (prefs.dashboard_kpis || DashboardLayout.DEFAULT_KPIS));
const dash = document.getElementById('briefing-dashboard');
this.applyDashboardPrefs(prefs);
this._setDragMode = DashboardLayout.initDrag(dash, (order) => {
@@ -391,7 +361,8 @@ function dashboardBriefing() {
await this.loadProjectsMini();
if (this.liveDigest) {
BriefingCharts.renderLiveSummary(this.liveDigest, false);
} else if (this.content) {
}
if (this.content) {
BriefingCharts.renderText(dash, this.content, this.createdAt);
}
window._refreshBriefingLive = (t) => this.refreshLive(t);
@@ -405,8 +376,6 @@ function dashboardBriefing() {
await fetch('/api/retail/rss/refresh', { method: 'POST' });
await this.loadRssTab();
if (this._stats) {
BriefingCharts.renderRetail(document.getElementById('briefing-retail'), this._stats);
BriefingCharts.renderFoodHighlights(document.getElementById('briefing-food-highlights'), this._stats);
BriefingCharts.renderKpis(document.getElementById('briefing-kpis'), this._stats, window._dashboardKpis);
}
if (toast) Cockpit.toast('RSS bijgewerkt (' + (this.rssTotal || 0) + ' items)', 'success');
@@ -509,7 +478,6 @@ function dashboardBriefing() {
} catch (e) {}
BriefingCharts.renderLive(document.getElementById('briefing-dashboard'), this._stats, this._vizMode, window._dashboardKpis, this.liveDigest, { pollRefresh: isPoll });
this.renderClientsMini(this._stats);
this.renderActivityLog(this._stats.activity_log || []);
this.updateLiveMeta(r.at, this.liveDigest);
}
if (toast !== false) Cockpit.toast('Live data bijgewerkt', 'success');
@@ -567,7 +535,6 @@ function dashboardBriefing() {
if (result.stats && result.stats.live_digest) this.liveDigest = result.stats.live_digest;
BriefingCharts.render(document.getElementById('briefing-dashboard'), result.stats, result.content, this.createdAt, this._vizMode);
this.renderClientsMini(result.stats || {});
this.renderActivityLog((result.stats || {}).activity_log || []);
document.getElementById('briefing-meta').textContent = 'Rapport: ' + this.createdAt.substring(0, 19).replace('T', ' ') + ' UTC';
Cockpit.toast('Dagrapport klaar!', 'success');
} catch (e) {