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:
@@ -764,7 +764,7 @@
|
|||||||
<script src="/static/js/api_network.js?v=20260316a"></script>
|
<script src="/static/js/api_network.js?v=20260316a"></script>
|
||||||
<script src="/static/js/network-graph.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/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 src="/static/js/chat.js?v=20260316f"></script>
|
||||||
<script>document.addEventListener("DOMContentLoaded",function(){Chat.init();Tutorial.init()});</script>
|
<script>document.addEventListener("DOMContentLoaded",function(){Chat.init();Tutorial.init()});</script>
|
||||||
|
|
||||||
|
|||||||
@@ -460,7 +460,7 @@ const Tutorial = {
|
|||||||
id: 'new-incident-btn',
|
id: 'new-incident-btn',
|
||||||
target: '#new-incident-btn',
|
target: '#new-incident-btn',
|
||||||
title: 'Neue Lage anlegen',
|
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',
|
position: 'right',
|
||||||
onEnter: function() {
|
onEnter: function() {
|
||||||
Tutorial._stepTimeout(function() {
|
Tutorial._stepTimeout(function() {
|
||||||
@@ -475,41 +475,29 @@ const Tutorial = {
|
|||||||
if (overlay) overlay.classList.remove('active');
|
if (overlay) overlay.classList.remove('active');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 3 - Neue Lage Modal: Überblick
|
// 3 - Formular ausf\u00fcllen (Cursor-Demo)
|
||||||
{
|
{
|
||||||
id: 'new-incident-modal',
|
id: 'new-incident-modal',
|
||||||
target: '#modal-new .modal',
|
target: '#modal-new .modal',
|
||||||
title: 'Lage konfigurieren',
|
title: 'Lage konfigurieren',
|
||||||
text: 'Das Formular zur Lage-Erstellung. Hier legen Sie fest:<br><br>'
|
text: 'Beobachten Sie, wie das Formular Schritt f\u00fcr Schritt ausgef\u00fcllt wird. '
|
||||||
+ '<strong>Titel</strong> - Kurze Bezeichnung des Vorfalls<br>'
|
+ 'So legen Sie eine neue Lage an.',
|
||||||
+ '<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',
|
|
||||||
position: 'left',
|
position: 'left',
|
||||||
|
disableNav: true,
|
||||||
onEnter: function() {
|
onEnter: function() {
|
||||||
var overlay = document.getElementById('modal-new');
|
var overlay = document.getElementById('modal-new');
|
||||||
if (overlay && !overlay.classList.contains('active')) {
|
if (overlay && !overlay.classList.contains('active')) {
|
||||||
overlay.classList.add('active');
|
overlay.classList.add('active');
|
||||||
}
|
}
|
||||||
if (overlay) overlay.style.zIndex = '9002';
|
if (overlay) overlay.style.zIndex = '9002';
|
||||||
// Demo: Titel vorausfüllen
|
// Formular zuruecksetzen
|
||||||
var titleInput = document.getElementById('inc-title');
|
var t = document.getElementById('inc-title'); if (t) t.value = '';
|
||||||
if (titleInput) titleInput.value = 'Explosion in Hamburger Hafen';
|
var d = document.getElementById('inc-description'); if (d) d.value = '';
|
||||||
var descInput = document.getElementById('inc-description');
|
var r = document.getElementById('inc-refresh-mode'); if (r) { r.value = 'manual'; try { r.dispatchEvent(new Event('change')); } catch(e) {} }
|
||||||
if (descInput) descInput.value = 'Schwere Explosion im Hamburger Hafengebiet, Burchardkai-Terminal';
|
Tutorial._simulateFormFill();
|
||||||
},
|
},
|
||||||
onExit: function() {
|
onExit: function() {
|
||||||
var overlay = document.getElementById('modal-new');
|
// Werte stehen lassen (werden in stop() aufgeraeumt)
|
||||||
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 = '';
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 4 - Lage-Typ: Live vs Recherche (mit Cursor-Demo)
|
// 4 - Lage-Typ: Live vs Recherche (mit Cursor-Demo)
|
||||||
@@ -531,8 +519,13 @@ const Tutorial = {
|
|||||||
overlay.classList.add('active');
|
overlay.classList.add('active');
|
||||||
}
|
}
|
||||||
if (overlay) overlay.style.zIndex = '9002';
|
if (overlay) overlay.style.zIndex = '9002';
|
||||||
Tutorial._highlightSub('#inc-type');
|
// Modal-Body nach oben scrollen zum Typ-Feld
|
||||||
Tutorial._simulateTypeSwitch();
|
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() {
|
onExit: function() {
|
||||||
var overlay = document.getElementById('modal-new');
|
var overlay = document.getElementById('modal-new');
|
||||||
@@ -1295,6 +1288,100 @@ const Tutorial = {
|
|||||||
this._enableNavAfterDemo();
|
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)
|
// Typ-Wechsel-Demo (Step 4)
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren