Tutorial: Umfassende Verbesserungen an Schritten 5,8+,11,20-26,28,31,32
- Schritt 5: Simuliertes Dropdown statt einfachem Wechsel - Ab Schritt 8: Verbessertes Modal-Scrolling fuer alle Formularfelder - Schritt 11: Cursor-Demo zwischen Alle/Eigene Filtern - Schritt 20: Status-Durchlauf statt Scroll-Sprung - Schritt 21: Quellenübersicht nach Timeline/Karte verschoben - Schritt 22: Timeline mit Cursor-Navigation durch Zeitpunkte - Schritt 23: Cursor zeigt Orte einlesen + Vollbild Buttons - Schritt 24: Z-Index Fix fuer Bubble ueber Vollbild-Karte - Schritt 25/26: Kombinierte Drag+Resize Demo - Schritt 28/31: Position hoeher (position:top statt right/left) - Schritt 32: Bubble tiefer zentriert (55% statt 50%) - 6 neue Simulationsfunktionen hinzugefuegt
Dieser Commit ist enthalten in:
@@ -731,16 +731,24 @@ const Tutorial = {
|
|||||||
Tutorial._clearSubHighlights();
|
Tutorial._clearSubHighlights();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 5 - Sidebar Filter
|
// Sidebar Filter
|
||||||
{
|
{
|
||||||
id: 'sidebar-filters',
|
id: 'sidebar-filters',
|
||||||
target: '.sidebar-filter',
|
target: '.sidebar-filter',
|
||||||
title: 'Lagen filtern',
|
title: 'Lagen filtern',
|
||||||
text: 'Mit diesen Filtern steuern Sie, welche Lagen angezeigt werden:<br><br>'
|
text: 'Mit diesen Filtern steuern Sie, welche Lagen angezeigt werden:<br><br>'
|
||||||
+ '<strong>Alle</strong> - Zeigt sämtliche Lagen Ihrer Organisation<br>'
|
+ '<strong>Alle</strong> - Zeigt s\u00e4mtliche Lagen Ihrer Organisation<br>'
|
||||||
+ '<strong>Eigene</strong> - Nur Lagen, die Sie selbst erstellt haben<br><br>'
|
+ '<strong>Eigene</strong> - Nur Lagen, die Sie selbst erstellt haben<br><br>'
|
||||||
+ 'Bei vielen Lagen hilft dies, den Überblick zu behalten.',
|
+ 'Bei vielen Lagen hilft dies, den \u00dcberblick zu behalten.',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
|
disableNav: true,
|
||||||
|
onEnter: function() {
|
||||||
|
Tutorial._runDemo(Tutorial._simulateFilterSwitch);
|
||||||
|
},
|
||||||
|
onExit: function() {
|
||||||
|
Tutorial._clearSubHighlights();
|
||||||
|
Tutorial._hideCursor();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// 6 - Demo-Lage einführen
|
// 6 - Demo-Lage einführen
|
||||||
{
|
{
|
||||||
@@ -872,33 +880,97 @@ const Tutorial = {
|
|||||||
Tutorial._clearSubHighlights();
|
Tutorial._clearSubHighlights();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 14 - Faktencheck: Einzelner Eintrag
|
// Faktencheck: Status-Demo
|
||||||
{
|
{
|
||||||
id: 'faktencheck-detail',
|
id: 'faktencheck-detail',
|
||||||
target: '.factcheck-item[data-fc-status="confirmed"]',
|
target: '.factcheck-item[data-fc-status="confirmed"]',
|
||||||
title: 'Faktencheck-Eintrag',
|
title: 'Faktencheck-Eintrag',
|
||||||
text: 'Jeder Faktencheck-Eintrag besteht aus:<br><br>'
|
text: 'Jeder Faktencheck-Eintrag besteht aus:<br><br>'
|
||||||
+ '<strong>Status-Symbol</strong> - Farbcodiert für schnelle Einordnung (links)<br>'
|
+ '<strong>Status-Symbol</strong> - Farbcodiert f\u00fcr schnelle Einordnung<br>'
|
||||||
+ '<strong>Behauptung</strong> - Die geprüfte Aussage<br>'
|
+ '<strong>Behauptung</strong> - Die gepr\u00fcfte Aussage<br>'
|
||||||
+ '<strong>Quellenanzahl</strong> - Wie viele Quellen diese Behauptung stützen<br><br>'
|
+ '<strong>Quellenanzahl</strong> - Wie viele Quellen diese Behauptung st\u00fctzen<br><br>'
|
||||||
+ 'Die Filterfunktion oben rechts ermöglicht es, nach Status zu filtern, '
|
+ 'Beobachten Sie, wie der Cursor die verschiedenen Status durchgeht:',
|
||||||
+ 'z.B. nur unbestätigte Meldungen anzeigen.',
|
|
||||||
position: 'left',
|
position: 'left',
|
||||||
|
disableNav: true,
|
||||||
onEnter: function() {
|
onEnter: function() {
|
||||||
// Zum nicht-verifizierten Eintrag scrollen
|
Tutorial._runDemo(Tutorial._simulateFactcheckStati);
|
||||||
var fcList = document.getElementById('factcheck-list');
|
|
||||||
if (fcList) fcList.scrollTo({ top: fcList.scrollHeight, behavior: 'smooth' });
|
|
||||||
Tutorial._stepTimeout(function() {
|
|
||||||
if (fcList) fcList.scrollTo({ top: 0, behavior: 'smooth' });
|
|
||||||
}, 2000);
|
|
||||||
var item = document.querySelector('.factcheck-item[data-fc-status="confirmed"]');
|
|
||||||
if (item) item.classList.add('tutorial-sub-highlight');
|
|
||||||
Tutorial._cleanupFns.push(function() {
|
|
||||||
if (item) item.classList.remove('tutorial-sub-highlight');
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
onExit: function() {
|
onExit: function() {
|
||||||
Tutorial._clearSubHighlights();
|
Tutorial._clearSubHighlights();
|
||||||
|
Tutorial._hideCursor();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Ereignis-Timeline
|
||||||
|
{
|
||||||
|
id: 'timeline',
|
||||||
|
target: '[gs-id="timeline"]',
|
||||||
|
title: 'Ereignis-Timeline',
|
||||||
|
text: 'Die Timeline zeigt den chronologischen Verlauf aller Ereignisse. '
|
||||||
|
+ 'Klicken Sie auf einen Zeitpunkt, um die zugeh\u00f6rige Meldung anzuzeigen.<br><br>'
|
||||||
|
+ '<strong>Punkte</strong> - Meldungen und Lageberichte auf der Zeitachse<br>'
|
||||||
|
+ '<strong>Detail-Panel</strong> - Zeigt Quelle, Titel und Zeitstempel<br><br>'
|
||||||
|
+ 'So k\u00f6nnen Sie auch \u00e4ltere Lagebilder abrufen und den Verlauf nachvollziehen.',
|
||||||
|
position: 'top',
|
||||||
|
disableNav: true,
|
||||||
|
onEnter: function() {
|
||||||
|
Tutorial._runDemo(Tutorial._simulateTimeline);
|
||||||
|
},
|
||||||
|
onExit: function() {
|
||||||
|
Tutorial._clearSubHighlights();
|
||||||
|
Tutorial._hideCursor();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Karte: Kachel-Ansicht
|
||||||
|
{
|
||||||
|
id: 'karte',
|
||||||
|
target: '[gs-id="karte"]',
|
||||||
|
title: 'Geografische Verteilung',
|
||||||
|
text: 'Die Karte zeigt per <strong>Geoparsing</strong> automatisch erkannte Orte aus den Quellen.<br><br>'
|
||||||
|
+ '<strong>Orte einlesen</strong> - Startet das Geoparsing manuell neu<br>'
|
||||||
|
+ '<strong>Vollbild</strong> - Vergr\u00f6\u00dfert die Karte auf den gesamten Bildschirm<br><br>'
|
||||||
|
+ 'Im n\u00e4chsten Schritt \u00f6ffnen wir die Vollbildansicht.',
|
||||||
|
position: 'top',
|
||||||
|
disableNav: true,
|
||||||
|
onEnter: function() {
|
||||||
|
if (Tutorial._demoMapTileMap) {
|
||||||
|
Tutorial._demoMapTileMap.invalidateSize();
|
||||||
|
setTimeout(function() {
|
||||||
|
if (Tutorial._demoMapTileMap) Tutorial._demoMapTileMap.setView([53.545, 9.98], 12);
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
Tutorial._runDemo(Tutorial._simulateKarteButtons);
|
||||||
|
},
|
||||||
|
onExit: function() {
|
||||||
|
Tutorial._clearSubHighlights();
|
||||||
|
Tutorial._hideCursor();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Karte: Vollbild
|
||||||
|
{
|
||||||
|
id: 'karte-fullscreen',
|
||||||
|
target: '.map-fullscreen-header',
|
||||||
|
title: 'Karte im Vollbild',
|
||||||
|
text: 'Die Orte werden automatisch aus den Quellenartikeln erkannt und auf der Karte dargestellt:<br><br>'
|
||||||
|
+ '<strong style="color:#EF4444;">● Hauptereignisort</strong> - Wo das Ereignis stattfindet<br>'
|
||||||
|
+ '<strong style="color:#F59E0B;">● Erw\u00e4hnt</strong> - In Meldungen genannte Orte<br>'
|
||||||
|
+ '<strong style="color:#3B82F6;">● Kontext</strong> - Orte im weiteren Zusammenhang<br><br>'
|
||||||
|
+ 'Klicken Sie auf einen Marker f\u00fcr Details und verkn\u00fcpfte Artikel.',
|
||||||
|
position: 'left',
|
||||||
|
disableNav: true,
|
||||||
|
onEnter: function() {
|
||||||
|
var chatBtn = document.getElementById('chat-toggle-btn');
|
||||||
|
if (chatBtn) chatBtn.style.display = 'none';
|
||||||
|
var bubble = document.getElementById('tutorial-bubble');
|
||||||
|
if (bubble) bubble.style.zIndex = '99999';
|
||||||
|
Tutorial._openDemoMapFullscreen();
|
||||||
|
},
|
||||||
|
onExit: function() {
|
||||||
|
Tutorial._closeDemoMapFullscreen();
|
||||||
|
var chatBtn = document.getElementById('chat-toggle-btn');
|
||||||
|
if (chatBtn) chatBtn.style.display = '';
|
||||||
|
var bubble = document.getElementById('tutorial-bubble');
|
||||||
|
if (bubble) bubble.style.zIndex = '';
|
||||||
|
Tutorial._clearSubHighlights();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 15 - Quellen
|
// 15 - Quellen
|
||||||
@@ -921,105 +993,24 @@ const Tutorial = {
|
|||||||
Tutorial._clearSubHighlights();
|
Tutorial._clearSubHighlights();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 16 - Timeline
|
// Dashboard anpassen (Drag + Resize)
|
||||||
{
|
{
|
||||||
id: 'timeline',
|
id: 'layout-demo',
|
||||||
target: '[gs-id="timeline"]',
|
|
||||||
title: 'Ereignis-Timeline',
|
|
||||||
text: 'Die Timeline zeigt den chronologischen Verlauf aller Ereignisse:<br><br>'
|
|
||||||
+ '<strong>Meldungen</strong> - Einzelne Artikel und Nachrichten aus den Quellen<br>'
|
|
||||||
+ '<strong>Lageberichte</strong> - Automatisch erstellte Zusammenfassungen (hervorgehoben)<br><br>'
|
|
||||||
+ 'Nutzen Sie die Filter oben:<br>'
|
|
||||||
+ '<strong>Alle/Meldungen/Lageberichte</strong> - Typ-Filter<br>'
|
|
||||||
+ '<strong>24h/7T/Alles</strong> - Zeitraum eingrenzen<br>'
|
|
||||||
+ '<strong>Suchfeld</strong> - Freitextsuche in allen Einträgen',
|
|
||||||
position: 'top',
|
|
||||||
onEnter: function() {
|
|
||||||
Tutorial._highlightSub('.ht-controls');
|
|
||||||
},
|
|
||||||
onExit: function() {
|
|
||||||
Tutorial._clearSubHighlights();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// 17 - Karte: Kachel-Ansicht
|
|
||||||
{
|
|
||||||
id: 'karte',
|
|
||||||
target: '[gs-id="karte"]',
|
|
||||||
title: 'Geografische Verteilung',
|
|
||||||
text: 'Die Karte zeigt per <strong>Geoparsing</strong> automatisch erkannte Orte aus den Quellen.<br><br>'
|
|
||||||
+ '<strong>Orte einlesen</strong> - Startet das Geoparsing manuell neu<br>'
|
|
||||||
+ '<strong>Vollbild</strong> - Vergr\u00f6\u00dfert die Karte auf den gesamten Bildschirm<br><br>'
|
|
||||||
+ 'Im n\u00e4chsten Schritt \u00f6ffnen wir die Vollbildansicht und schauen uns die Marker im Detail an.',
|
|
||||||
position: 'top',
|
|
||||||
onEnter: function() {
|
|
||||||
// Tile-Map Resize triggern
|
|
||||||
if (Tutorial._demoMapTileMap) {
|
|
||||||
Tutorial._demoMapTileMap.invalidateSize();
|
|
||||||
setTimeout(function() {
|
|
||||||
if (Tutorial._demoMapTileMap) Tutorial._demoMapTileMap.setView([53.545, 9.98], 12);
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
Tutorial._highlightSub('#geoparse-btn');
|
|
||||||
Tutorial._stepTimeout(function() {
|
|
||||||
Tutorial._clearSubHighlights();
|
|
||||||
Tutorial._highlightSub('#map-expand-btn');
|
|
||||||
}, 2500);
|
|
||||||
},
|
|
||||||
onExit: function() {
|
|
||||||
Tutorial._clearSubHighlights();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// 18 - Karte: Vollbild + Zoom + Marker-Demo
|
|
||||||
{
|
|
||||||
id: 'karte-fullscreen',
|
|
||||||
target: '.map-fullscreen-header',
|
|
||||||
title: 'Karte im Vollbild',
|
|
||||||
text: 'Die Karte zoomt jetzt auf den Ereignisort. Die Marker zeigen:<br><br>'
|
|
||||||
+ '<strong style="color:#EF4444;">● Hauptereignisort</strong> - Burchardkai Terminal (6 Artikel)<br>'
|
|
||||||
+ '<strong style="color:#F59E0B;">● Erw\u00e4hnt</strong> - Hamburg Innenstadt (2 Artikel)<br>'
|
|
||||||
+ '<strong style="color:#3B82F6;">● Kontext</strong> - Elbe / Hafengebiet (1 Artikel)<br><br>'
|
|
||||||
+ 'Die <strong>Legende</strong> unten rechts erkl\u00e4rt die Farbkategorien. '
|
|
||||||
+ 'Klicken Sie auf einen Marker f\u00fcr Details und verkn\u00fcpfte Artikel.',
|
|
||||||
position: 'left',
|
|
||||||
disableNav: true,
|
|
||||||
onEnter: function() {
|
|
||||||
var chatBtn = document.getElementById('chat-toggle-btn');
|
|
||||||
if (chatBtn) chatBtn.style.display = 'none';
|
|
||||||
Tutorial._openDemoMapFullscreen();
|
|
||||||
},
|
|
||||||
onExit: function() {
|
|
||||||
Tutorial._closeDemoMapFullscreen();
|
|
||||||
var chatBtn = document.getElementById('chat-toggle-btn');
|
|
||||||
if (chatBtn) chatBtn.style.display = '';
|
|
||||||
Tutorial._clearSubHighlights();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// 18 - Drag Demo
|
|
||||||
{
|
|
||||||
id: 'drag-demo',
|
|
||||||
target: '[gs-id="lagebild"] .card-header',
|
target: '[gs-id="lagebild"] .card-header',
|
||||||
title: 'Kacheln verschieben',
|
title: 'Dashboard anpassen',
|
||||||
text: 'Alle Kacheln im Dashboard lassen sich frei per Drag-and-Drop verschieben. '
|
text: 'Alle Kacheln lassen sich frei verschieben und in der Gr\u00f6\u00dfe anpassen. '
|
||||||
+ 'Greifen Sie dazu die Kopfzeile einer Kachel und ziehen Sie sie an die gewünschte Position.<br><br>'
|
+ 'Greifen Sie die Kopfzeile zum Verschieben oder den unteren Rand zum Vergr\u00f6\u00dfern.<br><br>'
|
||||||
+ 'Beobachten Sie die virtuelle Maus-Demo:',
|
+ 'Beobachten Sie die Demo: Erst wird das Lagebild verschoben, dann vergr\u00f6\u00dfert.',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
disableNav: true,
|
disableNav: true,
|
||||||
onEnter: function() {
|
onEnter: function() {
|
||||||
Tutorial._runDemo(Tutorial._simulateDrag);
|
Tutorial._runDemo(Tutorial._simulateLayoutDemo);
|
||||||
},
|
},
|
||||||
},
|
onExit: function() {
|
||||||
// 19 - Resize Demo
|
if (typeof LayoutManager !== 'undefined' && LayoutManager._grid) {
|
||||||
{
|
LayoutManager._applyLayout(LayoutManager.DEFAULT_LAYOUT);
|
||||||
id: 'resize-demo',
|
}
|
||||||
target: '[gs-id="faktencheck"]',
|
Tutorial._hideCursor();
|
||||||
title: 'Kacheln in der Größe anpassen',
|
|
||||||
text: 'Ziehen Sie am rechten unteren Rand einer Kachel, um ihre Größe zu verändern. '
|
|
||||||
+ 'So können Sie wichtigen Inhalten wie dem Faktencheck mehr Platz einräumen.<br><br>'
|
|
||||||
+ 'Beobachten Sie die virtuelle Maus-Demo:',
|
|
||||||
position: 'left',
|
|
||||||
disableNav: true,
|
|
||||||
onEnter: function() {
|
|
||||||
Tutorial._runDemo(Tutorial._simulateResize);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 20 - Theme
|
// 20 - Theme
|
||||||
@@ -1031,17 +1022,17 @@ const Tutorial = {
|
|||||||
+ 'Ihre Auswahl wird automatisch gespeichert und beim nächsten Besuch beibehalten.',
|
+ 'Ihre Auswahl wird automatisch gespeichert und beim nächsten Besuch beibehalten.',
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
},
|
},
|
||||||
// 21 - Quellen verwalten
|
// Quellen verwalten
|
||||||
{
|
{
|
||||||
id: 'sources-btn',
|
id: 'sources-btn',
|
||||||
target: '.sidebar-sources-link button:first-child',
|
target: '.sidebar-sources-link button:first-child',
|
||||||
title: 'Quellenverwaltung öffnen',
|
title: 'Quellenverwaltung \u00f6ffnen',
|
||||||
text: 'In der Seitenleiste ganz unten finden Sie den Zugang zur Quellenverwaltung. '
|
text: 'In der Seitenleiste ganz unten finden Sie den Zugang zur Quellenverwaltung. '
|
||||||
+ 'Hier können Sie:<br><br>'
|
+ 'Hier k\u00f6nnen Sie:<br><br>'
|
||||||
+ '<strong>Neue Quellen hinzufügen</strong> - URL eingeben oder automatisch erkennen lassen<br>'
|
+ '<strong>Neue Quellen hinzuf\u00fcgen</strong> - URL eingeben oder automatisch erkennen lassen<br>'
|
||||||
+ '<strong>Bestehende Quellen bearbeiten</strong> - Kategorie, Sprache, Notizen anpassen<br>'
|
+ '<strong>Bestehende Quellen bearbeiten</strong> - Kategorie, Sprache, Notizen anpassen<br>'
|
||||||
+ '<strong>Quellen deaktivieren</strong> - Temporär oder dauerhaft ausschließen',
|
+ '<strong>Quellen deaktivieren</strong> - Tempor\u00e4r oder dauerhaft ausschlie\u00dfen',
|
||||||
position: 'right',
|
position: 'top',
|
||||||
onEnter: function() {
|
onEnter: function() {
|
||||||
Tutorial._stepTimeout(function() {
|
Tutorial._stepTimeout(function() {
|
||||||
var overlay = document.getElementById('modal-sources');
|
var overlay = document.getElementById('modal-sources');
|
||||||
@@ -1122,30 +1113,36 @@ const Tutorial = {
|
|||||||
Tutorial._hideCursor();
|
Tutorial._hideCursor();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 23 - Chat
|
// Chat-Assistent
|
||||||
{
|
{
|
||||||
id: 'chat',
|
id: 'chat',
|
||||||
target: '#chat-toggle-btn',
|
target: '#chat-toggle-btn',
|
||||||
title: 'Chat-Assistent',
|
title: 'Chat-Assistent',
|
||||||
text: 'Der Chat-Assistent steht Ihnen jederzeit zur Verfügung. '
|
text: 'Der Chat-Assistent steht Ihnen jederzeit zur Verf\u00fcgung. '
|
||||||
+ 'Stellen Sie Fragen zur Bedienung des Monitors und erhalten Sie sofort eine Antwort.<br><br>'
|
+ 'Stellen Sie Fragen zur Bedienung des Monitors und erhalten Sie sofort eine Antwort.<br><br>'
|
||||||
+ 'Beispiele:<br>'
|
+ 'Beispiele:<br>'
|
||||||
+ '"Wie erstelle ich eine neue Lage?"<br>'
|
+ '"Wie erstelle ich eine neue Lage?"<br>'
|
||||||
+ '"Was bedeuten die Faktencheck-Status?"<br>'
|
+ '"Was bedeuten die Faktencheck-Status?"<br>'
|
||||||
+ '"Wie exportiere ich einen Lagebericht?"',
|
+ '"Wie exportiere ich einen Lagebericht?"',
|
||||||
position: 'left',
|
position: 'top',
|
||||||
},
|
},
|
||||||
// 24 - Ende
|
// Ende
|
||||||
{
|
{
|
||||||
id: 'end',
|
id: 'end',
|
||||||
target: null,
|
target: null,
|
||||||
title: 'Rundgang abgeschlossen',
|
title: 'Rundgang abgeschlossen',
|
||||||
text: 'Sie kennen jetzt alle wichtigen Funktionen des AegisSight Monitors.<br><br>'
|
text: 'Sie kennen jetzt alle wichtigen Funktionen des AegisSight Monitors.<br><br>'
|
||||||
+ 'Die Demo-Daten werden nach dem Schließen entfernt. '
|
+ 'Die Demo-Daten werden nach dem Schlie\u00dfen entfernt. '
|
||||||
+ 'Erstellen Sie Ihre erste eigene Lage über den Button "+ Neue Lage" in der Seitenleiste.<br><br>'
|
+ 'Erstellen Sie Ihre erste eigene Lage \u00fcber den Button "+ Neue Lage" in der Seitenleiste.<br><br>'
|
||||||
+ 'Bei weiteren Fragen steht Ihnen der Chat-Assistent '
|
+ 'Bei weiteren Fragen steht Ihnen der Chat-Assistent '
|
||||||
+ 'oder unser Support unter support@aegis-sight.de zur Verfügung.',
|
+ 'oder unser Support unter support@aegis-sight.de zur Verf\u00fcgung.',
|
||||||
position: 'center',
|
position: 'center',
|
||||||
|
onEnter: function() {
|
||||||
|
Tutorial._stepTimeout(function() {
|
||||||
|
var bubble = document.getElementById('tutorial-bubble');
|
||||||
|
if (bubble) bubble.style.top = '55%';
|
||||||
|
}, 50);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
@@ -1746,8 +1743,11 @@ const Tutorial = {
|
|||||||
var el = document.querySelector(selector);
|
var el = document.querySelector(selector);
|
||||||
var modalBody = document.querySelector('#modal-new .modal-body');
|
var modalBody = document.querySelector('#modal-new .modal-body');
|
||||||
if (!el || !modalBody) return;
|
if (!el || !modalBody) return;
|
||||||
var elTop = el.offsetTop - modalBody.offsetTop;
|
var elRect = el.getBoundingClientRect();
|
||||||
modalBody.scrollTo({ top: Math.max(0, elTop - 20), behavior: 'smooth' });
|
var bodyRect = modalBody.getBoundingClientRect();
|
||||||
|
var offset = elRect.top - bodyRect.top + modalBody.scrollTop;
|
||||||
|
var targetScroll = offset - Math.min(40, bodyRect.height * 0.2);
|
||||||
|
modalBody.scrollTo({ top: Math.max(0, targetScroll), behavior: 'smooth' });
|
||||||
},
|
},
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
@@ -1787,6 +1787,8 @@ const Tutorial = {
|
|||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Step 4: Art der Lage (Typ-Wechsel)
|
// Step 4: Art der Lage (Typ-Wechsel)
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
// Step 4: Art der Lage (simuliertes Dropdown)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
async _simulateTypeSwitch() {
|
async _simulateTypeSwitch() {
|
||||||
this._demoRunning = true;
|
this._demoRunning = true;
|
||||||
var sel = document.getElementById('inc-type');
|
var sel = document.getElementById('inc-type');
|
||||||
@@ -1795,15 +1797,67 @@ const Tutorial = {
|
|||||||
var pos = await this._cursorToElement('#inc-type');
|
var pos = await this._cursorToElement('#inc-type');
|
||||||
await this._wait(300);
|
await this._wait(300);
|
||||||
|
|
||||||
// Wechsel zu Recherche
|
// Simuliertes Dropdown
|
||||||
sel.value = 'research';
|
var rect = sel.getBoundingClientRect();
|
||||||
sel.dispatchEvent(new Event('change'));
|
var dropdown = document.createElement('div');
|
||||||
await this._wait(2000);
|
dropdown.id = 'tutorial-fake-dropdown';
|
||||||
|
dropdown.style.cssText = 'position:fixed;z-index:99999;background:var(--bg-card,#1a1f2e);'
|
||||||
|
+ 'border:1px solid var(--border,#2a3040);border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,0.4);'
|
||||||
|
+ 'overflow:hidden;left:' + rect.left + 'px;top:' + (rect.bottom + 4) + 'px;width:' + rect.width + 'px;'
|
||||||
|
+ 'opacity:0;transform:translateY(-4px);transition:opacity 0.2s,transform 0.2s;';
|
||||||
|
|
||||||
// Zur\u00fcck zu Live-Monitoring
|
var opt1 = document.createElement('div');
|
||||||
|
opt1.textContent = 'Live-Monitoring';
|
||||||
|
opt1.style.cssText = 'padding:10px 14px;font-size:14px;color:var(--text-primary,#e0e0e0);'
|
||||||
|
+ 'background:var(--accent-alpha,rgba(212,175,55,0.15));transition:background 0.15s;';
|
||||||
|
|
||||||
|
var opt2 = document.createElement('div');
|
||||||
|
opt2.textContent = 'Analyse / Recherche';
|
||||||
|
opt2.style.cssText = 'padding:10px 14px;font-size:14px;color:var(--text-primary,#e0e0e0);'
|
||||||
|
+ 'transition:background 0.15s;';
|
||||||
|
|
||||||
|
dropdown.appendChild(opt1);
|
||||||
|
dropdown.appendChild(opt2);
|
||||||
|
document.body.appendChild(dropdown);
|
||||||
|
|
||||||
|
await this._wait(50);
|
||||||
|
dropdown.style.opacity = '1';
|
||||||
|
dropdown.style.transform = 'translateY(0)';
|
||||||
|
await this._wait(400);
|
||||||
|
|
||||||
|
// Cursor zu Live-Monitoring
|
||||||
|
var opt1Rect = opt1.getBoundingClientRect();
|
||||||
|
await this._animateCursor(pos.x, pos.y,
|
||||||
|
opt1Rect.left + opt1Rect.width / 2, opt1Rect.top + opt1Rect.height / 2, 400);
|
||||||
|
await this._wait(1200);
|
||||||
|
|
||||||
|
// Cursor zu Analyse/Recherche
|
||||||
|
var opt2Rect = opt2.getBoundingClientRect();
|
||||||
|
await this._animateCursor(
|
||||||
|
opt1Rect.left + opt1Rect.width / 2, opt1Rect.top + opt1Rect.height / 2,
|
||||||
|
opt2Rect.left + opt2Rect.width / 2, opt2Rect.top + opt2Rect.height / 2, 400);
|
||||||
|
opt1.style.background = '';
|
||||||
|
opt2.style.background = 'var(--accent-alpha,rgba(212,175,55,0.15))';
|
||||||
|
sel.value = 'research';
|
||||||
|
try { sel.dispatchEvent(new Event('change')); } catch(e) {}
|
||||||
|
await this._wait(1500);
|
||||||
|
|
||||||
|
// Zurueck zu Live-Monitoring
|
||||||
|
opt1Rect = opt1.getBoundingClientRect();
|
||||||
|
opt2Rect = opt2.getBoundingClientRect();
|
||||||
|
await this._animateCursor(
|
||||||
|
opt2Rect.left + opt2Rect.width / 2, opt2Rect.top + opt2Rect.height / 2,
|
||||||
|
opt1Rect.left + opt1Rect.width / 2, opt1Rect.top + opt1Rect.height / 2, 400);
|
||||||
|
opt2.style.background = '';
|
||||||
|
opt1.style.background = 'var(--accent-alpha,rgba(212,175,55,0.15))';
|
||||||
sel.value = 'adhoc';
|
sel.value = 'adhoc';
|
||||||
sel.dispatchEvent(new Event('change'));
|
try { sel.dispatchEvent(new Event('change')); } catch(e) {}
|
||||||
await this._wait(800);
|
await this._wait(1000);
|
||||||
|
|
||||||
|
dropdown.style.opacity = '0';
|
||||||
|
dropdown.style.transform = 'translateY(-4px)';
|
||||||
|
await this._wait(250);
|
||||||
|
if (dropdown.parentNode) dropdown.remove();
|
||||||
|
|
||||||
this._hideCursor();
|
this._hideCursor();
|
||||||
this._demoRunning = false;
|
this._demoRunning = false;
|
||||||
@@ -2209,6 +2263,281 @@ const Tutorial = {
|
|||||||
this._enableNavAfterDemo();
|
this._enableNavAfterDemo();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Sidebar-Filter Demo
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
async _simulateFilterSwitch() {
|
||||||
|
this._demoRunning = true;
|
||||||
|
await this._wait(400);
|
||||||
|
var btns = document.querySelectorAll('.sidebar-filter button, .sidebar-filter .filter-btn');
|
||||||
|
var allBtn = null, ownBtn = null;
|
||||||
|
btns.forEach(function(b) {
|
||||||
|
var t = b.textContent.trim().toLowerCase();
|
||||||
|
if (t === 'alle') allBtn = b;
|
||||||
|
if (t === 'eigene') ownBtn = b;
|
||||||
|
});
|
||||||
|
if (!allBtn) allBtn = document.querySelector('.sidebar-filter [data-filter="all"]');
|
||||||
|
if (!ownBtn) ownBtn = document.querySelector('.sidebar-filter [data-filter="own"]');
|
||||||
|
if (!allBtn || !ownBtn) { this._demoRunning = false; this._enableNavAfterDemo(); return; }
|
||||||
|
|
||||||
|
var ar = allBtn.getBoundingClientRect();
|
||||||
|
this._showCursor(ar.left - 30, ar.top - 20, 'default');
|
||||||
|
await this._wait(300);
|
||||||
|
await this._animateCursor(ar.left - 30, ar.top - 20, ar.left + ar.width/2, ar.top + ar.height/2, 400);
|
||||||
|
allBtn.classList.add('tutorial-sub-highlight');
|
||||||
|
await this._wait(1500);
|
||||||
|
allBtn.classList.remove('tutorial-sub-highlight');
|
||||||
|
|
||||||
|
var or2 = ownBtn.getBoundingClientRect();
|
||||||
|
await this._animateCursor(ar.left+ar.width/2, ar.top+ar.height/2, or2.left+or2.width/2, or2.top+or2.height/2, 500);
|
||||||
|
ownBtn.classList.add('tutorial-sub-highlight');
|
||||||
|
await this._wait(1500);
|
||||||
|
ownBtn.classList.remove('tutorial-sub-highlight');
|
||||||
|
|
||||||
|
ar = allBtn.getBoundingClientRect();
|
||||||
|
or2 = ownBtn.getBoundingClientRect();
|
||||||
|
await this._animateCursor(or2.left+or2.width/2, or2.top+or2.height/2, ar.left+ar.width/2, ar.top+ar.height/2, 500);
|
||||||
|
allBtn.classList.add('tutorial-sub-highlight');
|
||||||
|
await this._wait(1000);
|
||||||
|
allBtn.classList.remove('tutorial-sub-highlight');
|
||||||
|
|
||||||
|
this._hideCursor();
|
||||||
|
this._demoRunning = false;
|
||||||
|
this._enableNavAfterDemo();
|
||||||
|
},
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Faktencheck-Stati Demo
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
async _simulateFactcheckStati() {
|
||||||
|
this._demoRunning = true;
|
||||||
|
await this._wait(400);
|
||||||
|
var fcList = document.getElementById('factcheck-list');
|
||||||
|
if (!fcList) { this._demoRunning = false; this._enableNavAfterDemo(); return; }
|
||||||
|
|
||||||
|
var stati = ['confirmed','established','unconfirmed','disputed','contradicted'];
|
||||||
|
var prevX, prevY;
|
||||||
|
for (var i = 0; i < stati.length; i++) {
|
||||||
|
if (!this._isActive) break;
|
||||||
|
var item = fcList.querySelector('[data-fc-status="' + stati[i] + '"]');
|
||||||
|
if (!item) continue;
|
||||||
|
item.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||||
|
await this._wait(300);
|
||||||
|
var r = item.getBoundingClientRect();
|
||||||
|
var tx = r.left + 20, ty = r.top + r.height/2;
|
||||||
|
if (prevX !== undefined) {
|
||||||
|
await this._animateCursor(prevX, prevY, tx, ty, 400);
|
||||||
|
} else {
|
||||||
|
this._showCursor(tx - 40, ty - 30, 'default');
|
||||||
|
await this._wait(200);
|
||||||
|
await this._animateCursor(tx - 40, ty - 30, tx, ty, 400);
|
||||||
|
}
|
||||||
|
item.classList.add('tutorial-sub-highlight');
|
||||||
|
await this._wait(1500);
|
||||||
|
if (i < stati.length - 1) item.classList.remove('tutorial-sub-highlight');
|
||||||
|
prevX = tx; prevY = ty;
|
||||||
|
}
|
||||||
|
this._hideCursor();
|
||||||
|
this._demoRunning = false;
|
||||||
|
this._enableNavAfterDemo();
|
||||||
|
},
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Timeline Demo
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
async _simulateTimeline() {
|
||||||
|
this._demoRunning = true;
|
||||||
|
await this._wait(400);
|
||||||
|
var points = document.querySelectorAll('.ht-point');
|
||||||
|
if (!points.length) { this._demoRunning = false; this._enableNavAfterDemo(); return; }
|
||||||
|
var detailPanel = document.querySelector('.ht-detail-panel');
|
||||||
|
var entries = this._DEMO_TIMELINE;
|
||||||
|
var prevX, prevY;
|
||||||
|
for (var i = 0; i < Math.min(points.length, entries.length); i++) {
|
||||||
|
if (!this._isActive) break;
|
||||||
|
var point = points[i];
|
||||||
|
var entry = entries[i];
|
||||||
|
var r = point.getBoundingClientRect();
|
||||||
|
var tx = r.left + r.width/2, ty = r.top + r.height/2;
|
||||||
|
if (prevX !== undefined) {
|
||||||
|
await this._animateCursor(prevX, prevY, tx, ty, 400);
|
||||||
|
} else {
|
||||||
|
this._showCursor(tx - 40, ty - 30, 'default');
|
||||||
|
await this._wait(200);
|
||||||
|
await this._animateCursor(tx - 40, ty - 30, tx, ty, 400);
|
||||||
|
}
|
||||||
|
points.forEach(function(p) { p.classList.remove('active'); });
|
||||||
|
point.classList.add('active');
|
||||||
|
if (detailPanel) {
|
||||||
|
var isSn = entry.type === 'snapshot';
|
||||||
|
detailPanel.innerHTML = '<div class="ht-detail-header"><span class="ht-detail-time">16.03.2026, '
|
||||||
|
+ entry.time + '</span><span class="ht-detail-count">' + (isSn ? 'Lagebericht' : '1 Meldung')
|
||||||
|
+ '</span></div><div class="ht-detail-entries"><div class="ht-detail-entry"><div class="ht-detail-entry-source">'
|
||||||
|
+ entry.source + '</div><div class="ht-detail-entry-title">' + entry.title + '</div></div></div>';
|
||||||
|
}
|
||||||
|
await this._wait(1500);
|
||||||
|
prevX = tx; prevY = ty;
|
||||||
|
}
|
||||||
|
this._hideCursor();
|
||||||
|
this._demoRunning = false;
|
||||||
|
this._enableNavAfterDemo();
|
||||||
|
},
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Karte-Buttons Demo
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
async _simulateKarteButtons() {
|
||||||
|
this._demoRunning = true;
|
||||||
|
await this._wait(400);
|
||||||
|
if (this._demoMapTileMap) {
|
||||||
|
this._demoMapTileMap.invalidateSize();
|
||||||
|
this._demoMapTileMap.setView([53.545, 9.98], 12);
|
||||||
|
}
|
||||||
|
var geoBtn = document.getElementById('geoparse-btn');
|
||||||
|
var expandBtn = document.getElementById('map-expand-btn');
|
||||||
|
var prevX, prevY;
|
||||||
|
if (geoBtn) {
|
||||||
|
this._highlightSub('#geoparse-btn');
|
||||||
|
var gr = geoBtn.getBoundingClientRect();
|
||||||
|
this._showCursor(gr.left - 40, gr.top - 30, 'default');
|
||||||
|
await this._wait(300);
|
||||||
|
await this._animateCursor(gr.left-40, gr.top-30, gr.left+gr.width/2, gr.top+gr.height/2, 500);
|
||||||
|
prevX = gr.left+gr.width/2; prevY = gr.top+gr.height/2;
|
||||||
|
await this._wait(2000);
|
||||||
|
this._clearSubHighlights();
|
||||||
|
}
|
||||||
|
if (expandBtn) {
|
||||||
|
this._highlightSub('#map-expand-btn');
|
||||||
|
var er = expandBtn.getBoundingClientRect();
|
||||||
|
if (prevX !== undefined) {
|
||||||
|
await this._animateCursor(prevX, prevY, er.left+er.width/2, er.top+er.height/2, 500);
|
||||||
|
} else {
|
||||||
|
this._showCursor(er.left+er.width/2, er.top+er.height/2, 'default');
|
||||||
|
}
|
||||||
|
await this._wait(2000);
|
||||||
|
this._clearSubHighlights();
|
||||||
|
}
|
||||||
|
this._hideCursor();
|
||||||
|
this._demoRunning = false;
|
||||||
|
this._enableNavAfterDemo();
|
||||||
|
},
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Layout Demo (Drag + Resize kombiniert)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
async _simulateLayoutDemo() {
|
||||||
|
this._demoRunning = true;
|
||||||
|
var lbTile = document.querySelector('[gs-id="lagebild"]');
|
||||||
|
var fcTile = document.querySelector('[gs-id="faktencheck"]');
|
||||||
|
if (!lbTile || !fcTile) { this._demoRunning = false; this._enableNavAfterDemo(); return; }
|
||||||
|
var grid = typeof LayoutManager !== 'undefined' ? LayoutManager._grid : null;
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Phase 1: Drag
|
||||||
|
var lbH = lbTile.querySelector('.card-header');
|
||||||
|
if (lbH) {
|
||||||
|
var lbR = lbH.getBoundingClientRect();
|
||||||
|
var fcR = fcTile.getBoundingClientRect();
|
||||||
|
var sx = lbR.left+lbR.width/2, sy = lbR.top+lbR.height/2;
|
||||||
|
var ex = fcR.left+fcR.width/2, ey = fcR.top+lbR.height/2;
|
||||||
|
|
||||||
|
this._showCursor(sx-40, sy-30, 'default');
|
||||||
|
await this._wait(300);
|
||||||
|
await this._animateCursor(sx-40, sy-30, sx, sy, 400);
|
||||||
|
await this._wait(200);
|
||||||
|
this._els.cursor.classList.remove('tutorial-cursor-default');
|
||||||
|
this._els.cursor.classList.add('tutorial-cursor-grabbing');
|
||||||
|
await this._wait(200);
|
||||||
|
|
||||||
|
lbTile.style.transition = 'none';
|
||||||
|
lbTile.style.zIndex = '9002';
|
||||||
|
var dx = ex-sx, dy = ey-sy;
|
||||||
|
var ms = null;
|
||||||
|
await new Promise(function(resolve) {
|
||||||
|
function f(ts) {
|
||||||
|
if (!self._isActive) { resolve(); return; }
|
||||||
|
if (!ms) ms = ts;
|
||||||
|
var p = Math.min((ts-ms)/1200,1);
|
||||||
|
var e2 = p<0.5?4*p*p*p:1-Math.pow(-2*p+2,3)/2;
|
||||||
|
lbTile.style.transform = 'translate('+(dx*e2)+'px,'+(dy*e2)+'px)';
|
||||||
|
self._els.cursor.style.left = (sx+dx*e2)+'px';
|
||||||
|
self._els.cursor.style.top = (sy+dy*e2)+'px';
|
||||||
|
if (p<1) requestAnimationFrame(f); else resolve();
|
||||||
|
}
|
||||||
|
requestAnimationFrame(f);
|
||||||
|
});
|
||||||
|
await this._wait(400);
|
||||||
|
|
||||||
|
lbTile.style.transform = '';
|
||||||
|
lbTile.style.transition = '';
|
||||||
|
lbTile.style.zIndex = '';
|
||||||
|
if (grid) {
|
||||||
|
var ln = grid.engine.nodes.find(function(n){return n.el===lbTile;});
|
||||||
|
var fn = grid.engine.nodes.find(function(n){return n.el===fcTile;});
|
||||||
|
if (ln && fn) {
|
||||||
|
var tmpX=ln.x, tmpY=ln.y;
|
||||||
|
grid.update(lbTile,{x:fn.x,y:fn.y});
|
||||||
|
grid.update(fcTile,{x:tmpX,y:tmpY});
|
||||||
|
grid.compact();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._els.cursor.classList.remove('tutorial-cursor-grabbing');
|
||||||
|
this._els.cursor.classList.add('tutorial-cursor-default');
|
||||||
|
await this._wait(800);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2: Resize
|
||||||
|
lbTile = document.querySelector('[gs-id="lagebild"]');
|
||||||
|
if (lbTile) {
|
||||||
|
var tr = lbTile.getBoundingClientRect();
|
||||||
|
var hx = tr.right-6, hy = tr.bottom-6;
|
||||||
|
await this._animateCursor(
|
||||||
|
parseFloat(this._els.cursor.style.left)||hx-40,
|
||||||
|
parseFloat(this._els.cursor.style.top)||hy-30, hx, hy, 500);
|
||||||
|
await this._wait(200);
|
||||||
|
this._els.cursor.classList.remove('tutorial-cursor-default');
|
||||||
|
this._els.cursor.classList.add('tutorial-cursor-resize');
|
||||||
|
await this._wait(200);
|
||||||
|
|
||||||
|
lbTile.style.transition = 'none';
|
||||||
|
lbTile.style.zIndex = '9002';
|
||||||
|
lbTile.style.transformOrigin = 'top left';
|
||||||
|
var ow = tr.width, oh = tr.height;
|
||||||
|
var epx = ow*0.6, epy = oh*0.5;
|
||||||
|
var rs = null;
|
||||||
|
await new Promise(function(resolve) {
|
||||||
|
function f(ts) {
|
||||||
|
if (!self._isActive) { resolve(); return; }
|
||||||
|
if (!rs) rs = ts;
|
||||||
|
var p = Math.min((ts-rs)/1200,1);
|
||||||
|
var e3 = p<0.5?4*p*p*p:1-Math.pow(-2*p+2,3)/2;
|
||||||
|
lbTile.style.transform = 'scale('+(1+epx/ow*e3)+','+(1+epy/oh*e3)+')';
|
||||||
|
self._els.cursor.style.left = (hx+epx*e3)+'px';
|
||||||
|
self._els.cursor.style.top = (hy+epy*e3)+'px';
|
||||||
|
if (p<1) requestAnimationFrame(f); else resolve();
|
||||||
|
}
|
||||||
|
requestAnimationFrame(f);
|
||||||
|
});
|
||||||
|
await this._wait(600);
|
||||||
|
|
||||||
|
lbTile.style.transform = '';
|
||||||
|
lbTile.style.transformOrigin = '';
|
||||||
|
lbTile.style.transition = '';
|
||||||
|
lbTile.style.zIndex = '';
|
||||||
|
if (grid) {
|
||||||
|
var ln2 = grid.engine.nodes.find(function(n){return n.el===lbTile;});
|
||||||
|
if (ln2) { grid.update(lbTile,{w:12,h:Math.max(ln2.h+3,6)}); grid.compact(); }
|
||||||
|
}
|
||||||
|
await this._wait(600);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._hideCursor();
|
||||||
|
this._demoRunning = false;
|
||||||
|
this._enableNavAfterDemo();
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// Navigation nach Demo-Ende freigeben
|
// Navigation nach Demo-Ende freigeben
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren