feat(dashboard): add status+graph views

This commit is contained in:
2026-05-27 00:11:44 +02:00
parent ec8870ea40
commit e5061b317f
3 changed files with 376 additions and 0 deletions

View File

@@ -27,6 +27,13 @@
</select>
</div>
<!-- Views -->
<div class="view-tabs">
<button class="tab-btn active" id="tabCards" onclick="setView('cards')">Cards</button>
<button class="tab-btn" id="tabGraph" onclick="setView('graph')">Graph</button>
<button class="tab-btn" id="tabStatus" onclick="setView('status')">Status</button>
</div>
<!-- New Engram -->
<div class="new-engram">
<textarea id="newContent" placeholder="Neues Engramm..."></textarea>
@@ -37,6 +44,12 @@
<!-- Cards -->
<div class="cards" id="cards"></div>
<!-- Graph -->
<div class="graph" id="graph" style="display:none;"></div>
<!-- Status -->
<div class="status" id="status" style="display:none;"></div>
<!-- Pagination -->
<div class="pagination" id="pagination">
<button id="btnPrev" onclick="prevPage()"></button>
@@ -67,6 +80,7 @@ let state = {
filter: 'all',
search: '',
autoRefresh: true,
view: 'cards',
};
// ─── Fetch ──────────────────────────────────────────────────────────────────
@@ -84,6 +98,21 @@ async function loadStats() {
document.getElementById('statErrors').textContent = s.errors;
}
function setView(view) {
state.view = view;
document.getElementById('tabCards').classList.toggle('active', view === 'cards');
document.getElementById('tabGraph').classList.toggle('active', view === 'graph');
document.getElementById('tabStatus').classList.toggle('active', view === 'status');
document.getElementById('cards').style.display = view === 'cards' ? '' : 'none';
document.getElementById('pagination').style.display = view === 'cards' ? '' : 'none';
document.getElementById('graph').style.display = view === 'graph' ? '' : 'none';
document.getElementById('status').style.display = view === 'status' ? '' : 'none';
if (view === 'graph') loadGraph();
if (view === 'status') loadStatus();
}
async function loadCards() {
let url = `/api/engrams?limit=${state.limit}&offset=${state.offset}`;
if (state.search) url += `&search=${encodeURIComponent(state.search)}`;
@@ -99,6 +128,70 @@ async function loadCards() {
document.getElementById('btnNext').disabled = data.items.length < state.limit;
}
async function loadStatus() {
const [cfg, db, jobs, ins] = await Promise.all([
api('/api/config'),
api('/api/db_info'),
api('/api/jobs'),
api('/api/insights?limit=8'),
]);
const el = document.getElementById('status');
const jobsHtml = (jobs.items || []).map(j => `
<div class="kv-row">
<div class="kv-key">${j.unit}</div>
<div class="kv-val">${j.error ? ('ERR: ' + j.error) : (j.active + '/' + j.sub)}</div>
</div>
`).join('');
const topTags = (ins.top_tags || []).map(t => `<span class="pill">${t.key} (${t.count})</span>`).join(' ');
const topHosts = (ins.top_hosts || []).map(t => `<span class="pill">${t.key} (${t.count})</span>`).join(' ');
el.innerHTML = `
<div class="panel">
<div class="panel-title">Config</div>
<div class="kv-row"><div class="kv-key">workspace</div><div class="kv-val">${cfg.workspace}</div></div>
<div class="kv-row"><div class="kv-key">db</div><div class="kv-val">${db.db_path}</div></div>
<div class="kv-row"><div class="kv-key">db mtime</div><div class="kv-val">${new Date(db.mtime).toLocaleString()}</div></div>
</div>
<div class="panel">
<div class="panel-title">Jobs</div>
${jobsHtml || '<div class="muted">Keine Daten</div>'}
</div>
<div class="panel">
<div class="panel-title">Insights</div>
<div class="kv-row"><div class="kv-key">pending</div><div class="kv-val">${ins.pending}</div></div>
<div class="kv-row"><div class="kv-key">top tags</div><div class="kv-val">${topTags || '-'}</div></div>
<div class="kv-row"><div class="kv-key">top hosts</div><div class="kv-val">${topHosts || '-'}</div></div>
</div>
`;
}
async function loadGraph() {
const g = await api('/api/graph?limit_nodes=250');
const nodes = g.nodes || [];
const edges = g.edges || [];
const countByKind = nodes.reduce((acc, n) => {
acc[n.kind] = (acc[n.kind] || 0) + 1;
return acc;
}, {});
const el = document.getElementById('graph');
el.innerHTML = `
<div class="panel">
<div class="panel-title">Graph (lightweight)</div>
<div class="kv-row"><div class="kv-key">nodes</div><div class="kv-val">${nodes.length} (${Object.entries(countByKind).map(([k,v])=>k+':'+v).join(', ')})</div></div>
<div class="kv-row"><div class="kv-key">edges</div><div class="kv-val">${edges.length}</div></div>
<div class="muted">Aktuell: Tag/Host Knoten + Engram-Links. Nächster Schritt: echte Tool↔API↔Endpoint Ontologie.</div>
</div>
<div class="panel">
<div class="panel-title">Beispiele</div>
<div class="muted">Tip: öffne ein Engram-Detail und folge Links.</div>
</div>
`;
}
function renderCards() {
const el = document.getElementById('cards');
el.innerHTML = state.items.map(item => `