Lagebild: Faktenchecks als Filter + Akkordeon mit Monitor-Icons
- Stat-Cards klickbar als Filter (Gesamt/Bestätigt/Offen/Widerlegt), zentriert - Kompakte Akkordeon-Zeilen statt großer Karten - Icons vom echten Monitor (✓ ✗ ? ↻ ⚠) als farbige Quadrate - Klick auf Zeile klappt Detail auf (Evidenz + Statusverlauf) - Nur eine Zeile gleichzeitig offen - Gold-Punkt bei Einträgen mit Statusverlauf Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -695,27 +695,57 @@ var Lagebild = {
|
||||
},
|
||||
|
||||
/* ===== TAB: FAKTENCHECKS ===== */
|
||||
/* ===== Factcheck Icons (from real Monitor) ===== */
|
||||
fcIcons: {
|
||||
confirmed: '✓',
|
||||
unconfirmed: '?',
|
||||
contradicted: '✗',
|
||||
developing: '↻',
|
||||
established: '✓',
|
||||
disputed: '⚠',
|
||||
'false': '✗',
|
||||
unverified: '?'
|
||||
},
|
||||
|
||||
fcLabels: {
|
||||
confirmed: 'Bestätigt',
|
||||
unconfirmed: 'Unbestätigt',
|
||||
contradicted: 'Widerlegt',
|
||||
developing: 'Unklar',
|
||||
established: 'Gesichert',
|
||||
disputed: 'Umstritten',
|
||||
'false': 'Falsch',
|
||||
unverified: 'Nicht verifiziert'
|
||||
},
|
||||
|
||||
renderFactChecksTab: function() {
|
||||
var checks = this.currentView.fact_checks || [];
|
||||
if (!checks.length) {
|
||||
document.getElementById('factchecks-content').innerHTML = '<p style="color:#8896AB">Keine Faktenchecks verf\u00fcgbar.</p>';
|
||||
document.getElementById('factchecks-content').innerHTML = '<p style="color:#8896AB">Keine Faktenchecks verfügbar.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Count stats
|
||||
var stats = { confirmed: 0, unconfirmed: 0, contradicted: 0, developing: 0, established: 0, disputed: 0 };
|
||||
for (var k = 0; k < checks.length; k++) {
|
||||
var st = checks[k].status || 'developing';
|
||||
if (stats[st] !== undefined) stats[st]++;
|
||||
}
|
||||
|
||||
var confirmedTotal = stats.confirmed + stats.established;
|
||||
var openTotal = stats.unconfirmed + stats.developing;
|
||||
var contradictedTotal = stats.contradicted + stats.disputed;
|
||||
|
||||
// Stat cards (clickable filters)
|
||||
var h = '<div class="fc-stats">';
|
||||
h += '<div class="fc-stat"><span class="fc-stat-num">' + checks.length + '</span><span class="fc-stat-label">Gesamt</span></div>';
|
||||
h += '<div class="fc-stat confirmed"><span class="fc-stat-num">' + (stats.confirmed + stats.established) + '</span><span class="fc-stat-label">Best\u00e4tigt</span></div>';
|
||||
h += '<div class="fc-stat unconfirmed"><span class="fc-stat-num">' + (stats.unconfirmed + stats.developing) + '</span><span class="fc-stat-label">Offen</span></div>';
|
||||
if (stats.contradicted + stats.disputed > 0)
|
||||
h += '<div class="fc-stat contradicted"><span class="fc-stat-num">' + (stats.contradicted + stats.disputed) + '</span><span class="fc-stat-label">Widerlegt</span></div>';
|
||||
h += '<button class="fc-stat active" data-filter="all"><span class="fc-stat-num">' + checks.length + '</span><span class="fc-stat-label">Gesamt</span></button>';
|
||||
h += '<button class="fc-stat confirmed" data-filter="confirmed"><span class="fc-stat-num">' + confirmedTotal + '</span><span class="fc-stat-label">Bestätigt</span></button>';
|
||||
h += '<button class="fc-stat unconfirmed" data-filter="unconfirmed"><span class="fc-stat-num">' + openTotal + '</span><span class="fc-stat-label">Offen</span></button>';
|
||||
if (contradictedTotal > 0)
|
||||
h += '<button class="fc-stat contradicted" data-filter="contradicted"><span class="fc-stat-num">' + contradictedTotal + '</span><span class="fc-stat-label">Widerlegt</span></button>';
|
||||
h += '</div>';
|
||||
|
||||
// Sort: status_history first, then by sources_count
|
||||
checks = checks.slice().sort(function(a, b) {
|
||||
var aH = (a.status_history || []).length;
|
||||
var bH = (b.status_history || []).length;
|
||||
@@ -723,40 +753,100 @@ var Lagebild = {
|
||||
return (b.sources_count || 0) - (a.sources_count || 0);
|
||||
});
|
||||
|
||||
// Compact accordion list
|
||||
h += '<div class="fc-list" id="fc-list">';
|
||||
for (var i = 0; i < checks.length; i++) {
|
||||
var fc = checks[i];
|
||||
var status = fc.status || 'developing';
|
||||
var hasProg = fc.status_history && fc.status_history.length > 1;
|
||||
var filterGroup = 'all';
|
||||
if (status === 'confirmed' || status === 'established') filterGroup = 'confirmed';
|
||||
else if (status === 'unconfirmed' || status === 'developing') filterGroup = 'unconfirmed';
|
||||
else if (status === 'contradicted' || status === 'disputed') filterGroup = 'contradicted';
|
||||
|
||||
h += '<div class="factcheck-card' + (hasProg ? ' highlight' : '') + '" data-status="' + status + '">';
|
||||
h += '<div class="factcheck-header">';
|
||||
h += '<span class="status-badge ' + status + '">' + this.stLabel(status) + '</span>';
|
||||
h += '<span class="factcheck-sources">' + (fc.sources_count || 0) + ' unabh\u00e4ngige Quellen</span>';
|
||||
var hasProg = fc.status_history && fc.status_history.length > 1;
|
||||
var icon = this.fcIcons[status] || '?';
|
||||
var label = this.fcLabels[status] || status;
|
||||
|
||||
h += '<div class="fc-row" data-status-group="' + filterGroup + '">';
|
||||
h += '<div class="fc-row-header" data-fc-index="' + i + '">';
|
||||
h += '<span class="fc-icon ' + status + '" title="' + this.esc(label) + '">' + icon + '</span>';
|
||||
h += '<span class="fc-row-claim">' + this.esc(this.fixUmlauts(fc.claim || '')) + '</span>';
|
||||
h += '<span class="fc-row-sources">' + (fc.sources_count || 0) + '</span>';
|
||||
if (hasProg) h += '<span class="fc-row-history-dot" title="Statusverlauf vorhanden"></span>';
|
||||
h += '<span class="fc-row-chevron">▸</span>';
|
||||
h += '</div>';
|
||||
h += '<p class="factcheck-claim">' + this.esc(this.fixUmlauts(fc.claim || '')) + '</p>';
|
||||
|
||||
// Expandable detail
|
||||
h += '<div class="fc-row-detail">';
|
||||
h += '<div class="fc-row-detail-inner">';
|
||||
h += '<div class="fc-detail-status"><span class="fc-icon ' + status + '">' + icon + '</span> <strong>' + label + '</strong> – ' + (fc.sources_count || 0) + ' unabhängige Quellen</div>';
|
||||
|
||||
if (fc.evidence) {
|
||||
var ev = this.fixUmlauts(fc.evidence);
|
||||
ev = this.esc(ev).replace(/(https?:\/\/[^\s,)]+)/g, '<a href="$1" target="_blank" rel="noopener">$1</a>');
|
||||
h += '<div class="factcheck-evidence"><strong>Evidenz:</strong> ' + ev + '</div>';
|
||||
h += '<div class="fc-detail-evidence"><strong>Evidenz:</strong> ' + ev + '</div>';
|
||||
}
|
||||
|
||||
if (hasProg) {
|
||||
h += '<div class="status-progression">';
|
||||
h += '<span class="progression-label">Verlauf:</span>';
|
||||
h += '<div class="fc-detail-progression">';
|
||||
h += '<span class="fc-detail-prog-label">Verlauf:</span>';
|
||||
for (var j = 0; j < fc.status_history.length; j++) {
|
||||
var step = fc.status_history[j];
|
||||
if (j > 0) h += '<span class="progression-arrow">→</span>';
|
||||
h += '<span class="progression-step">';
|
||||
h += '<span class="status-badge ' + step.status + '">' + this.stLabel(step.status) + '</span>';
|
||||
h += '<span class="fc-icon small ' + step.status + '">' + (this.fcIcons[step.status] || '?') + '</span>';
|
||||
if (step.at) h += '<span class="progression-time">' + this.fmtShort(step.at) + '</span>';
|
||||
h += '</span>';
|
||||
}
|
||||
h += '</div>';
|
||||
}
|
||||
h += '</div></div>';
|
||||
h += '</div>';
|
||||
}
|
||||
h += '</div>';
|
||||
|
||||
document.getElementById('factchecks-content').innerHTML = h;
|
||||
|
||||
// Filter click handler
|
||||
var statBtns = document.querySelectorAll('.fc-stat');
|
||||
statBtns.forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
for (var k = 0; k < statBtns.length; k++) statBtns[k].classList.remove('active');
|
||||
btn.classList.add('active');
|
||||
var filter = btn.getAttribute('data-filter');
|
||||
var rows = document.querySelectorAll('.fc-row');
|
||||
for (var k = 0; k < rows.length; k++) {
|
||||
if (filter === 'all' || rows[k].getAttribute('data-status-group') === filter) {
|
||||
rows[k].style.display = '';
|
||||
} else {
|
||||
rows[k].style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Accordion click handler
|
||||
document.getElementById('fc-list').addEventListener('click', function(e) {
|
||||
var header = e.target.closest('.fc-row-header');
|
||||
if (!header) return;
|
||||
var row = header.closest('.fc-row');
|
||||
var wasOpen = row.classList.contains('open');
|
||||
|
||||
// Close all open rows
|
||||
var allRows = document.querySelectorAll('.fc-row.open');
|
||||
for (var k = 0; k < allRows.length; k++) allRows[k].classList.remove('open');
|
||||
|
||||
// Toggle clicked row
|
||||
if (!wasOpen) {
|
||||
row.classList.add('open');
|
||||
var detail = row.querySelector('.fc-row-detail');
|
||||
if (detail) {
|
||||
setTimeout(function() {
|
||||
detail.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/* ===== TABS ===== */
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren