Beim Create-Flow wurde selectIncident() aufgerufen, BEVOR der Fall
als refreshend markiert wurde. Dadurch entfernte selectIncident den
'.blurred'-Zustand des Tab-Containers und rief _lockActionsIfFirst(false)
auf — der Fallinhalt war zwischen Oeffnen und Eintreffen der ersten
WebSocket-Statusnachricht kurzzeitig klickbar und unblurred.
Jetzt wird der Refresh-Status und ein Initial-State mit isFirst=true
schon VOR selectIncident gesetzt. selectIncident erkennt isRefreshing
und ruft _showPopupProgress + _lockActionsIfFirst(true) mit dem
bestehenden State auf — Blur und Lock greifen sofort.
Backend:
- GET /{id}/articles paginiert jetzt per limit/offset (Default 500,
Max 1000) und unterstuetzt optionalen search-Parameter (LIKE ueber
headline/source/content). Response-Shape: {total, articles}.
- Neuer Endpunkt GET /{id}/articles/sources-summary liefert pro Quelle
{source, article_count, languages} sowie language_counts gesamt —
serverseitige Aggregation, unabhaengig von Artikel-Paginierung.
- Neuer Endpunkt GET /{id}/articles/timeline-buckets?granularity=hour|day|week|month
aggregiert Artikel + Snapshot-Counts pro Zeitbucket (fuer spaetere
Timeline-Zaehler ueber die volle Historie).
- database.py: Index idx_articles_incident_collected auf
(incident_id, collected_at DESC) fuer schnelleres ORDER BY + Pagination.
Frontend:
- api.js: getArticles({limit, offset, search}),
getArticlesSourcesSummary(), getArticlesTimelineBuckets().
- app.js: loadIncidentDetail laedt erste Seite (500 Artikel), startet
_loadSourcesSummary parallel und zieht restliche Artikel
batchweise (500er Bloecke) im Hintergrund nach, bis _currentArticlesTotal
erreicht ist. rerenderTimeline nach jedem Batch.
- components.js: renderSourceOverviewFromSummary(data) rendert aus
Aggregat-Daten (ersetzt clientseitige Zaehlung ueber geladene Artikel).
Hintergrund: /articles lieferte bei der Iran-Lage 22 MB (17.286 Artikel
mit SELECT *). Die Erstantwort sinkt auf ~650 KB (500 Artikel), weitere
werden progressiv im Hintergrund nachgeladen. Quellenuebersicht zeigt
dank Aggregat-Endpunkt sofort alle Quellen + Sprachen komplett.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend:
- GET /{id}/snapshots liefert nur noch schlanke Shape (Metadaten +
SUBSTR(summary,1,300) AS summary_preview), kein Volltext, kein sources_json.
- Neuer Endpunkt GET /{id}/snapshots/{snapshot_id} fuer Volltext-Lazy-Load.
- Neuer Endpunkt GET /{id}/snapshots/search?q=... fuer serverseitige
Volltextsuche ueber alle Snapshots einer Lage.
Frontend:
- api.js: getSnapshot() und searchSnapshots() ergaenzt.
- app.js: _snapshotFullCache, Volltext wird beim Aufklappen eines
Snapshot-Eintrags per lazyLoadSnapshotDetail() nachgeladen und gecacht.
- Suche ueber Snapshots filtert weiterhin clientseitig ueber summary_preview.
Hintergrund: Bei grossen Lagen (Iran-Lage: 347 Snapshots) fiel die
Snapshots-Listenantwort mit Volltext-Summaries auf ~54 MB. Die Liste
faellt damit auf ~150 KB; Volltexte werden nur on-demand geladen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Der Bullet-Render fuer Live-Monitoring (adhoc) zeigt nun pro Eintrag eine
Karte mit klickbaren Quellen-Pills (Quellname statt nur [N]) im Header und
dezentem Zeitstempel rechts oben. Der Ereignistext steht darunter ohne
Inline-Citations. Bias-Markierung (pro-RU, staatsnah) als kleines Suffix.
Recherchen behalten den bisherigen renderZusammenfassung-Render unveraendert.
- DB-Migration: Spalte latest_developments (TEXT) in incidents
- Analyzer: neuer Prompt LATEST_DEVELOPMENTS_PROMPT_TEMPLATE und
Methode generate_latest_developments() liefert chronologische
Bullet-Liste (max. 8, neueste oben, Zeitstempel DD.MM. HH:MM)
- Orchestrator: nach Analyse+Faktencheck ein Extra-Schritt nur fuer
incident_type=adhoc, der die neue Kachel fortschreibt
- Analyzer-Prompts (Erst- und inkrementell): erzeugen KEINE
Zusammenfassung-Sektion mehr im Lagebild (vermeidet Duplikat mit
der neuen Kachel)
- models.IncidentResponse um latest_developments erweitert
- Frontend: Rendering der Kachel in app.js
Research-Lagen: ZUSAMMENFASSUNG-Sektion wird aus dem Bericht
extrahiert und in eigener Kachel oberhalb des Recherchberichts
angezeigt. Der Recherchebericht zeigt den Rest ohne Dopplung.
- Neue Kachel mit gs-id="zusammenfassung" im GridStack
- Toggle-Button in der Layout-Leiste
- extractZusammenfassung() und renderZusammenfassung() in UI
- Adhoc/Live-Lagen: Kachel wird automatisch ausgeblendet
- Export nutzt weiterhin _extract_zusammenfassung() aus dem Backend
Wenn ein Fall aus der Queue entfernt wird (Cancel, Fehler, Abschluss),
bleiben die #-Nummern der verbleibenden Eintraege jetzt nicht mehr
stecken. _reindexQueuePositions() sortiert nach alter Position und
nummeriert sequentiell neu (#1, #2, ...).
Aufgerufen in: handleRefreshCancelled, handleRefreshError,
handleRefreshComplete.
Nutzer koennen per Klick auf Chips Anweisungen zur Beschreibung
hinzufuegen: Zusammenfassung, Vergleichstabelle, Zeitverlauf,
Pro/Contra oder eigene Tabellen. Format: [TABELLE: ...] und
[ZUSAMMENFASSUNG]. Mehrere Anweisungen moeglich. Analyzer-Prompts
beachten diese Anweisungen verbindlich. Beschreibung-generieren
bewahrt bestehende Direktiven.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Beim Zurueckwechseln auf eine laufende Lage wird der gespeicherte
State (minimized/offen) direkt aus _progressState wiederhergestellt.
War das Popup offen -> offen. War es minimiert -> Mini-Bar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Confirm-Dialog z-index ueber Progress-Popup (10000 > 9000)
2. Progress-Popup wird ausgeblendet waehrend Confirm-Dialog offen
3. Kein dunkler-werdendes Overlay bei mehrfachem Abbrechen-Klick
4. Abbrechen funktioniert jetzt auch fuer Lagen in der Warteschlange
(werden direkt aus der Queue entfernt statt auf Start zu warten)
5. Cancel-Status wird im Popup-Titel angezeigt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
handleRefreshError und handleRefreshCancelled haben den Sidebar-
Refresh-Status (Gold-Rand, Spinner-Text) nicht entfernt. Dadurch
blieben Lagen faeelschlicherweise als laufend markiert.
Jetzt wird bei Error/Cancel: _removeSidebarRefreshStatus() aufgerufen,
_progressState geloescht, und Sidebar neu gerendert.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
API /incidents/refreshing gibt jetzt auch queued IDs mit Position
und den aktuell laufenden Task zurueck.
Frontend nutzt started_at aus der API fuer Timer-Wiederherstellung.
Queued Lagen werden mit korrekter Position wiederhergestellt.
Aktiv laufender Task wird als researching angezeigt statt queued.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Progress-State wird jetzt fuer alle refreshenden Lagen angelegt,
nicht nur fuer die aktuell ausgewaehlte. Sidebar-Update passiert
vor dem Early-Return fuer nicht-aktuelle Lagen.
Bei WebSocket-Reconnect (auch nach Ctrl+Shift+R) wird der State
fuer bereits laufende Refreshes korrekt wiederhergestellt.
Sidebar-Cleanup bei Refresh-Abschluss fuer alle Lagen.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
renderIncidentItem() baut den Refresh-Status (Gold-Rand, Spinner,
Statustext, Warteschlange-Position) direkt ins HTML ein. Ueberlebt
jetzt renderSidebar()-Aufrufe bei Lagenwechsel und Aktualisierungen.
Sidebar wird nach jedem WebSocket-Status-Update neu gerendert,
damit der Status fuer ALLE Lagen immer sichtbar bleibt.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Aktualisierungen starten minimiert (Mini-Bar), Popup nur per Klick.
Verhindert Ueberlagerung von Bearbeiten/Export-Buttons.
2. Erster Durchlauf: Bearbeiten/Export/Archivieren/Loeschen gesperrt,
nur Abbrechen moeglich.
3. Sidebar: Warteschlange-Lagen zeigen Position (#1, #2...) mit
eigenem visuellen Stil (gedimmt, pulsierender Dot).
4. Sidebar-Status (Recherchiert/Analysiert/Faktencheck) wird fuer
ALLE laufenden Lagen angezeigt, nicht nur die aktuelle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ladebalken ersetzt durch zentriertes Popup-Fenster mit Checkbox-Checkliste
(Warteschlange, Recherche, Analyse, Faktencheck) und Echtzeit-Timer.
Erster Durchlauf: Popup nicht wegklickbar, Blur-Effekt auf Kacheln.
Aktualisierung: Popup minimierbar zu kompakter Status-Leiste.
Timer laeuft pro Lage im Hintergrund weiter bei Lagenwechsel.
Gesamtzeit wird am Ende im Abschluss-Popup angezeigt.
Sidebar: Animierter Gold-Rand und Fortschrittstext (Recherchiert/
Analysiert/Faktencheck) unter dem Lage-Namen bei laufendem Refresh.
Zusaetzlicher Cancel-Checkpoint im Orchestrator nach Uebersetzung.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
stopPropagation auf dem Dropdown-Container verhindert, dass der
globale Click-Listener das Menue schliesst wenn man den Org-Switcher
bedient.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ermoeglicht dem Global Admin (is_global_admin Flag) zwischen
Organisationen zu wechseln. Neue Endpoints: GET /api/auth/organizations,
POST /api/auth/switch-org. Org-Dropdown im Header-Menue, nur fuer
Global Admin sichtbar. Komplett herausnehmbar (Flag + Code-Bloecke).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Beschreibungs-Textarea waechst automatisch mit dem Inhalt (min 80px).
Greift beim Tippen, nach Beschreibung generieren und im Edit-Modus.
Emdashes (U+2014) durch Doppelpunkte ersetzt.
Button wird beim Bearbeiten einer Lage korrekt aktiviert (Titel per
JS gesetzt loest kein input-Event aus). Im Create-Modus explizit
disabled nach form.reset().
Hinweistext zur guten Beschreibung erscheint jetzt als Tooltip am
i-Icon neben dem Label, konsistent mit allen anderen Formularfeldern.
Tooltip-Text wechselt weiterhin je nach Typ (Live/Recherche).
KI-gestütztes Prompt Enhancement: Button generiert per Haiku aus dem
Titel eine strukturierte Beschreibung. Unterscheidet zwischen
Live-Monitoring (kompakte Vorfallsbeschreibung) und Recherche
(strukturiertes Briefing mit Schwerpunkten und Suchbegriffen).
- Neuer Endpoint POST /api/incidents/enhance-description
- Button erscheint für beide Lage-Typen, aktiv ab 3 Zeichen Titel
- Info-Hinweis wechselt je nach Typ mit Beispiel
- Spinner-Animation während der Generierung
Bei Klick auf Aktualisieren bleibt der Progress-Indikator aktiv,
auch wenn die Recherche in der Warteschlange landet. Statt einer
Warning-Meldung wird eine Info-Meldung angezeigt. Erneutes Klicken
bestaetigt, dass die Aktualisierung bereits in Bearbeitung ist.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
_applyLayout entfernte Widgets ohne die Card-Elemente vorher zu parken.
Beim Wiederherstellen eines Layouts mit versteckten Kacheln (z.B. Timeline)
gingen die DOM-Elemente verloren, was zu null-Referenz-Fehlern fuehrte.
Fixes:
- layout.js: Cards in tile-parking retten bevor Widget entfernt wird
- app.js: Null-Guards in rerenderTimeline und _updateTimelineCount
Stellt das Original-Verhalten wieder her: Der Typ wird aus den
Auto-Discovery-Daten gelesen, nicht aus dem versteckten Select-Element.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Das readonly Input zeigt dem Nutzer den erkannten Quellentyp (RSS-Feed,
Web-Quelle, Telegram) nach der Auto-Erkennung an. Der hidden Select
dient weiterhin als Datenspeicher für saveSource().
Telegram-Pfad setzt jetzt ebenfalls src-type-display.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
discoverSource() und editSource() referenzierten ein nicht existierendes
DOM-Element src-type-display, was beim Hinzufügen/Bearbeiten von Quellen
den Fehler "Cannot set properties of null (setting value)" auslöste.
src-type-select wird bereits korrekt mit Null-Check gesetzt.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>