Dashboard: GridStack durch Tab-Navigation ersetzen
Der Monitor-Dashboard zeigte bisher alle sechs Kacheln gleichzeitig in einem GridStack-Layout (Drag/Resize, je Kachel eigenes Scrolling). Nutzer- wunsch: Analog zur Lagebild-Seite nur ein Tab-Panel gleichzeitig, maximiert auf volle Breite, Seiten-Scroll statt interne Scrollbars. Aenderungen: - dashboard.html: Layout-Toolbar + grid-stack-Wrapper entfernt; neue tab-nav mit 6 Buttons + tab-panels mit 6 Panels. GridStack CDN-Links raus. - layout.js: GridStack-Init/toggleTile/reset komplett entfernt. Neu: switchTab(tabId) + restoreTabFor(incidentId) mit localStorage-Persistenz pro Lage osint_tab_id. applyTypeLabels fuer adhoc vs. research. Legacy- Methoden sind No-Op-Stubs. - app.js: renderIncidentDetail ruft LayoutManager.restoreTabFor und applyTypeLabels auf. openContentModal-Trigger aus Card-Titeln raus. Tile-Resize-Bloecke fuer Quellen und Timeline entfernt. - components.js: Telegram-Pills bekommen Suffix Telegram-Link, wenn die URL auf t.me verweist. - style.css: grid-stack/layout-toggle Klassen raus; neue tab-nav/tab-btn/ tab-panel Klassen. Internes Scrolling entfernt. map-container 600px. Alte osint_layout-Eintraege werden ignoriert.
Dieser Commit ist enthalten in:
@@ -716,7 +716,7 @@ const App = {
|
||||
|
||||
// GridStack-Animation deaktivieren und Scroll komplett sperren
|
||||
// bis alle Tile-Resize-Operationen (doppeltes rAF) abgeschlossen sind
|
||||
var gridEl = document.querySelector('.grid-stack');
|
||||
var gridEl = document.querySelector('.tab-panels');
|
||||
if (gridEl) gridEl.classList.remove('grid-stack-animate');
|
||||
var scrollLock = function() { mc.scrollTop = 0; };
|
||||
mc.addEventListener('scroll', scrollLock);
|
||||
@@ -732,7 +732,7 @@ const App = {
|
||||
if (prevOverlay) prevOverlay.style.display = 'none';
|
||||
const prevMini = document.getElementById('progress-mini');
|
||||
if (prevMini) prevMini.style.display = 'none';
|
||||
const grid = document.querySelector('.grid-stack');
|
||||
const grid = document.querySelector('.tab-panels');
|
||||
if (grid) grid.classList.remove('blurred');
|
||||
|
||||
if (isRefreshing) {
|
||||
@@ -825,10 +825,11 @@ const App = {
|
||||
|
||||
// Kachel-Label: 'Recherchebericht' fuer Recherche-Lagen, 'Lagebild' fuer Live-Monitoring
|
||||
const _lbLabel = incident.type === 'research' ? 'Recherchebericht' : 'Lagebild';
|
||||
const _cardTitle = document.querySelector('[gs-id="lagebild"] .card-title');
|
||||
if (_cardTitle) { _cardTitle.textContent = _lbLabel; _cardTitle.setAttribute("onclick", "openContentModal('" + _lbLabel + "', 'summary-content')"); }
|
||||
const _toggleBtn = document.querySelector('.layout-toggle-btn[data-tile="lagebild"]');
|
||||
if (_toggleBtn) _toggleBtn.textContent = _lbLabel;
|
||||
const _cardTitle = document.querySelector('#panel-lagebild .card-title');
|
||||
if (_cardTitle) _cardTitle.textContent = _lbLabel;
|
||||
if (typeof LayoutManager !== 'undefined' && typeof LayoutManager.applyTypeLabels === 'function') {
|
||||
LayoutManager.applyTypeLabels(incident.type);
|
||||
}
|
||||
{ const _nt = document.querySelector("#inc-notify-summary"); if (_nt) { const _ns = _nt.closest("label")?.querySelector(".toggle-text"); if (_ns) _ns.textContent = "Neues " + _lbLabel; } }
|
||||
|
||||
// Archiv-Button Text
|
||||
@@ -855,7 +856,6 @@ const App = {
|
||||
if (incident.type === 'research') {
|
||||
// Recherche: ZUSAMMENFASSUNG-Sektion aus Briefing extrahieren
|
||||
if (zusammenfassungTitle) zusammenfassungTitle.textContent = 'Zusammenfassung';
|
||||
if (zusammenfassungTitle) zusammenfassungTitle.setAttribute('onclick', "openContentModal('Zusammenfassung', 'zusammenfassung-content')");
|
||||
if (incident.summary) {
|
||||
const { zusammenfassung, remaining } = UI.extractZusammenfassung(incident.summary);
|
||||
if (zusammenfassung) {
|
||||
@@ -874,7 +874,6 @@ const App = {
|
||||
} else {
|
||||
// Live-Monitoring (adhoc): Kachel zeigt "Neueste Entwicklungen" (max 8 Bullets mit Zeitstempel)
|
||||
if (zusammenfassungTitle) zusammenfassungTitle.textContent = 'Neueste Entwicklungen';
|
||||
if (zusammenfassungTitle) zusammenfassungTitle.setAttribute('onclick', "openContentModal('Neueste Entwicklungen', 'zusammenfassung-content')");
|
||||
if (zusammenfassungCard) zusammenfassungCard.style.display = '';
|
||||
const devText = (incident.latest_developments || '').trim();
|
||||
if (devText) {
|
||||
@@ -949,30 +948,18 @@ const App = {
|
||||
const _soSources = new Set(articles.map(a => a.source).filter(Boolean));
|
||||
_soStats.textContent = articles.length + " Artikel aus " + _soSources.size + " Quellen";
|
||||
}
|
||||
// Kachel an Inhalt anpassen
|
||||
if (typeof LayoutManager !== 'undefined' && LayoutManager._grid) {
|
||||
if (sourceOverview.style.display !== 'none') {
|
||||
// Offen → an Inhalt anpassen
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => {
|
||||
LayoutManager.resizeTileToContent('quellen');
|
||||
}));
|
||||
} else {
|
||||
// Geschlossen → einheitliche Default-Höhe
|
||||
const defaults = LayoutManager.DEFAULT_LAYOUT.find(d => d.id === 'quellen');
|
||||
if (defaults) {
|
||||
const node = LayoutManager._grid.engine.nodes.find(
|
||||
n => n.el && n.el.getAttribute('gs-id') === 'quellen'
|
||||
);
|
||||
if (node) LayoutManager._grid.update(node.el, { h: defaults.h });
|
||||
}
|
||||
}
|
||||
}
|
||||
// Im Tab-Modus wird die Kachel vom Seiten-Layout bestimmt — kein Resize noetig
|
||||
}
|
||||
|
||||
// Timeline - Artikel + Snapshots zwischenspeichern und rendern
|
||||
this._currentArticles = articles;
|
||||
this._currentSnapshots = snapshots || [];
|
||||
this._currentIncidentType = incident.type;
|
||||
|
||||
// Tab-Auswahl: gemerkt pro Lage (localStorage), Default = erster Tab
|
||||
if (typeof LayoutManager !== 'undefined' && typeof LayoutManager.restoreTabFor === 'function') {
|
||||
LayoutManager.restoreTabFor(incident.id);
|
||||
}
|
||||
this._timelineFilter = 'all';
|
||||
this._timelineRange = 'all';
|
||||
this._activePointIndex = null;
|
||||
@@ -1360,35 +1347,15 @@ const App = {
|
||||
},
|
||||
|
||||
_resizeTimelineTile() {
|
||||
if (typeof LayoutManager === 'undefined' || !LayoutManager._grid) return;
|
||||
// Tab-Modus: Kein internes Resize noetig, Panel waechst mit Inhalt.
|
||||
// Wir scrollen lediglich ein offenes Detail in den sichtbaren Bereich.
|
||||
requestAnimationFrame(() => { requestAnimationFrame(() => {
|
||||
// Prüfen ob Detail-Panel oder expandierter Eintrag offen ist
|
||||
const hasDetail = document.querySelector('.ht-detail-panel') !== null;
|
||||
const hasExpanded = document.querySelector('.timeline-card .vt-entry.expanded') !== null;
|
||||
|
||||
if (hasDetail || hasExpanded) {
|
||||
LayoutManager.resizeTileToContent('timeline');
|
||||
} else {
|
||||
// Zurück auf Default-Höhe
|
||||
const defaults = LayoutManager.DEFAULT_LAYOUT.find(d => d.id === 'timeline');
|
||||
if (defaults) {
|
||||
const node = LayoutManager._grid.engine.nodes.find(
|
||||
n => n.el && n.el.getAttribute('gs-id') === 'timeline'
|
||||
);
|
||||
if (node) {
|
||||
LayoutManager._grid.update(node.el, { h: defaults.h });
|
||||
LayoutManager._debouncedSave();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Scroll in Sicht
|
||||
const card = document.querySelector('.timeline-card');
|
||||
const main = document.querySelector('.main-content');
|
||||
if (!card || !main) return;
|
||||
if (!card) return;
|
||||
const cardBottom = card.getBoundingClientRect().bottom;
|
||||
const mainBottom = main.getBoundingClientRect().bottom;
|
||||
if (cardBottom > mainBottom) {
|
||||
main.scrollBy({ top: cardBottom - mainBottom + 16, behavior: document.documentElement.dataset.a11yMotion ? 'instant' : 'smooth' });
|
||||
const viewBottom = window.innerHeight;
|
||||
if (cardBottom > viewBottom) {
|
||||
window.scrollBy({ top: cardBottom - viewBottom + 16, behavior: document.documentElement.dataset.a11yMotion ? 'instant' : 'smooth' });
|
||||
}
|
||||
}); });
|
||||
},
|
||||
@@ -2669,27 +2636,7 @@ async handleRefresh() {
|
||||
// aria-expanded auf dem Header-Toggle synchronisieren
|
||||
const header = chevron ? chevron.closest('[role="button"]') : null;
|
||||
if (header) header.setAttribute('aria-expanded', String(isHidden));
|
||||
// gridstack-Kachel an Inhalt anpassen (doppelter rAF für vollständiges Layout)
|
||||
if (typeof LayoutManager !== 'undefined' && LayoutManager._grid) {
|
||||
if (isHidden) {
|
||||
// Aufgeklappt → Inhalt muss erst layouten
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => {
|
||||
LayoutManager.resizeTileToContent('quellen');
|
||||
}));
|
||||
} else {
|
||||
// Zugeklappt → auf Default-Höhe zurück
|
||||
const defaults = LayoutManager.DEFAULT_LAYOUT.find(d => d.id === 'quellen');
|
||||
if (defaults) {
|
||||
const node = LayoutManager._grid.engine.nodes.find(
|
||||
n => n.el && n.el.getAttribute('gs-id') === 'quellen'
|
||||
);
|
||||
if (node) {
|
||||
LayoutManager._grid.update(node.el, { h: defaults.h });
|
||||
LayoutManager._debouncedSave();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Tab-Modus: Panel waechst mit Inhalt, kein Resize noetig
|
||||
},
|
||||
|
||||
toggleGroup(domain) {
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren