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:
Claude Code
2026-03-07 17:24:49 +01:00
Ursprung 2bbe0b0bb7
Commit 8189cf9add
2 geänderte Dateien mit 277 neuen und 118 gelöschten Zeilen

Datei anzeigen

@@ -938,36 +938,61 @@ a.source-detail-article-title:hover {
100% { transform: scale(3.5); opacity: 0; } 100% { transform: scale(3.5); opacity: 0; }
} }
/* ---------- Fact Check Cards ---------- */ /* ---------- Fact Checks (Filter + Accordion) ---------- */
.fc-stats { .fc-stats {
justify-content: center;
display: flex; display: flex;
gap: 12px; gap: 10px;
margin-bottom: 24px; margin-bottom: 20px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.fc-stat { .fc-stat {
background: var(--lb-bg-secondary); background: var(--lb-bg-secondary);
border-radius: var(--radius-sm, 4px); border-radius: var(--radius-sm, 4px);
padding: 14px 20px; padding: 12px 18px;
text-align: center; text-align: center;
min-width: 80px; min-width: 75px;
border: 1px solid var(--lb-border); border: 1px solid var(--lb-border);
cursor: pointer;
transition: all 0.2s;
font-family: inherit;
}
.fc-stat:hover {
border-color: rgba(200, 168, 81, 0.4);
box-shadow: 0 0 16px var(--lb-glow-soft);
}
.fc-stat.active {
border-color: var(--lb-accent);
box-shadow: 0 0 20px var(--lb-glow-soft);
background: rgba(200, 168, 81, 0.06);
} }
.fc-stat.confirmed { .fc-stat.confirmed {
background: rgba(16, 185, 129, 0.08); background: rgba(16, 185, 129, 0.08);
border-color: rgba(16, 185, 129, 0.2); border-color: rgba(16, 185, 129, 0.2);
} }
.fc-stat.confirmed.active {
border-color: var(--lb-success);
box-shadow: 0 0 16px rgba(16, 185, 129, 0.2);
}
.fc-stat.unconfirmed { .fc-stat.unconfirmed {
background: rgba(245, 158, 11, 0.08); background: rgba(245, 158, 11, 0.08);
border-color: rgba(245, 158, 11, 0.2); border-color: rgba(245, 158, 11, 0.2);
} }
.fc-stat.unconfirmed.active {
border-color: var(--lb-warning);
box-shadow: 0 0 16px rgba(245, 158, 11, 0.2);
}
.fc-stat.contradicted { .fc-stat.contradicted {
background: rgba(239, 68, 68, 0.08); background: rgba(239, 68, 68, 0.08);
border-color: rgba(239, 68, 68, 0.2); border-color: rgba(239, 68, 68, 0.2);
} }
.fc-stat.contradicted.active {
border-color: var(--lb-error);
box-shadow: 0 0 16px rgba(239, 68, 68, 0.2);
}
.fc-stat-num { .fc-stat-num {
display: block; display: block;
font-size: 1.6rem; font-size: 1.5rem;
font-weight: 800; font-weight: 800;
color: var(--lb-text); color: var(--lb-text);
} }
@@ -975,109 +1000,166 @@ a.source-detail-article-title:hover {
.fc-stat.unconfirmed .fc-stat-num { color: var(--lb-warning); } .fc-stat.unconfirmed .fc-stat-num { color: var(--lb-warning); }
.fc-stat.contradicted .fc-stat-num { color: var(--lb-error); } .fc-stat.contradicted .fc-stat-num { color: var(--lb-error); }
.fc-stat-label { .fc-stat-label {
font-size: 0.75rem; font-size: 0.72rem;
color: var(--lb-text-sec); color: var(--lb-text-sec);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.5px; letter-spacing: 0.5px;
font-weight: 600; font-weight: 600;
} }
.factcheck-card { /* Factcheck Icon (from real Monitor) */
background: var(--lb-bg-secondary); .fc-icon {
border-radius: var(--radius-sm, 4px); flex-shrink: 0;
padding: 20px; width: 22px;
margin-bottom: 12px; height: 22px;
border-left: 4px solid var(--lb-border); border-radius: 4px;
transition: transform 0.2s, box-shadow 0.2s;
}
.factcheck-card:hover {
transform: translateY(-1px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}
.factcheck-card.highlight {
background: rgba(200, 168, 81, 0.06);
border-left-color: var(--lb-accent);
box-shadow: 0 2px 12px rgba(200, 168, 81, 0.1);
}
.factcheck-card[data-status="confirmed"],
.factcheck-card[data-status="established"] {
border-left-color: var(--lb-success);
}
.factcheck-card[data-status="unconfirmed"],
.factcheck-card[data-status="unverified"] {
border-left-color: var(--lb-warning);
}
.factcheck-card[data-status="contradicted"],
.factcheck-card[data-status="disputed"] {
border-left-color: var(--lb-error);
}
.factcheck-card[data-status="developing"] {
border-left-color: #3b82f6;
}
.factcheck-card[data-status="false"] {
border-left-color: #dc2626;
}
.factcheck-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: center;
margin-bottom: 8px; font-size: 11px;
}
.factcheck-sources {
font-size: 0.8rem;
color: var(--lb-text-sec);
}
.factcheck-claim {
font-size: 0.95rem;
color: var(--lb-text);
margin: 0 0 4px;
line-height: 1.5;
}
/* Status Badges */
.status-badge {
display: inline-block;
padding: 3px 10px;
border-radius: var(--radius-sm, 4px);
font-size: 0.72rem;
font-weight: 700; font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
} }
.status-badge.confirmed, .status-badge.established { .fc-icon.small {
width: 18px;
height: 18px;
font-size: 10px;
}
.fc-icon.confirmed, .fc-icon.established {
background: rgba(16, 185, 129, 0.15); background: rgba(16, 185, 129, 0.15);
color: #34d399; color: #34d399;
} }
.status-badge.unconfirmed, .status-badge.unverified { .fc-icon.unconfirmed, .fc-icon.unverified {
background: rgba(245, 158, 11, 0.15); background: rgba(245, 158, 11, 0.15);
color: #fbbf24; color: #fbbf24;
} }
.status-badge.contradicted, .status-badge.disputed { .fc-icon.contradicted, .fc-icon.disputed, .fc-icon.false {
background: rgba(239, 68, 68, 0.15); background: rgba(239, 68, 68, 0.15);
color: #f87171; color: #f87171;
} }
.status-badge.developing { .fc-icon.developing {
background: rgba(59, 130, 246, 0.15); background: rgba(59, 130, 246, 0.15);
color: #60a5fa; color: #60a5fa;
} }
.status-badge.false {
background: rgba(220, 38, 38, 0.15); /* Accordion List */
color: #f87171; .fc-list {
border: 1px solid var(--lb-border);
border-radius: 4px;
overflow: hidden;
}
.fc-row {
border-bottom: 1px solid var(--lb-border);
}
.fc-row:last-child {
border-bottom: none;
}
.fc-row-header {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
cursor: pointer;
transition: background 0.15s;
}
.fc-row-header:hover {
background: var(--lb-bg-secondary);
}
.fc-row.open .fc-row-header {
background: var(--lb-bg-secondary);
border-bottom: 1px solid var(--lb-border);
}
.fc-row-claim {
flex: 1;
font-size: 0.88rem;
color: var(--lb-text);
line-height: 1.4;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.fc-row.open .fc-row-claim {
white-space: normal;
text-overflow: unset;
}
.fc-row-sources {
flex-shrink: 0;
font-size: 0.72rem;
color: var(--lb-text-sec);
background: var(--lb-bg-card);
border: 1px solid var(--lb-border);
padding: 2px 7px;
border-radius: 3px;
font-weight: 600;
}
.fc-row-history-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--lb-accent);
flex-shrink: 0;
box-shadow: 0 0 6px var(--lb-glow);
}
.fc-row-chevron {
flex-shrink: 0;
color: var(--lb-text-sec);
font-size: 0.8rem;
transition: transform 0.2s;
width: 16px;
text-align: center;
}
.fc-row.open .fc-row-chevron {
transform: rotate(90deg);
color: var(--lb-accent);
} }
/* Status Progression */ /* Expandable Detail */
.status-progression { .fc-row-detail {
display: none;
background: rgba(0, 0, 0, 0.15);
}
.fc-row.open .fc-row-detail {
display: block;
animation: sourceDetailSlideIn 0.25s ease-out;
}
.fc-row-detail-inner {
padding: 14px 14px 14px 46px;
}
.fc-detail-status {
font-size: 0.85rem;
color: var(--lb-text);
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 6px;
}
.fc-detail-evidence {
font-size: 0.82rem;
color: var(--lb-text-sec);
line-height: 1.6;
padding: 10px 14px;
background: rgba(0, 0, 0, 0.2);
border-radius: 4px;
border: 1px solid var(--lb-border);
margin-bottom: 8px;
}
.fc-detail-evidence strong {
color: var(--lb-text);
}
.fc-detail-evidence a {
color: var(--lb-accent) !important;
word-break: break-all;
}
.fc-detail-progression {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid var(--lb-border);
font-size: 0.78rem; font-size: 0.78rem;
flex-wrap: wrap; flex-wrap: wrap;
padding-top: 8px;
border-top: 1px solid var(--lb-border);
} }
.progression-label { .fc-detail-prog-label {
color: var(--lb-text-sec); color: var(--lb-text-sec);
font-weight: 500; font-weight: 500;
margin-right: 4px; margin-right: 4px;
@@ -1096,25 +1178,6 @@ a.source-detail-article-title:hover {
font-size: 0.7rem; font-size: 0.7rem;
} }
/* Evidence block */
.factcheck-evidence {
font-size: 0.82rem;
color: var(--lb-text-sec);
margin: 10px 0 0;
line-height: 1.6;
padding: 10px 14px;
background: rgba(0, 0, 0, 0.2);
border-radius: var(--radius-sm, 4px);
border: 1px solid var(--lb-border);
}
.factcheck-evidence strong {
color: var(--lb-text);
}
.factcheck-evidence a {
color: var(--lb-accent) !important;
word-break: break-all;
}
/* ---------- Floating CTA Pill ---------- */ /* ---------- Floating CTA Pill ---------- */
.floating-cta { .floating-cta {
position: fixed; position: fixed;
@@ -1343,10 +1406,15 @@ a.source-detail-article-title:hover {
.lagebild-main { .lagebild-main {
padding: 1rem 12px 3rem; padding: 1rem 12px 3rem;
} }
.factcheck-header { .fc-row-claim {
flex-direction: column; font-size: 0.82rem;
align-items: flex-start; }
gap: 6px; .fc-row-detail-inner {
padding: 12px 12px 12px 12px;
}
.fc-stat {
padding: 10px 14px;
min-width: 65px;
} }
.sources-grid { .sources-grid {
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
@@ -1375,6 +1443,7 @@ a.source-detail-article-title:hover {
height: 350px !important; height: 350px !important;
} }
.fc-stats { .fc-stats {
justify-content: center;
gap: 8px; gap: 8px;
} }
.fc-stat { .fc-stat {

Datei anzeigen

@@ -695,27 +695,57 @@ var Lagebild = {
}, },
/* ===== TAB: FAKTENCHECKS ===== */ /* ===== TAB: FAKTENCHECKS ===== */
/* ===== Factcheck Icons (from real Monitor) ===== */
fcIcons: {
confirmed: '&#10003;',
unconfirmed: '?',
contradicted: '&#10007;',
developing: '&#8635;',
established: '&#10003;',
disputed: '&#9888;',
'false': '&#10007;',
unverified: '?'
},
fcLabels: {
confirmed: 'Bestätigt',
unconfirmed: 'Unbestätigt',
contradicted: 'Widerlegt',
developing: 'Unklar',
established: 'Gesichert',
disputed: 'Umstritten',
'false': 'Falsch',
unverified: 'Nicht verifiziert'
},
renderFactChecksTab: function() { renderFactChecksTab: function() {
var checks = this.currentView.fact_checks || []; var checks = this.currentView.fact_checks || [];
if (!checks.length) { 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; return;
} }
// Count stats
var stats = { confirmed: 0, unconfirmed: 0, contradicted: 0, developing: 0, established: 0, disputed: 0 }; var stats = { confirmed: 0, unconfirmed: 0, contradicted: 0, developing: 0, established: 0, disputed: 0 };
for (var k = 0; k < checks.length; k++) { for (var k = 0; k < checks.length; k++) {
var st = checks[k].status || 'developing'; var st = checks[k].status || 'developing';
if (stats[st] !== undefined) stats[st]++; 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">'; 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 += '<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 += '<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 += '<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 += '<div class="fc-stat unconfirmed"><span class="fc-stat-num">' + (stats.unconfirmed + stats.developing) + '</span><span class="fc-stat-label">Offen</span></div>'; 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 (stats.contradicted + stats.disputed > 0) if (contradictedTotal > 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 contradicted" data-filter="contradicted"><span class="fc-stat-num">' + contradictedTotal + '</span><span class="fc-stat-label">Widerlegt</span></button>';
h += '</div>'; h += '</div>';
// Sort: status_history first, then by sources_count
checks = checks.slice().sort(function(a, b) { checks = checks.slice().sort(function(a, b) {
var aH = (a.status_history || []).length; var aH = (a.status_history || []).length;
var bH = (b.status_history || []).length; var bH = (b.status_history || []).length;
@@ -723,40 +753,100 @@ var Lagebild = {
return (b.sources_count || 0) - (a.sources_count || 0); 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++) { for (var i = 0; i < checks.length; i++) {
var fc = checks[i]; var fc = checks[i];
var status = fc.status || 'developing'; 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 + '">'; var hasProg = fc.status_history && fc.status_history.length > 1;
h += '<div class="factcheck-header">'; var icon = this.fcIcons[status] || '?';
h += '<span class="status-badge ' + status + '">' + this.stLabel(status) + '</span>'; var label = this.fcLabels[status] || status;
h += '<span class="factcheck-sources">' + (fc.sources_count || 0) + ' unabh\u00e4ngige Quellen</span>';
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">&#9656;</span>';
h += '</div>'; 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) { if (fc.evidence) {
var ev = this.fixUmlauts(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>'); 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) { if (hasProg) {
h += '<div class="status-progression">'; h += '<div class="fc-detail-progression">';
h += '<span class="progression-label">Verlauf:</span>'; h += '<span class="fc-detail-prog-label">Verlauf:</span>';
for (var j = 0; j < fc.status_history.length; j++) { for (var j = 0; j < fc.status_history.length; j++) {
var step = fc.status_history[j]; var step = fc.status_history[j];
if (j > 0) h += '<span class="progression-arrow">&rarr;</span>'; if (j > 0) h += '<span class="progression-arrow">&rarr;</span>';
h += '<span class="progression-step">'; 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>'; if (step.at) h += '<span class="progression-time">' + this.fmtShort(step.at) + '</span>';
h += '</span>'; h += '</span>';
} }
h += '</div>'; h += '</div>';
} }
h += '</div></div>';
h += '</div>'; h += '</div>';
} }
h += '</div>';
document.getElementById('factchecks-content').innerHTML = h; 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 ===== */ /* ===== TABS ===== */