diff --git a/src/fgai/dashboard.py b/src/fgai/dashboard.py
index 5ca2682..35e5393 100644
--- a/src/fgai/dashboard.py
+++ b/src/fgai/dashboard.py
@@ -276,9 +276,17 @@ function drawCorrelationGraph(correlations) {
});
ctx.textAlign='left'; ctx.fillStyle='#91abc4'; ctx.font='12px Arial'; ctx.fillText('Top correlated entities and streams. Red links/entities include security-event activity; circle size follows event volume.', 8, ch-14);
}
+function showRefreshError(error) {
+ const message = error && error.stack ? error.stack : String(error || 'unknown refresh error');
+ console.error(error);
+ document.getElementById('stamp').textContent = 'Dashboard render error';
+ document.getElementById('liveStatus').innerHTML = `Dashboard render error:
${esc(message)}`;
+}
async function refresh() {
+ try {
const openDetails = new Set([...document.querySelectorAll('details[open][data-detail-id]')].map(item => item.dataset.detailId));
const res = await fetch('/api/status', {cache: 'no-store'});
+ if (!res.ok) throw new Error(`GET /api/status failed: ${res.status} ${res.statusText}`);
const data = await res.json();
const s = data.summary || {};
const a = data.anomaly_summary || {};
@@ -484,6 +492,9 @@ async function refresh() {
document.querySelectorAll('details[data-detail-id]').forEach(item => { if (openDetails.has(item.dataset.detailId)) item.open = true; });
document.querySelectorAll('[data-sort-table]').forEach(button => button.addEventListener('click', () => { const current=tableSort[button.dataset.sortTable]; tableSort[button.dataset.sortTable]={key:button.dataset.sortKey,direction:current && current.key===button.dataset.sortKey ? -current.direction : 1}; refresh(); }));
document.querySelectorAll('.apply-suggested-profile').forEach(button => button.addEventListener('click', () => applySuggestedProfile(button.dataset.streamId)));
+ } catch (error) {
+ showRefreshError(error);
+ }
}
async function loadSettings() {
const config = await (await fetch('/api/config', {cache: 'no-store'})).json();