diff --git a/src/static/css/style.css b/src/static/css/style.css index 8c56442..dd0fa68 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -4943,266 +4943,266 @@ a.map-popup-article:hover { position: relative; z-index: 100; } - -/* ================================================================ - Tutorial System - ================================================================ */ - -/* Overlay (Hintergrund-Abdunkelung) */ -.tutorial-overlay { - display: none; - position: fixed; - inset: 0; - z-index: 9000; - pointer-events: none; -} -.tutorial-overlay.active { - display: block; -} - -/* Spotlight */ -.tutorial-spotlight { - position: fixed; - z-index: 9001; - box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.65); - border: 2px solid var(--accent); - border-radius: var(--radius-lg); - transition: top 0.4s ease, left 0.4s ease, width 0.4s ease, height 0.4s ease, opacity 0.3s ease; - opacity: 0; - pointer-events: none; -} - -/* Target-Element klickbar machen */ -.tutorial-overlay.active ~ * [data-tutorial-target] { - position: relative; - z-index: 9002; -} - -/* Bubble (Sprechblase) */ -.tutorial-bubble { - position: fixed; - z-index: 9003; - width: 340px; - background: var(--bg-card); - border: 1px solid var(--accent); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-lg), 0 0 20px rgba(150, 121, 26, 0.15); - padding: var(--sp-xl); - pointer-events: auto; - opacity: 0; - transition: opacity 0.3s ease, top 0.4s ease, left 0.4s ease, transform 0.4s ease; - font-family: var(--font-body); -} -.tutorial-bubble.visible { - opacity: 1; -} - -/* Bubble-Pfeil */ -.tutorial-bubble::before { - content: ''; - position: absolute; - width: 12px; - height: 12px; - background: var(--bg-card); - border: 1px solid var(--accent); - transform: rotate(45deg); -} - -.tutorial-pos-bottom::before { - top: -7px; - left: 50%; - margin-left: -6px; - border-right: none; - border-bottom: none; -} -.tutorial-pos-top::before { - bottom: -7px; - left: 50%; - margin-left: -6px; - border-left: none; - border-top: none; -} -.tutorial-pos-right::before { - left: -7px; - top: 30px; - border-top: none; - border-right: none; -} -.tutorial-pos-left::before { - right: -7px; - top: 30px; - border-bottom: none; - border-left: none; -} -.tutorial-pos-center::before { - display: none; -} - -/* Bubble-Inhalt */ -.tutorial-bubble-counter { - font-size: 11px; - color: var(--accent); - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; - margin-bottom: var(--sp-sm); -} - -.tutorial-bubble-title { - font-family: var(--font-title); - font-size: 16px; - font-weight: 600; - color: var(--text-primary); - margin-bottom: var(--sp-md); -} - -.tutorial-bubble-text { - font-size: 13px; - color: var(--text-secondary); - line-height: 1.6; - margin-bottom: var(--sp-lg); -} - -/* Close-Button */ -.tutorial-bubble-close { - position: absolute; - top: var(--sp-md); - right: var(--sp-md); - width: 24px; - height: 24px; - border: none; - background: transparent; - color: var(--text-secondary); - font-size: 18px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - border-radius: var(--radius); - transition: color 0.15s, background 0.15s; - line-height: 1; -} -.tutorial-bubble-close:hover { - color: var(--text-primary); - background: var(--bg-hover); -} - -/* Fortschrittspunkte */ -.tutorial-bubble-dots { - display: flex; - gap: 5px; - justify-content: center; - margin-bottom: var(--sp-lg); - flex-wrap: wrap; -} -.tutorial-dot { - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--border); - transition: background 0.2s; -} -.tutorial-dot.active { - background: var(--accent); - width: 18px; - border-radius: 3px; -} -.tutorial-dot.done { - background: var(--accent-hover); -} - -/* Nav-Buttons */ -.tutorial-bubble-nav { - display: flex; - justify-content: space-between; - align-items: center; - gap: var(--sp-md); -} - -.tutorial-btn { - border: none; - border-radius: var(--radius); - padding: var(--sp-md) var(--sp-xl); - font-size: 13px; - font-weight: 500; - cursor: pointer; - transition: background 0.15s, color 0.15s; - font-family: var(--font-body); -} -.tutorial-btn-back { - background: var(--bg-hover); - color: var(--text-secondary); -} -.tutorial-btn-back:hover { - background: var(--bg-elevated); - color: var(--text-primary); -} -.tutorial-btn-next { - background: var(--accent); - color: #fff; -} -.tutorial-btn-next:hover { - background: var(--accent-hover); -} - -/* Virtueller Cursor */ -.tutorial-cursor { - position: fixed; - z-index: 9500; - width: 24px; - height: 24px; - pointer-events: none; - opacity: 0; - transition: opacity 0.3s ease; -} -.tutorial-cursor.visible { - opacity: 1; -} -.tutorial-cursor-default { - background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M5 3l14 8-6 2 4 8-3 1-4-8-5 4z' fill='%23fff' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E") no-repeat center/contain; -} -.tutorial-cursor-grabbing { - background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M8 10V8a1 1 0 112 0v2h1V7a1 1 0 112 0v3h1V8a1 1 0 112 0v2h.5a1.5 1.5 0 011.5 1.5V16a5 5 0 01-5 5h-2a5 5 0 01-5-5v-3.5A1.5 1.5 0 017.5 11H8z' fill='%23fff' stroke='%23000' stroke-width='0.8'/%3E%3C/svg%3E") no-repeat center/contain; -} -.tutorial-cursor-resize { - background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M22 22H20V20H22V22ZM22 18H18V22H16V16H22V18ZM18 18V14H22V12H16V18H18ZM14 22H12V16H18V14H10V22H14Z' fill='%23fff' stroke='%23000' stroke-width='0.3'/%3E%3C/svg%3E") no-repeat center/contain; -} - -/* Chat Tutorial-Hinweis */ -.chat-tutorial-hint { - background: var(--bg-card); - border: 1px solid var(--accent); - border-radius: var(--radius); - padding: var(--sp-lg); - margin: var(--sp-md) var(--sp-md) 0; - cursor: pointer; - transition: background 0.15s; - font-size: 13px; - color: var(--text-secondary); - line-height: 1.5; -} -.chat-tutorial-hint:hover { - background: var(--tint-accent-subtle); -} -.chat-tutorial-hint strong { - color: var(--accent); -} - - -/* Sub-Element Highlight innerhalb von Tutorial-Steps */ -.tutorial-sub-highlight { - outline: 2px solid var(--accent) !important; - outline-offset: 3px; - border-radius: var(--radius); - animation: tutorial-sub-pulse 1.5s ease-in-out infinite; - position: relative; - z-index: 9002; -} - -@keyframes tutorial-sub-pulse { - 0%, 100% { outline-color: var(--accent); } - 50% { outline-color: rgba(150, 121, 26, 0.4); } -} + +/* ================================================================ + Tutorial System + ================================================================ */ + +/* Overlay (Hintergrund-Abdunkelung) */ +.tutorial-overlay { + display: none; + position: fixed; + inset: 0; + z-index: 9000; + pointer-events: none; +} +.tutorial-overlay.active { + display: block; +} + +/* Spotlight */ +.tutorial-spotlight { + position: fixed; + z-index: 9001; + box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.65); + border: 2px solid var(--accent); + border-radius: var(--radius-lg); + transition: top 0.4s ease, left 0.4s ease, width 0.4s ease, height 0.4s ease, opacity 0.3s ease; + opacity: 0; + pointer-events: none; +} + +/* Target-Element klickbar machen */ +.tutorial-overlay.active ~ * [data-tutorial-target] { + position: relative; + z-index: 9002; +} + +/* Bubble (Sprechblase) */ +.tutorial-bubble { + position: fixed; + z-index: 9003; + width: 340px; + background: var(--bg-card); + border: 1px solid var(--accent); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg), 0 0 20px rgba(150, 121, 26, 0.15); + padding: var(--sp-xl); + pointer-events: auto; + opacity: 0; + transition: opacity 0.3s ease, top 0.4s ease, left 0.4s ease, transform 0.4s ease; + font-family: var(--font-body); +} +.tutorial-bubble.visible { + opacity: 1; +} + +/* Bubble-Pfeil */ +.tutorial-bubble::before { + content: ''; + position: absolute; + width: 12px; + height: 12px; + background: var(--bg-card); + border: 1px solid var(--accent); + transform: rotate(45deg); +} + +.tutorial-pos-bottom::before { + top: -7px; + left: 50%; + margin-left: -6px; + border-right: none; + border-bottom: none; +} +.tutorial-pos-top::before { + bottom: -7px; + left: 50%; + margin-left: -6px; + border-left: none; + border-top: none; +} +.tutorial-pos-right::before { + left: -7px; + top: 30px; + border-top: none; + border-right: none; +} +.tutorial-pos-left::before { + right: -7px; + top: 30px; + border-bottom: none; + border-left: none; +} +.tutorial-pos-center::before { + display: none; +} + +/* Bubble-Inhalt */ +.tutorial-bubble-counter { + font-size: 11px; + color: var(--accent); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: var(--sp-sm); +} + +.tutorial-bubble-title { + font-family: var(--font-title); + font-size: 16px; + font-weight: 600; + color: var(--text-primary); + margin-bottom: var(--sp-md); +} + +.tutorial-bubble-text { + font-size: 13px; + color: var(--text-secondary); + line-height: 1.6; + margin-bottom: var(--sp-lg); +} + +/* Close-Button */ +.tutorial-bubble-close { + position: absolute; + top: var(--sp-md); + right: var(--sp-md); + width: 24px; + height: 24px; + border: none; + background: transparent; + color: var(--text-secondary); + font-size: 18px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--radius); + transition: color 0.15s, background 0.15s; + line-height: 1; +} +.tutorial-bubble-close:hover { + color: var(--text-primary); + background: var(--bg-hover); +} + +/* Fortschrittspunkte */ +.tutorial-bubble-dots { + display: flex; + gap: 5px; + justify-content: center; + margin-bottom: var(--sp-lg); + flex-wrap: wrap; +} +.tutorial-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--border); + transition: background 0.2s; +} +.tutorial-dot.active { + background: var(--accent); + width: 18px; + border-radius: 3px; +} +.tutorial-dot.done { + background: var(--accent-hover); +} + +/* Nav-Buttons */ +.tutorial-bubble-nav { + display: flex; + justify-content: space-between; + align-items: center; + gap: var(--sp-md); +} + +.tutorial-btn { + border: none; + border-radius: var(--radius); + padding: var(--sp-md) var(--sp-xl); + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: background 0.15s, color 0.15s; + font-family: var(--font-body); +} +.tutorial-btn-back { + background: var(--bg-hover); + color: var(--text-secondary); +} +.tutorial-btn-back:hover { + background: var(--bg-elevated); + color: var(--text-primary); +} +.tutorial-btn-next { + background: var(--accent); + color: #fff; +} +.tutorial-btn-next:hover { + background: var(--accent-hover); +} + +/* Virtueller Cursor */ +.tutorial-cursor { + position: fixed; + z-index: 9500; + width: 24px; + height: 24px; + pointer-events: none; + opacity: 0; + transition: opacity 0.3s ease; +} +.tutorial-cursor.visible { + opacity: 1; +} +.tutorial-cursor-default { + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M5 3l14 8-6 2 4 8-3 1-4-8-5 4z' fill='%23fff' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E") no-repeat center/contain; +} +.tutorial-cursor-grabbing { + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M8 10V8a1 1 0 112 0v2h1V7a1 1 0 112 0v3h1V8a1 1 0 112 0v2h.5a1.5 1.5 0 011.5 1.5V16a5 5 0 01-5 5h-2a5 5 0 01-5-5v-3.5A1.5 1.5 0 017.5 11H8z' fill='%23fff' stroke='%23000' stroke-width='0.8'/%3E%3C/svg%3E") no-repeat center/contain; +} +.tutorial-cursor-resize { + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M22 22H20V20H22V22ZM22 18H18V22H16V16H22V18ZM18 18V14H22V12H16V18H18ZM14 22H12V16H18V14H10V22H14Z' fill='%23fff' stroke='%23000' stroke-width='0.3'/%3E%3C/svg%3E") no-repeat center/contain; +} + +/* Chat Tutorial-Hinweis */ +.chat-tutorial-hint { + background: var(--bg-card); + border: 1px solid var(--accent); + border-radius: var(--radius); + padding: var(--sp-lg); + margin: var(--sp-md) var(--sp-md) 0; + cursor: pointer; + transition: background 0.15s; + font-size: 13px; + color: var(--text-secondary); + line-height: 1.5; +} +.chat-tutorial-hint:hover { + background: var(--tint-accent-subtle); +} +.chat-tutorial-hint strong { + color: var(--accent); +} + + +/* Sub-Element Highlight innerhalb von Tutorial-Steps */ +.tutorial-sub-highlight { + outline: 2px solid var(--accent) !important; + outline-offset: 3px; + border-radius: var(--radius); + animation: tutorial-sub-pulse 1.5s ease-in-out infinite; + position: relative; + z-index: 9002; +} + +@keyframes tutorial-sub-pulse { + 0%, 100% { outline-color: var(--accent); } + 50% { outline-color: rgba(150, 121, 26, 0.4); } +} /* Chat Tutorial-Hint Layout */ .chat-tutorial-hint { @@ -5228,3 +5228,17 @@ a.map-popup-article:hover { .chat-tutorial-hint-close:hover { color: var(--text-primary); } + + +/* Tutorial: Klicks auf Dashboard blockieren */ +body.tutorial-active .dashboard, +body.tutorial-active .modal-overlay, +body.tutorial-active .chat-toggle-btn, +body.tutorial-active #chat-window { + pointer-events: none !important; +} +/* Bubble und Cursor bleiben klickbar */ +body.tutorial-active .tutorial-bubble, +body.tutorial-active .tutorial-cursor { + pointer-events: auto !important; +} diff --git a/src/static/dashboard.html b/src/static/dashboard.html index 1347da4..ffd9d97 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -17,7 +17,7 @@ - + @@ -764,7 +764,7 @@ - + diff --git a/src/static/js/tutorial.js b/src/static/js/tutorial.js index 31bf762..80510dc 100644 --- a/src/static/js/tutorial.js +++ b/src/static/js/tutorial.js @@ -460,14 +460,13 @@ const Tutorial = { id: 'new-incident-btn', target: '#new-incident-btn', title: 'Neue Lage anlegen', - text: 'Mit diesem Button erstellen Sie eine neue Lage. Wir simulieren jetzt die Eingabe f\u00fcr Sie.', + text: 'Mit diesem Button öffnen Sie das Formular zur Erstellung einer neuen Lage. ' + + 'Wir gehen jetzt gemeinsam alle Felder durch.', position: 'right', onEnter: function() { Tutorial._stepTimeout(function() { var overlay = document.getElementById('modal-new'); - if (overlay && !overlay.classList.contains('active')) { - overlay.classList.add('active'); - } + if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active'); }, 1500); }, onExit: function() { @@ -475,51 +474,45 @@ const Tutorial = { if (overlay) overlay.classList.remove('active'); }, }, - // 3 - Formular ausf\u00fcllen (Cursor-Demo) + // 3 - Titel und Beschreibung (Cursor-Demo) { - id: 'new-incident-modal', + id: 'form-title-desc', target: '#modal-new .modal', - title: 'Lage konfigurieren', - text: 'Beobachten Sie, wie das Formular Schritt f\u00fcr Schritt ausgef\u00fcllt wird. ' - + 'So legen Sie eine neue Lage an.', + title: 'Titel und Beschreibung', + text: 'Geben Sie einen aussagekräftigen Titel ein, der das Ereignis klar beschreibt. ' + + 'Die Beschreibung liefert zusätzlichen Kontext für die Recherche.

' + + 'Beobachten Sie die Eingabe:', position: 'left', disableNav: true, onEnter: function() { var overlay = document.getElementById('modal-new'); - if (overlay && !overlay.classList.contains('active')) { - overlay.classList.add('active'); - } + if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active'); if (overlay) overlay.style.zIndex = '9002'; - // Formular zuruecksetzen + var modalBody = document.querySelector('#modal-new .modal-body'); + if (modalBody) modalBody.scrollTop = 0; var t = document.getElementById('inc-title'); if (t) t.value = ''; var d = document.getElementById('inc-description'); if (d) d.value = ''; - var r = document.getElementById('inc-refresh-mode'); if (r) { r.value = 'manual'; try { r.dispatchEvent(new Event('change')); } catch(e) {} } - Tutorial._simulateFormFill(); + Tutorial._simulateFormTitleDesc(); }, onExit: function() { - // Werte stehen lassen (werden in stop() aufgeraeumt) + Tutorial._clearSubHighlights(); }, }, - // 4 - Lage-Typ: Live vs Recherche (mit Cursor-Demo) + // 4 - Art der Lage (Cursor-Demo) { - id: 'incident-type', + id: 'form-type', target: '#modal-new .modal', - title: 'Live-Monitoring vs. Recherche', - text: 'Live-Monitoring beobachtet ein Ereignis in Echtzeit. ' - + 'Hunderte Nachrichtenquellen werden automatisch durchsucht. ' - + 'Ideal f\u00fcr aktuelle Vorf\u00e4lle, Krisen oder sich entwickelnde Lagen.

' - + 'Analyse/Recherche untersucht ein Thema tiefergehend. ' - + 'Keine automatischen Updates, stattdessen gezielte Recherche mit eigenen Suchbegriffen. ' - + 'Ideal f\u00fcr Hintergrundanalysen und Lageberichte.', + title: 'Art der Lage', + text: 'Live-Monitoring beobachtet ein Ereignis in Echtzeit. Hunderte Quellen werden ' + + 'laufend durchsucht. Ideal für aktuelle Krisen und sich entwickelnde Lagen.

' + + 'Analyse/Recherche untersucht ein Thema tiefergehend ohne automatische Updates. ' + + 'Ideal für Hintergrundanalysen und Lageberichte.', position: 'left', disableNav: true, onEnter: function() { var overlay = document.getElementById('modal-new'); - if (overlay && !overlay.classList.contains('active')) { - overlay.classList.add('active'); - } + if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active'); if (overlay) overlay.style.zIndex = '9002'; - // Modal-Body nach oben scrollen zum Typ-Feld var modalBody = document.querySelector('#modal-new .modal-body'); if (modalBody) modalBody.scrollTo({ top: 0, behavior: 'smooth' }); Tutorial._stepTimeout(function() { @@ -527,19 +520,124 @@ const Tutorial = { Tutorial._simulateTypeSwitch(); }, 500); }, + onExit: function() { + var sel = document.getElementById('inc-type'); + if (sel) { sel.value = 'adhoc'; try { sel.dispatchEvent(new Event('change')); } catch(e) {} } + Tutorial._clearSubHighlights(); + }, + }, + // 5 - Quellen + { + id: 'form-sources', + target: '#modal-new .modal', + title: 'Quellen konfigurieren', + text: 'Internationale Quellen bezieht englischsprachige und internationale Medien ' + + 'ein (Reuters, BBC, Al Jazeera etc.). Erhöht die Abdeckung, aber auch den Analyseumfang.

' + + 'Telegram-Kanäle liefern oft frühzeitige OSINT-Informationen, ' + + 'können aber auch unbestätigte Meldungen enthalten. Für sensible Lagen empfohlen.', + position: 'left', + disableNav: true, + onEnter: function() { + var overlay = document.getElementById('modal-new'); + if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active'); + if (overlay) overlay.style.zIndex = '9002'; + Tutorial._simulateFormSources(); + }, + onExit: function() { + Tutorial._clearSubHighlights(); + }, + }, + // 6 - Sichtbarkeit + { + id: 'form-visibility', + target: '#modal-new .modal', + title: 'Sichtbarkeit', + text: 'Öffentlich bedeutet, dass alle Nutzer Ihrer Organisation diese Lage sehen ' + + 'und darauf zugreifen können.

' + + 'Privat macht die Lage nur für Sie persönlich sichtbar. ' + + 'Nützlich für persönliche Recherchen oder sensible Vorgänge.', + position: 'left', + disableNav: true, + onEnter: function() { + var overlay = document.getElementById('modal-new'); + if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active'); + if (overlay) overlay.style.zIndex = '9002'; + Tutorial._simulateFormVisibility(); + }, + onExit: function() { + Tutorial._clearSubHighlights(); + }, + }, + // 7 - Aktualisierung und Intervall + { + id: 'form-refresh', + target: '#modal-new .modal', + title: 'Aktualisierung', + text: 'Manuell: Sie starten Aktualisierungen selbst per Button.
' + + 'Automatisch: Der Monitor aktualisiert im eingestellten Intervall.

' + + 'Wichtig: Kürzere Intervalle liefern aktuellere Daten, ' + + 'erhöhen aber den Creditverbrauch. Für die meisten Lagen sind 15 bis 30 Minuten ein guter Richtwert.', + position: 'left', + disableNav: true, + onEnter: function() { + var overlay = document.getElementById('modal-new'); + if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active'); + if (overlay) overlay.style.zIndex = '9002'; + Tutorial._simulateFormRefresh(); + }, + onExit: function() { + Tutorial._clearSubHighlights(); + }, + }, + // 8 - Aufbewahrung + { + id: 'form-retention', + target: '#modal-new .modal', + title: 'Aufbewahrung', + text: 'Legen Sie fest, wie lange die Lage aktiv bleibt. Nach Ablauf der Frist ' + + 'wird sie automatisch ins Archiv verschoben.

' + + 'Setzen Sie den Wert auf 0 für unbegrenzte Aufbewahrung. ' + + 'Standard sind 30 Tage.', + position: 'left', + disableNav: true, + onEnter: function() { + var overlay = document.getElementById('modal-new'); + if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active'); + if (overlay) overlay.style.zIndex = '9002'; + Tutorial._simulateFormRetention(); + }, + onExit: function() { + Tutorial._clearSubHighlights(); + }, + }, + // 9 - E-Mail-Benachrichtigungen + { + id: 'form-notifications', + target: '#modal-new .modal', + title: 'E-Mail-Benachrichtigungen', + text: 'Lassen Sie sich per E-Mail informieren bei:

' + + 'Neues Lagebild - Wenn eine aktualisierte Zusammenfassung vorliegt
' + + 'Neue Artikel - Wenn neue Quellen gefunden werden
' + + 'Statusänderung Faktencheck - Wenn sich die Bewertung einer Behauptung ändert

' + + 'So bleiben Sie auch ohne ständiges Einloggen auf dem Laufenden.', + position: 'left', + disableNav: true, + onEnter: function() { + var overlay = document.getElementById('modal-new'); + if (overlay && !overlay.classList.contains('active')) overlay.classList.add('active'); + if (overlay) overlay.style.zIndex = '9002'; + Tutorial._simulateFormNotifications(); + }, onExit: function() { var overlay = document.getElementById('modal-new'); if (overlay) { overlay.classList.remove('active'); overlay.style.zIndex = ''; } - // Select zur\u00fccksetzen - var sel = document.getElementById('inc-type'); - if (sel) { sel.value = 'adhoc'; sel.dispatchEvent(new Event('change')); } Tutorial._clearSubHighlights(); }, }, - // 5 - Sidebar Filter + // 5 - Sidebar Filter { id: 'sidebar-filters', target: '.sidebar-filter', @@ -905,8 +1003,9 @@ const Tutorial = { // Chat schließen falls offen if (typeof Chat !== 'undefined' && Chat._isOpen) Chat.close(); - // Overlay einblenden + // Overlay einblenden + Klicks blockieren this._els.overlay.classList.add('active'); + document.body.classList.add('tutorial-active'); // Keyboard this._keyHandler = this._onKey.bind(this); @@ -929,8 +1028,9 @@ const Tutorial = { this._currentStep = -1; this._demoRunning = false; - // Overlay ausblenden + // Overlay ausblenden + Klicks freigeben this._els.overlay.classList.remove('active'); + document.body.classList.remove('tutorial-active'); this._els.spotlight.style.opacity = '0'; this._els.bubble.classList.remove('visible'); this._hideCursor(); @@ -1307,146 +1407,117 @@ const Tutorial = { }); }, - // ----------------------------------------------------------------------- - // Formular-Ausf\u00fcll-Demo (Step 3) - // ----------------------------------------------------------------------- - async _simulateFormFill() { - this._demoRunning = true; + // Helfer: Cursor zu einem Element bewegen + async _cursorToElement(selector, fromX, fromY) { + var el = document.querySelector(selector); + if (!el) return { x: fromX, y: fromY }; + var rect = el.getBoundingClientRect(); + var tx = rect.left + Math.min(rect.width / 2, 60); + var ty = rect.top + rect.height / 2; + if (fromX !== undefined && fromY !== undefined) { + await this._animateCursor(fromX, fromY, tx, ty, 500); + } else { + this._showCursor(tx, ty, 'default'); + } + await this._wait(200); + return { x: tx, y: ty }; + }, + // Helfer: Modal-Body zu einem Element scrollen + _scrollModalTo(selector) { + var el = document.querySelector(selector); + var modalBody = document.querySelector('#modal-new .modal-body'); + if (!el || !modalBody) return; + var elTop = el.offsetTop - modalBody.offsetTop; + modalBody.scrollTo({ top: Math.max(0, elTop - 20), behavior: 'smooth' }); + }, + + // ----------------------------------------------------------------------- + // Step 3: Titel + Beschreibung + // ----------------------------------------------------------------------- + async _simulateFormTitleDesc() { + this._demoRunning = true; var titleInput = document.getElementById('inc-title'); var descInput = document.getElementById('inc-description'); - var refreshSelect = document.getElementById('inc-refresh-mode'); - var modal = document.querySelector('#modal-new .modal'); + if (!titleInput) { this._demoRunning = false; this._enableNavAfterDemo(); return; } - if (!titleInput || !modal) { - this._demoRunning = false; - this._enableNavAfterDemo(); - return; - } - - // Modal-Body scrollen wir nach oben - var modalBody = modal.querySelector('.modal-body'); - if (modalBody) modalBody.scrollTop = 0; - - // 1. Cursor zum Titel-Feld - var titleRect = titleInput.getBoundingClientRect(); - var startX = titleRect.left - 60; - var startY = titleRect.top - 40; - this._showCursor(startX, startY, 'default'); - await this._wait(300); - await this._animateCursor(startX, startY, titleRect.left + 20, titleRect.top + titleRect.height / 2, 600); - await this._wait(200); - - // Fokus + Tippen - titleInput.focus(); + // Cursor zum Titel this._highlightSub('#inc-title'); + var pos = await this._cursorToElement('#inc-title'); + titleInput.focus(); await this._simulateTyping(titleInput, 'Explosion in Hamburger Hafen', 1200); - await this._wait(500); + await this._wait(400); this._clearSubHighlights(); - // 2. Cursor zur Beschreibung + // Cursor zur Beschreibung if (descInput) { - var descRect = descInput.getBoundingClientRect(); - await this._animateCursor(titleRect.left + 20, titleRect.top + titleRect.height / 2, descRect.left + 20, descRect.top + 12, 500); - await this._wait(200); - descInput.focus(); this._highlightSub('#inc-description'); - await this._simulateTyping(descInput, 'Schwere Explosion im Hafengebiet', 1000); - await this._wait(500); + pos = await this._cursorToElement('#inc-description', pos.x, pos.y); + descInput.focus(); + await this._simulateTyping(descInput, 'Schwere Explosion im Hafengebiet, Burchardkai-Terminal', 1200); + await this._wait(400); this._clearSubHighlights(); } - // 3. Modal-Body runterscrollen zum Refresh-Feld - if (refreshSelect && modalBody) { - // Sanft scrollen - modalBody.scrollTo({ top: modalBody.scrollHeight, behavior: 'smooth' }); - await this._wait(600); - - var refRect = refreshSelect.getBoundingClientRect(); - var prevRect = descInput ? descInput.getBoundingClientRect() : titleRect; - await this._animateCursor(prevRect.left + 20, prevRect.top + 12, refRect.left + refRect.width / 2, refRect.top + refRect.height / 2, 600); - await this._wait(200); - - this._highlightSub('#inc-refresh-mode'); - refreshSelect.value = 'auto'; - try { refreshSelect.dispatchEvent(new Event('change')); } catch(e) {} - await this._wait(1200); - this._clearSubHighlights(); - } - - // 4. Cursor verschwindet this._hideCursor(); - await this._wait(300); - this._demoRunning = false; this._enableNavAfterDemo(); }, // ----------------------------------------------------------------------- - // Typ-Wechsel-Demo (Step 4) + // Step 4: Art der Lage (Typ-Wechsel) // ----------------------------------------------------------------------- async _simulateTypeSwitch() { this._demoRunning = true; var sel = document.getElementById('inc-type'); if (!sel) { this._demoRunning = false; this._enableNavAfterDemo(); return; } - var rect = sel.getBoundingClientRect(); - var targetX = rect.left + rect.width / 2; - var targetY = rect.top + rect.height / 2; - - // 1. Cursor erscheint oben links im Modal, f\u00e4hrt zum Select - var startX = rect.left - 80; - var startY = rect.top - 60; - this._showCursor(startX, startY, 'default'); - await this._wait(400); - await this._animateCursor(startX, startY, targetX, targetY, 800); + var pos = await this._cursorToElement('#inc-type'); await this._wait(300); - // 2. Klick - wechselt zu Recherche + // Wechsel zu Recherche sel.value = 'research'; sel.dispatchEvent(new Event('change')); - this._highlightSub('#inc-type'); await this._wait(2000); - // 3. Zur\u00fcck zu Live-Monitoring + // Zur\u00fcck zu Live-Monitoring sel.value = 'adhoc'; sel.dispatchEvent(new Event('change')); - await this._wait(1000); + await this._wait(800); - // 4. Cursor verschwindet this._hideCursor(); this._demoRunning = false; this._enableNavAfterDemo(); }, // ----------------------------------------------------------------------- - // Drag-Demo + // Step 5: Quellen (International + Telegram toggles) // ----------------------------------------------------------------------- - async _simulateDrag() { + async _simulateFormSources() { this._demoRunning = true; - var el = document.querySelector('[gs-id="lagebild"] .card-header'); - if (!el) { this._demoRunning = false; this._enableNavAfterDemo(); return; } + this._scrollModalTo('#inc-international'); + await this._wait(400); - var rect = el.getBoundingClientRect(); - var startX = rect.left + rect.width / 2; - var startY = rect.top + rect.height / 2; - var endX = startX + 200; - var endY = startY; + // International-Toggle highlighten + var intlCheckbox = document.getElementById('inc-international'); + var intlLabel = intlCheckbox ? intlCheckbox.closest('.toggle-label') : null; + if (intlLabel) { + this._highlightSub('#inc-international'); + var pos = await this._cursorToElement('#inc-international'); + await this._wait(1500); + this._clearSubHighlights(); - this._showCursor(startX, startY, 'default'); - await this._wait(500); - - this._els.cursor.classList.remove('tutorial-cursor-default'); - this._els.cursor.classList.add('tutorial-cursor-grabbing'); - await this._wait(300); - - await this._animateCursor(startX, startY, endX, endY, 1500); - await this._wait(200); - - this._els.cursor.classList.remove('tutorial-cursor-grabbing'); - this._els.cursor.classList.add('tutorial-cursor-default'); - await this._animateCursor(endX, endY, startX, startY, 800); - await this._wait(300); + // Telegram-Toggle + var telegramCheckbox = document.getElementById('inc-telegram'); + if (telegramCheckbox) { + this._highlightSub('#inc-telegram'); + pos = await this._cursorToElement('#inc-telegram', pos.x, pos.y); + // Aktivieren + telegramCheckbox.checked = true; + await this._wait(1500); + this._clearSubHighlights(); + } + } this._hideCursor(); this._demoRunning = false; @@ -1454,52 +1525,121 @@ const Tutorial = { }, // ----------------------------------------------------------------------- - // Resize-Demo + // Step 6: Sichtbarkeit // ----------------------------------------------------------------------- - async _simulateResize() { + async _simulateFormVisibility() { this._demoRunning = true; - var el = document.querySelector('[gs-id="faktencheck"]'); - if (!el) { this._demoRunning = false; this._enableNavAfterDemo(); return; } + this._scrollModalTo('#inc-visibility'); + await this._wait(400); - var rect = el.getBoundingClientRect(); - var startX = rect.right - 4; - var startY = rect.bottom - 4; - var endX = startX + 100; - var endY = startY + 60; + var checkbox = document.getElementById('inc-visibility'); + if (checkbox) { + this._highlightSub('#inc-visibility'); + var pos = await this._cursorToElement('#inc-visibility'); + await this._wait(1000); - this._showCursor(startX, startY, 'resize'); - await this._wait(500); + // Umschalten auf Privat + checkbox.checked = false; + var textEl = document.getElementById('visibility-text'); + if (textEl) textEl.textContent = 'Privat \u2014 nur f\u00fcr dich sichtbar'; + await this._wait(1500); - await this._animateCursor(startX, startY, endX, endY, 1200); - await this._wait(200); - - await this._animateCursor(endX, endY, startX, startY, 800); - await this._wait(300); + // Zur\u00fcck auf \u00d6ffentlich + checkbox.checked = true; + if (textEl) textEl.textContent = '\u00d6ffentlich \u2014 f\u00fcr alle Nutzer sichtbar'; + await this._wait(800); + this._clearSubHighlights(); + } this._hideCursor(); this._demoRunning = false; this._enableNavAfterDemo(); }, - _enableNavAfterDemo() { - var bubble = this._els.bubble; - var nav = bubble.querySelector('.tutorial-bubble-nav'); - if (!nav) return; - var index = this._currentStep; - var total = this._steps.length; + // ----------------------------------------------------------------------- + // Step 7: Aktualisierung + Intervall + // ----------------------------------------------------------------------- + async _simulateFormRefresh() { + this._demoRunning = true; + this._scrollModalTo('#inc-refresh-mode'); + await this._wait(400); - var navHtml = ''; - if (index > 0) { - navHtml += ''; - } else { - navHtml += ''; + var refreshSelect = document.getElementById('inc-refresh-mode'); + if (refreshSelect) { + this._highlightSub('#inc-refresh-mode'); + var pos = await this._cursorToElement('#inc-refresh-mode'); + await this._wait(800); + + // Auf Auto wechseln + refreshSelect.value = 'auto'; + try { refreshSelect.dispatchEvent(new Event('change')); } catch(e) {} + await this._wait(1000); + this._clearSubHighlights(); + + // Intervall-Feld highlighten + var intervalField = document.getElementById('refresh-interval-field'); + var intervalInput = document.getElementById('inc-refresh-value'); + if (intervalField && intervalInput) { + this._highlightSub('#inc-refresh-value'); + pos = await this._cursorToElement('#inc-refresh-value', pos.x, pos.y); + await this._wait(1500); + this._clearSubHighlights(); + } } - if (index < total - 1) { - navHtml += ''; - } else { - navHtml += ''; + + this._hideCursor(); + this._demoRunning = false; + this._enableNavAfterDemo(); + }, + + // ----------------------------------------------------------------------- + // Step 8: Aufbewahrung + // ----------------------------------------------------------------------- + async _simulateFormRetention() { + this._demoRunning = true; + this._scrollModalTo('#inc-retention'); + await this._wait(400); + + var retentionInput = document.getElementById('inc-retention'); + if (retentionInput) { + this._highlightSub('#inc-retention'); + var pos = await this._cursorToElement('#inc-retention'); + await this._wait(2000); + this._clearSubHighlights(); } - nav.innerHTML = navHtml; + + this._hideCursor(); + this._demoRunning = false; + this._enableNavAfterDemo(); + }, + + // ----------------------------------------------------------------------- + // Step 9: E-Mail-Benachrichtigungen + // ----------------------------------------------------------------------- + async _simulateFormNotifications() { + this._demoRunning = true; + this._scrollModalTo('#inc-notify-summary'); + await this._wait(400); + + var checks = ['#inc-notify-summary', '#inc-notify-new-articles', '#inc-notify-status-change']; + var pos; + for (var i = 0; i < checks.length; i++) { + var cb = document.querySelector(checks[i]); + if (!cb) continue; + this._highlightSub(checks[i]); + if (pos) { + pos = await this._cursorToElement(checks[i], pos.x, pos.y); + } else { + pos = await this._cursorToElement(checks[i]); + } + cb.checked = true; + await this._wait(1000); + this._clearSubHighlights(); + } + + this._hideCursor(); + this._demoRunning = false; + this._enableNavAfterDemo(); }, // -----------------------------------------------------------------------