Polish CEO dashboard UI with fixed live dock and cleaner layout.
Remove page scroll hijacking during briefing generation, replace nested scroll panels with timeline and grid layouts, and refine the command bar and hero for a more professional feel.
This commit is contained in:
@@ -2,15 +2,24 @@
|
||||
{% 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=21" />
|
||||
<link rel="stylesheet" href="/static/css/herman-dashboard.css?v=22" />
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="herman-shell hm-neo">
|
||||
|
||||
<div class="herman-hero">
|
||||
<h1>CEO Dashboard · Cucina <span class="live-badge">Live</span></h1>
|
||||
<p class="hero-sub">Business overzicht · retail · deals · briefing · marketing</p>
|
||||
<div class="hm-hero-links beurs-mini-row" style="margin-top:0.85rem">
|
||||
<div class="herman-hero hm-hero-pro">
|
||||
<div class="hm-hero-top">
|
||||
<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>
|
||||
</div>
|
||||
<div class="hm-hero-status" id="hero-live-status">
|
||||
<span class="hm-live-dot"></span>
|
||||
<span id="briefing-meta-hero">Live data laden…</span>
|
||||
</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>
|
||||
@@ -34,7 +43,7 @@
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<header class="herman-briefing-header">
|
||||
<header class="herman-briefing-header hm-command-bar">
|
||||
<div>
|
||||
<h2>Dagelijkse briefing</h2>
|
||||
<div class="briefing-meta" id="briefing-meta"></div>
|
||||
@@ -49,14 +58,14 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="hm-live-bar" class="hm-live-bar" x-show="loading || liveLog.length" x-cloak :class="loading ? 'hm-live-bar-active' : 'hm-live-bar-done'">
|
||||
<div class="hm-live-bar-head">
|
||||
<div id="hm-live-bar" class="hm-live-dock" x-show="loading || liveLog.length" x-cloak :class="loading ? 'hm-live-dock-active' : 'hm-live-dock-done'">
|
||||
<div class="hm-live-dock-head">
|
||||
<span class="hm-live-dot"></span>
|
||||
<strong class="hm-live-title" x-text="loading ? loadingMsg : 'Dagrapport voltooid'"></strong>
|
||||
<span class="hm-live-progress" x-show="loading" x-text="liveProgress"></span>
|
||||
<button type="button" class="btn btn-sm hm-live-close" x-show="!loading && liveLog.length" @click="liveLog = []">✕</button>
|
||||
<button type="button" class="btn btn-sm hm-live-close" x-show="!loading && liveLog.length" @click="liveLog = []" title="Sluiten">✕</button>
|
||||
</div>
|
||||
<ol id="hm-live-log" class="hm-live-log">
|
||||
<ol id="hm-live-log" class="hm-live-log hm-live-log-compact">
|
||||
<template x-for="(entry, idx) in liveLog" :key="idx">
|
||||
<li :class="'hm-live-entry hm-live-' + (entry.status || 'ok') + (entry.type === 'agent_msg' ? ' hm-live-agent' : '')">
|
||||
<span class="hm-live-src" x-text="entry.source || entry.agent"></span>
|
||||
@@ -96,9 +105,12 @@
|
||||
<h4>Samenvatting · live</h4>
|
||||
<div id="briefing-summary-text" class="live-digest-body"></div>
|
||||
</div>
|
||||
<div class="briefing-card" style="max-height:220px;overflow-y:auto">
|
||||
<h4>📋 Herman activiteitenlog</h4>
|
||||
<ul id="briefing-activity-log" class="hm-exec-list" style="margin:0;padding-left:1.1rem;font-size:0.82rem"></ul>
|
||||
<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">
|
||||
@@ -129,7 +141,7 @@
|
||||
</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-scroll"></div>
|
||||
<div id="briefing-food-highlights" class="hm-highlight-grid"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -442,9 +454,12 @@ function dashboardBriefing() {
|
||||
renderActivityLog(entries) {
|
||||
const el = document.getElementById('briefing-activity-log');
|
||||
if (!el) return;
|
||||
el.innerHTML = (entries || []).length
|
||||
? entries.slice(0, 12).map(e => '<li>' + e + '</li>').join('')
|
||||
: '<li class="muted">Nog geen agent-acties vandaag — Herman documenteert automatisch.</li>';
|
||||
const rows = (entries || []).slice(0, 10);
|
||||
el.innerHTML = rows.length
|
||||
? rows.map(function (e, i) {
|
||||
return '<li class="hm-activity-item"><span class="hm-activity-dot"></span><span class="hm-activity-text">' + e + '</span></li>';
|
||||
}).join('')
|
||||
: '<li class="hm-activity-empty">Nog geen agent-acties vandaag — Herman documenteert automatisch.</li>';
|
||||
},
|
||||
renderClientsMini(stats) {
|
||||
const el = document.getElementById('briefing-clients-mini');
|
||||
@@ -458,12 +473,13 @@ function dashboardBriefing() {
|
||||
updateLiveMeta(at, digest) {
|
||||
const tsRaw = at || (digest && digest.updated_at) || '';
|
||||
const ts = String(tsRaw).substring(0, 19).replace('T', ' ');
|
||||
const n = digest && digest.activity_count != null ? digest.activity_count + ' activiteiten' : '';
|
||||
const line = ts ? ('Ververst ' + ts + ' UTC' + (n ? ' · ' + n : '')) : 'Live data laden…';
|
||||
const meta = document.getElementById('briefing-meta');
|
||||
if (meta && ts) {
|
||||
const n = digest && digest.activity_count != null ? ' · ' + digest.activity_count + ' activiteiten' : '';
|
||||
meta.textContent = 'Live · ververst ' + ts + ' UTC' + n;
|
||||
}
|
||||
const badge = document.querySelector('.herman-hero .live-badge');
|
||||
if (meta) meta.textContent = line;
|
||||
const heroMeta = document.getElementById('briefing-meta-hero');
|
||||
if (heroMeta) heroMeta.textContent = line;
|
||||
const badge = document.querySelector('.hm-live-badge');
|
||||
if (badge && tsRaw) {
|
||||
try {
|
||||
badge.textContent = 'Live · ' + new Date(tsRaw).toLocaleTimeString('nl-NL', { hour: '2-digit', minute: '2-digit' });
|
||||
@@ -510,8 +526,6 @@ function dashboardBriefing() {
|
||||
this.$nextTick(() => {
|
||||
const log = document.getElementById('hm-live-log');
|
||||
if (log) log.scrollTop = log.scrollHeight;
|
||||
const bar = document.getElementById('hm-live-bar');
|
||||
if (bar && this.loading) bar.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
});
|
||||
},
|
||||
async generate() {
|
||||
@@ -519,9 +533,6 @@ function dashboardBriefing() {
|
||||
this.liveLog = [];
|
||||
this.liveProgress = '';
|
||||
this.loadingMsg = 'Herman start dagrapport…';
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
const bar = document.getElementById('hm-live-bar');
|
||||
if (bar) bar.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
try {
|
||||
const resp = await fetch('/api/herman/briefing/stream', { method: 'POST' });
|
||||
if (!resp.ok) throw new Error('Stream mislukt (' + resp.status + ')');
|
||||
|
||||
Reference in New Issue
Block a user