diff --git a/src/static/css/style.css b/src/static/css/style.css index 30c6132..dbc8ab8 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -12,9 +12,9 @@ --bg-elevated: #1E2D45; /* Accent (Gold) */ - --accent: #C8A851; - --accent-hover: #B5923E; - --accent-pressed: #A07E2B; + --accent: #96791A; + --accent-hover: #7D6516; + --accent-pressed: #645112; /* Text */ --text-primary: #E8ECF4; @@ -61,10 +61,10 @@ --radius-lg: 8px; /* Tints (halbtransparente Hintergründe) */ - --tint-accent: rgba(200, 168, 81, 0.15); - --tint-accent-subtle: rgba(200, 168, 81, 0.08); - --tint-accent-faint: rgba(200, 168, 81, 0.04); - --tint-accent-strong: rgba(200, 168, 81, 0.18); + --tint-accent: rgba(150, 121, 26, 0.15); + --tint-accent-subtle: rgba(150, 121, 26, 0.08); + --tint-accent-faint: rgba(150, 121, 26, 0.04); + --tint-accent-strong: rgba(150, 121, 26, 0.18); --tint-error: rgba(239, 68, 68, 0.12); --tint-error-strong: rgba(239, 68, 68, 0.3); --tint-error-border: rgba(239, 68, 68, 0.4); @@ -81,8 +81,8 @@ --shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.5); /* Glows */ - --glow-accent: 0 0 8px rgba(200, 168, 81, 0.4); - --glow-accent-strong: 0 0 16px rgba(200, 168, 81, 0.6); + --glow-accent: 0 0 8px rgba(150, 121, 26, 0.4); + --glow-accent-strong: 0 0 16px rgba(150, 121, 26, 0.6); /* Overlay */ --backdrop: rgba(11, 17, 33, 0.85); @@ -3613,6 +3613,14 @@ a:hover { flex-shrink: 0; } +/* === Quellenübersicht Header-Stats (zugeklappter Zustand) === */ +.source-overview-header-stats { + font-size: 12px; + color: var(--text-tertiary); + font-weight: 400; + margin-left: 4px; +} + .source-overview-chevron.open { transform: rotate(90deg); } @@ -4569,11 +4577,16 @@ a.map-popup-article:hover { padding: 5px 0; } .tg-cat-item input[type="checkbox"] { + flex-shrink: 0; + margin: 0; accent-color: var(--accent); - width: 15px; - height: 15px; + width: 16px; + height: 16px; cursor: pointer; } +.tg-cat-item span { + line-height: 16px; +} .tg-cat-count { font-size: 11px; color: var(--text-disabled); @@ -4596,3 +4609,227 @@ a.map-popup-article:hover { .btn-link:hover { color: var(--accent-hover); } +/* ============================================================ + Chat-Assistent Widget + ============================================================ */ + +.chat-toggle-btn { + position: fixed; + bottom: 24px; + right: 24px; + width: 52px; + height: 52px; + border-radius: 50%; + background: var(--accent); + color: #fff; + border: none; + cursor: pointer; + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4px 16px rgba(0,0,0,0.3); + transition: transform 0.2s, background 0.2s; +} +.chat-toggle-btn:hover { + transform: scale(1.08); + background: var(--accent-hover); +} +.chat-toggle-btn.active { + background: var(--text-secondary); +} +.chat-toggle-btn svg { + width: 24px; + height: 24px; + fill: currentColor; +} + +.chat-window { + position: fixed; + bottom: 88px; + right: 24px; + width: 380px; + height: 520px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 12px; + z-index: 9998; + display: none; + flex-direction: column; + box-shadow: 0 8px 32px rgba(0,0,0,0.25); + overflow: hidden; +} +.chat-window.open { + display: flex; +} + +.chat-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + background: var(--bg-secondary); + border-bottom: 1px solid var(--border); + flex-shrink: 0; +} +.chat-header-title { + font-family: var(--font-title); + font-size: 14px; + font-weight: 600; + color: var(--text-primary); +} +.chat-header-close { + background: none; + border: none; + color: var(--text-secondary); + cursor: pointer; + padding: 4px; + line-height: 1; + font-size: 18px; + border-radius: 4px; +} +.chat-header-close:hover { + color: var(--text-primary); + background: var(--bg-tertiary); +} + +.chat-messages { + flex: 1; + overflow-y: auto; + padding: 16px; + display: flex; + flex-direction: column; + gap: 10px; +} + +.chat-message { + display: flex; + max-width: 85%; +} +.chat-message.user { + align-self: flex-end; +} +.chat-message.assistant { + align-self: flex-start; +} + +.chat-bubble { + padding: 10px 14px; + border-radius: 12px; + font-size: 13px; + line-height: 1.5; + word-break: break-word; +} +.chat-message.user .chat-bubble { + background: var(--accent); + color: #fff; + font-weight: 600; + border-bottom-right-radius: 4px; + box-shadow: var(--shadow-sm); +} +.chat-message.assistant .chat-bubble { + background: var(--bg-primary); + color: var(--text-primary); + border: 1px solid var(--border); + border-bottom-left-radius: 4px; + box-shadow: var(--shadow-sm); +} + +.chat-input-area { + display: flex; + align-items: flex-end; + gap: 8px; + padding: 12px; + border-top: 1px solid var(--border); + background: var(--bg-secondary); + flex-shrink: 0; +} +.chat-input-area textarea { + flex: 1; + resize: none; + border: 1px solid var(--border); + border-radius: 8px; + padding: 8px 12px; + font-size: 13px; + font-family: inherit; + line-height: 1.4; + background: var(--bg-primary); + color: var(--text-primary); + max-height: 120px; + min-height: 36px; + outline: none; +} +.chat-input-area textarea:focus { + border-color: var(--accent); +} +.chat-input-area textarea::placeholder { + color: var(--text-disabled); +} +.chat-send-btn { + background: var(--accent); + color: #fff; + border: none; + border-radius: 8px; + width: 36px; + height: 36px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + transition: background 0.15s; +} +.chat-send-btn:hover { + background: var(--accent-hover); +} +.chat-send-btn svg { + width: 16px; + height: 16px; + fill: currentColor; +} + +/* Typing animation */ +.chat-typing { + display: flex; + gap: 4px; + padding: 12px 16px; +} +.chat-typing span { + width: 6px; + height: 6px; + background: var(--text-disabled); + border-radius: 50%; + animation: chat-typing-bounce 1.2s infinite; +} +.chat-typing span:nth-child(2) { animation-delay: 0.2s; } +.chat-typing span:nth-child(3) { animation-delay: 0.4s; } + +@keyframes chat-typing-bounce { + 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } + 30% { transform: translateY(-6px); opacity: 1; } +} + +/* Mobile */ +@media (max-width: 640px) { + .chat-window { + bottom: 0; + right: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: 0; + border: none; + } + .chat-toggle-btn { + bottom: 16px; + right: 16px; + } +} + +/* Light Theme */ +[data-theme="light"] .chat-window { + box-shadow: 0 8px 32px rgba(0,0,0,0.12); +} +[data-theme="light"] .chat-message.assistant .chat-bubble { + background: var(--bg-primary); +} diff --git a/src/static/dashboard.html b/src/static/dashboard.html index eafb915..a72b456 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -239,7 +239,7 @@
@@ -346,26 +346,7 @@ Telegram-Kanäle einbeziehen
Nachrichten aus konfigurierten Telegram-Kanälen berücksichtigen
-
- - +
@@ -600,6 +581,24 @@
+ + +
+
+ AegisSight Assistent + +
+
+
+ + +
+
+
@@ -611,6 +610,8 @@ + +
diff --git a/src/static/js/app.js b/src/static/js/app.js index 50d329c..8ba4063 100644 --- a/src/static/js/app.js +++ b/src/static/js/app.js @@ -505,7 +505,7 @@ const App = { // Telegram-Kategorien Toggle const tgCheckbox = document.getElementById('inc-telegram'); if (tgCheckbox) { - tgCheckbox.addEventListener('change', function() { toggleTgCategories(this.checked); }); + } @@ -676,6 +676,7 @@ const App = { el = document.querySelector(".factcheck-list"); if (el) el.scrollTop = 0; el = document.getElementById("factcheck-list"); if (el) el.innerHTML = ""; el = document.getElementById("source-overview-content"); if (el) el.innerHTML = ""; + el = document.getElementById("source-overview-header-stats"); if (el) el.textContent = ""; el = document.getElementById("timeline-entries"); if (el) el.innerHTML = ""; await this.loadIncidentDetail(id); @@ -707,14 +708,15 @@ const App = { this.renderIncidentDetail(incident, articles, factchecks, snapshots, locations); } catch (err) { + console.error('loadIncidentDetail Fehler:', err); UI.showToast('Fehler beim Laden: ' + err.message, 'error'); } }, renderIncidentDetail(incident, articles, factchecks, snapshots, locations) { // Header Strip - document.getElementById('incident-title').textContent = incident.title; - document.getElementById('incident-description').textContent = incident.description || ''; + { const _e = document.getElementById('incident-title'); if (_e) _e.textContent = incident.title; } + { const _e = document.getElementById('incident-description'); if (_e) _e.textContent = incident.description || ''; } // Typ-Badge const typeBadge = document.getElementById('incident-type-badge'); @@ -768,9 +770,9 @@ const App = { : ''; } - document.getElementById('meta-refresh-mode').textContent = incident.refresh_mode === 'auto' + { const _e = document.getElementById('meta-refresh-mode'); if (_e) _e.textContent = incident.refresh_mode === 'auto' ? `Auto alle ${App._formatInterval(incident.refresh_interval)}` - : 'Manuell'; + : 'Manuell'; } // International-Badge const intlBadge = document.getElementById('intl-badge'); @@ -795,6 +797,12 @@ const App = { const sourceOverview = document.getElementById('source-overview-content'); if (sourceOverview) { sourceOverview.innerHTML = UI.renderSourceOverview(articles); + // Stats im Header aktualisieren (sichtbar im zugeklappten Zustand) + const _soStats = document.getElementById("source-overview-header-stats"); + if (_soStats) { + 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') { @@ -822,7 +830,7 @@ const App = { this._timelineFilter = 'all'; this._timelineRange = 'all'; this._activePointIndex = null; - document.getElementById('timeline-search').value = ''; + const _tsEl = document.getElementById('timeline-search'); if (_tsEl) _tsEl.value = ''; document.querySelectorAll('.ht-filter-btn').forEach(btn => { const isActive = btn.dataset.filter === 'all'; btn.classList.toggle('active', isActive); @@ -1459,9 +1467,6 @@ const App = { retention_days: parseInt(document.getElementById('inc-retention').value) || 0, international_sources: document.getElementById('inc-international').checked, include_telegram: document.getElementById('inc-telegram').checked, - telegram_categories: document.getElementById('inc-telegram').checked - ? Array.from(document.querySelectorAll('.tg-cat-cb:checked')).map(cb => cb.value) - : null, visibility: document.getElementById('inc-visibility').checked ? 'public' : 'private', }; }, @@ -1658,7 +1663,7 @@ const App = { const input = document.getElementById('inc-refresh-value'); input.value = value; input.min = unit === '1' ? 10 : 1; - document.getElementById('inc-refresh-unit').value = unit; + { const _e = document.getElementById('inc-refresh-unit'); if (_e) _e.value = unit; } }, _refreshHistoryOpen: false, @@ -1808,43 +1813,35 @@ const App = { this._editingIncidentId = this.currentIncidentId; // Formular mit aktuellen Werten füllen - document.getElementById('inc-title').value = incident.title; - document.getElementById('inc-description').value = incident.description || ''; - document.getElementById('inc-type').value = incident.type || 'adhoc'; - document.getElementById('inc-refresh-mode').value = incident.refresh_mode; + { const _e = document.getElementById('inc-title'); if (_e) _e.value = incident.title; } + { const _e = document.getElementById('inc-description'); if (_e) _e.value = incident.description || ''; } + { const _e = document.getElementById('inc-type'); if (_e) _e.value = incident.type || 'adhoc'; } + { const _e = document.getElementById('inc-refresh-mode'); if (_e) _e.value = incident.refresh_mode; } App._setIntervalFields(incident.refresh_interval); - document.getElementById('inc-retention').value = incident.retention_days; - document.getElementById('inc-international').checked = incident.international_sources !== false && incident.international_sources !== 0; - document.getElementById('inc-telegram').checked = !!incident.include_telegram; - // Telegram-Kategorien wiederherstellen - toggleTgCategories(!!incident.include_telegram); - if (incident.telegram_categories) { - let cats = incident.telegram_categories; - if (typeof cats === 'string') { try { cats = JSON.parse(cats); } catch(e) { cats = []; } } - document.querySelectorAll('.tg-cat-cb').forEach(cb => { - cb.checked = cats.includes(cb.value); - }); - } - document.getElementById('inc-visibility').checked = incident.visibility !== 'private'; + { const _e = document.getElementById('inc-retention'); if (_e) _e.value = incident.retention_days; } + { const _e = document.getElementById('inc-international'); if (_e) _e.checked = incident.international_sources !== false && incident.international_sources !== 0; } + { const _e = document.getElementById('inc-telegram'); if (_e) _e.checked = !!incident.include_telegram; } + + { const _e = document.getElementById('inc-visibility'); if (_e) _e.checked = incident.visibility !== 'private'; } updateVisibilityHint(); updateSourcesHint(); toggleTypeDefaults(); toggleRefreshInterval(); // Modal-Titel und Submit ändern - document.getElementById('modal-new-title').textContent = 'Lage bearbeiten'; - document.getElementById('modal-new-submit').textContent = 'Speichern'; + { const _e = document.getElementById('modal-new-title'); if (_e) _e.textContent = 'Lage bearbeiten'; } + { const _e = document.getElementById('modal-new-submit'); if (_e) _e.textContent = 'Speichern'; } // E-Mail-Subscription laden try { const sub = await API.getSubscription(this.currentIncidentId); - document.getElementById('inc-notify-summary').checked = !!sub.notify_email_summary; - document.getElementById('inc-notify-new-articles').checked = !!sub.notify_email_new_articles; - document.getElementById('inc-notify-status-change').checked = !!sub.notify_email_status_change; + { const _e = document.getElementById('inc-notify-summary'); if (_e) _e.checked = !!sub.notify_email_summary; } + { const _e = document.getElementById('inc-notify-new-articles'); if (_e) _e.checked = !!sub.notify_email_new_articles; } + { const _e = document.getElementById('inc-notify-status-change'); if (_e) _e.checked = !!sub.notify_email_status_change; } } catch (e) { - document.getElementById('inc-notify-summary').checked = false; - document.getElementById('inc-notify-new-articles').checked = false; - document.getElementById('inc-notify-status-change').checked = false; + { const _e = document.getElementById('inc-notify-summary'); if (_e) _e.checked = false; } + { const _e = document.getElementById('inc-notify-new-articles'); if (_e) _e.checked = false; } + { const _e = document.getElementById('inc-notify-status-change'); if (_e) _e.checked = false; } } openModal('modal-new'); @@ -3114,14 +3111,7 @@ function buildDetailedSourceOverview() { -function toggleTgCategories(show) { - const panel = document.getElementById('tg-categories-panel'); - if (panel) panel.style.display = show ? 'block' : 'none'; -} -function toggleAllTgCats(checked) { - document.querySelectorAll('.tg-cat-cb').forEach(cb => { cb.checked = checked; }); -} function toggleRefreshInterval() { const mode = document.getElementById('inc-refresh-mode').value; const field = document.getElementById('refresh-interval-field');