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 @@
-
+
Zum Hauptinhalt springen
@@ -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 += 'Zurück ';
- } 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 += 'Weiter ';
- } else {
- navHtml += 'Fertig ';
+
+ 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();
},
// -----------------------------------------------------------------------