Incident-Response: sources_json nur noch via Lazy-Endpunkt, Sidebar schlank
Backend:
- IncidentResponse: sources_json-Feld entfernt (Detail-GET liefert es
nicht mehr mit).
- Neues Schema IncidentListItem fuer GET /incidents (Sidebar):
Ohne summary, ohne sources_json. Ein has_summary-Bit fuer
Erster-Refresh-Erkennung, description bleibt fuer das Edit-Modal.
- list_incidents selektiert nur die noetigen Spalten (kein SELECT *)
— spart bei grossen Lagen Speicher + Serialisierung.
- Neuer Endpunkt GET /incidents/{id}/sources liefert geparstes
Sources-Array fuer Zitate-Lookups (Lazy).
Frontend:
- api.js: getIncidentSources(id).
- app.js: loadIncidentDetail laedt /sources parallel, speichert Array
in _currentSources. Alle renderSummary/Zusammenfassung/
LatestDevelopments-Aufrufe bekommen jetzt _currentSources statt
incident.sources_json. inc.summary-Checks -> inc.has_summary.
- components.js: _parseSources(input) akzeptiert Array ODER String
(Rueckwaertskompatibilitaet). renderZusammenfassung, renderSummary,
renderLatestDevelopments nutzen den Helper.
Hintergrund: Die Sidebar-Liste lieferte bei 17 Lagen 1,23 MB
(Iran allein 386 KB wegen sources_json + summary). Detail-Endpunkt
lieferte sources_json (324 KB bei Iran) bei jedem Oeffnen mit.
Beides jetzt radikal kleiner — die 324 KB Sources gibt's nur
einmalig auf Anfrage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -422,6 +422,7 @@ const App = {
|
||||
_currentArticles: [],
|
||||
_currentSnapshots: [],
|
||||
_snapshotFullCache: new Map(),
|
||||
_currentSources: [],
|
||||
_currentIncidentType: 'adhoc',
|
||||
_sidebarFilter: 'all',
|
||||
_currentUsername: '',
|
||||
@@ -586,7 +587,7 @@ const App = {
|
||||
this._refreshingIncidents.add(id);
|
||||
const d = details[String(id)] || {};
|
||||
const inc = this.incidents.find(i => i.id === id);
|
||||
const isFirst = inc && !inc.summary;
|
||||
const isFirst = inc && !inc.has_summary;
|
||||
const isCurrent = (id === currentTask);
|
||||
// Use 'researching' as default step for the actively running task
|
||||
UI.showProgress(isCurrent ? 'researching' : 'queued', { started_at: d.started_at }, id, isFirst);
|
||||
@@ -598,7 +599,7 @@ const App = {
|
||||
queuedIds.forEach((id, idx) => {
|
||||
this._refreshingIncidents.add(id);
|
||||
const inc = this.incidents.find(i => i.id === id);
|
||||
const isFirst = inc && !inc.summary;
|
||||
const isFirst = inc && !inc.has_summary;
|
||||
UI.showProgress('queued', { queue_position: idx + 1 }, id, isFirst);
|
||||
});
|
||||
}
|
||||
@@ -787,14 +788,18 @@ const App = {
|
||||
|
||||
async loadIncidentDetail(id) {
|
||||
try {
|
||||
const [incident, articlesResponse, factchecks, snapshots, locationsResponse] = await Promise.all([
|
||||
const [incident, articlesResponse, factchecks, snapshots, locationsResponse, sourcesResponse] = await Promise.all([
|
||||
API.getIncident(id),
|
||||
API.getArticles(id, { limit: 500, offset: 0 }),
|
||||
API.getFactChecks(id),
|
||||
API.getSnapshots(id),
|
||||
API.getLocations(id).catch(() => []),
|
||||
API.getIncidentSources(id).catch(() => ({ sources: [] })),
|
||||
]);
|
||||
|
||||
// Sources-Array (ersetzt frueheres incident.sources_json — lazy via /sources-Endpunkt)
|
||||
this._currentSources = (sourcesResponse && sourcesResponse.sources) || [];
|
||||
|
||||
// Articles: neue Shape {total, articles} oder alter nackter Array (Rueckwaertskompatibel)
|
||||
let articles, articlesTotal;
|
||||
if (Array.isArray(articlesResponse)) {
|
||||
@@ -921,13 +926,13 @@ const App = {
|
||||
if (incident.summary) {
|
||||
const { zusammenfassung, remaining } = UI.extractZusammenfassung(incident.summary);
|
||||
if (zusammenfassung) {
|
||||
if (zusammenfassungText) zusammenfassungText.innerHTML = UI.renderZusammenfassung(zusammenfassung, incident.sources_json);
|
||||
if (zusammenfassungText) zusammenfassungText.innerHTML = UI.renderZusammenfassung(zusammenfassung, this._currentSources);
|
||||
if (zusammenfassungCard) zusammenfassungCard.style.display = '';
|
||||
summaryText.innerHTML = UI.renderSummary(remaining, incident.sources_json, incident.type);
|
||||
summaryText.innerHTML = UI.renderSummary(remaining, this._currentSources, incident.type);
|
||||
} else {
|
||||
if (zusammenfassungText) zusammenfassungText.innerHTML = '<span style="color:var(--text-disabled);">Zusammenfassung wird beim n\u00e4chsten Refresh generiert.</span>';
|
||||
if (zusammenfassungCard) zusammenfassungCard.style.display = '';
|
||||
summaryText.innerHTML = UI.renderSummary(incident.summary, incident.sources_json, incident.type);
|
||||
summaryText.innerHTML = UI.renderSummary(incident.summary, this._currentSources, incident.type);
|
||||
}
|
||||
} else {
|
||||
if (zusammenfassungCard) zusammenfassungCard.style.display = 'none';
|
||||
@@ -939,12 +944,12 @@ const App = {
|
||||
if (zusammenfassungCard) zusammenfassungCard.style.display = '';
|
||||
const devText = (incident.latest_developments || '').trim();
|
||||
if (devText) {
|
||||
if (zusammenfassungText) zusammenfassungText.innerHTML = UI.renderLatestDevelopments(devText, incident.sources_json);
|
||||
if (zusammenfassungText) zusammenfassungText.innerHTML = UI.renderLatestDevelopments(devText, this._currentSources);
|
||||
} else if (zusammenfassungText) {
|
||||
zusammenfassungText.innerHTML = '<span style="color:var(--text-disabled);">Noch keine Entwicklungen erfasst. Wird beim n\u00e4chsten Refresh generiert.</span>';
|
||||
}
|
||||
if (incident.summary) {
|
||||
summaryText.innerHTML = UI.renderSummary(incident.summary, incident.sources_json, incident.type);
|
||||
summaryText.innerHTML = UI.renderSummary(incident.summary, this._currentSources, incident.type);
|
||||
} else {
|
||||
summaryText.innerHTML = '<span style="color:var(--text-disabled);">Noch kein Lagebild. Klicke auf "Aktualisieren" um die Recherche zu starten.</span>';
|
||||
}
|
||||
@@ -1833,7 +1838,7 @@ async handleRefresh() {
|
||||
} else {
|
||||
UI.showToast('Aktualisierung gestartet.', 'success');
|
||||
var _inc2 = this.incidents.find(function(i) { return i.id === this.currentIncidentId; }.bind(this));
|
||||
UI.showProgress('queued', {}, this.currentIncidentId, _inc2 && !_inc2.summary);
|
||||
UI.showProgress('queued', {}, this.currentIncidentId, _inc2 && !_inc2.has_summary);
|
||||
}
|
||||
} catch (err) {
|
||||
this._refreshingIncidents.delete(this.currentIncidentId);
|
||||
@@ -2176,7 +2181,7 @@ async handleRefresh() {
|
||||
this._updateSidebarDot(msg.incident_id);
|
||||
// Detect first refresh: no summary means first run
|
||||
const inc = this.incidents.find(i => i.id === msg.incident_id);
|
||||
const isFirst = inc && !inc.summary;
|
||||
const isFirst = inc && !inc.has_summary;
|
||||
// Update progress state for ALL incidents (sidebar + popup if current)
|
||||
UI.showProgress(status, msg.data, msg.incident_id, isFirst);
|
||||
// Re-render sidebar so status is baked into HTML (survives future re-renders)
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren