Tutorial: Formular live mit Cursor ausfuellen statt statisch vorab

Step 3 ist jetzt eine interaktive Demo:
- Cursor faehrt zum Titel-Feld, tippt "Explosion in Hamburger Hafen"
  Zeichen fuer Zeichen ein
- Cursor wechselt zur Beschreibung, tippt Kurztext
- Modal scrollt nach unten, Cursor wechselt Aktualisierung auf Auto
- Jedes Feld wird beim Ausfuellen gehighlightet
- _simulateTyping(): Neue Helfer-Methode fuer Zeichen-fuer-Zeichen-Eingabe
- Step 4 (Typ-Wechsel) scrollt Modal zurueck nach oben zum Typ-Feld

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
Claude Dev
2026-03-16 15:28:49 +01:00
Ursprung 1ea62ba901
Commit 1a372343bc
2 geänderte Dateien mit 113 neuen und 26 gelöschten Zeilen

Datei anzeigen

@@ -764,7 +764,7 @@
<script src="/static/js/api_network.js?v=20260316a"></script>
<script src="/static/js/network-graph.js?v=20260316a"></script>
<script src="/static/js/app_network.js?v=20260316a"></script>
<script src="/static/js/tutorial.js?v=20260316f"></script>
<script src="/static/js/tutorial.js?v=20260316g"></script>
<script src="/static/js/chat.js?v=20260316f"></script>
<script>document.addEventListener("DOMContentLoaded",function(){Chat.init();Tutorial.init()});</script>

Datei anzeigen

@@ -460,7 +460,7 @@ const Tutorial = {
id: 'new-incident-btn',
target: '#new-incident-btn',
title: 'Neue Lage anlegen',
text: 'Hier starten Sie die Erstellung einer neuen Lage. Im nächsten Schritt zeigen wir Ihnen das Formular dazu.',
text: 'Mit diesem Button erstellen Sie eine neue Lage. Wir simulieren jetzt die Eingabe f\u00fcr Sie.',
position: 'right',
onEnter: function() {
Tutorial._stepTimeout(function() {
@@ -475,41 +475,29 @@ const Tutorial = {
if (overlay) overlay.classList.remove('active');
},
},
// 3 - Neue Lage Modal: Überblick
// 3 - Formular ausf\u00fcllen (Cursor-Demo)
{
id: 'new-incident-modal',
target: '#modal-new .modal',
title: 'Lage konfigurieren',
text: 'Das Formular zur Lage-Erstellung. Hier legen Sie fest:<br><br>'
+ '<strong>Titel</strong> - Kurze Bezeichnung des Vorfalls<br>'
+ '<strong>Beschreibung</strong> - Kontext und Hintergrundinformationen<br>'
+ '<strong>Art der Lage</strong> - Live-Monitoring oder Analyse/Recherche<br>'
+ '<strong>Quellen</strong> - Internationale und Telegram-Quellen ein/aus<br>'
+ '<strong>Sichtbarkeit</strong> - Öffentlich oder privat für Ihr Team<br>'
+ '<strong>Aktualisierung</strong> - Manuell oder automatisch mit Intervall',
text: 'Beobachten Sie, wie das Formular Schritt f\u00fcr Schritt ausgef\u00fcllt wird. '
+ 'So legen Sie eine neue Lage an.',
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';
// Demo: Titel vorausfüllen
var titleInput = document.getElementById('inc-title');
if (titleInput) titleInput.value = 'Explosion in Hamburger Hafen';
var descInput = document.getElementById('inc-description');
if (descInput) descInput.value = 'Schwere Explosion im Hamburger Hafengebiet, Burchardkai-Terminal';
// Formular zuruecksetzen
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();
},
onExit: function() {
var overlay = document.getElementById('modal-new');
if (overlay) {
overlay.classList.remove('active');
overlay.style.zIndex = '';
}
var titleInput = document.getElementById('inc-title');
if (titleInput) titleInput.value = '';
var descInput = document.getElementById('inc-description');
if (descInput) descInput.value = '';
// Werte stehen lassen (werden in stop() aufgeraeumt)
},
},
// 4 - Lage-Typ: Live vs Recherche (mit Cursor-Demo)
@@ -531,8 +519,13 @@ const Tutorial = {
overlay.classList.add('active');
}
if (overlay) overlay.style.zIndex = '9002';
Tutorial._highlightSub('#inc-type');
Tutorial._simulateTypeSwitch();
// 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() {
Tutorial._highlightSub('#inc-type');
Tutorial._simulateTypeSwitch();
}, 500);
},
onExit: function() {
var overlay = document.getElementById('modal-new');
@@ -1295,6 +1288,100 @@ const Tutorial = {
this._enableNavAfterDemo();
},
// -----------------------------------------------------------------------
// Tipp-Simulation: Text Zeichen fuer Zeichen eingeben
// -----------------------------------------------------------------------
_simulateTyping(input, text, ms) {
var self = this;
var charDelay = ms / text.length;
return new Promise(function(resolve) {
var i = 0;
function typeNext() {
if (!self._isActive || i >= text.length) { resolve(); return; }
input.value += text[i];
input.dispatchEvent(new Event('input', { bubbles: true }));
i++;
setTimeout(typeNext, charDelay);
}
typeNext();
});
},
// -----------------------------------------------------------------------
// Formular-Ausf\u00fcll-Demo (Step 3)
// -----------------------------------------------------------------------
async _simulateFormFill() {
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 || !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();
this._highlightSub('#inc-title');
await this._simulateTyping(titleInput, 'Explosion in Hamburger Hafen', 1200);
await this._wait(500);
this._clearSubHighlights();
// 2. 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);
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)
// -----------------------------------------------------------------------