fix: Marker-Duplikate & Spiderfy repariert | version 1.1.230

This commit is contained in:
ISA
2025-06-04 08:23:55 +02:00
parent 66afc1cee7
commit 569790d35f
6 changed files with 273 additions and 174 deletions

View File

@@ -4,17 +4,39 @@ Alle bedeutenden Änderungen an diesem Projekt werden in dieser Datei dokumentie
---
## [1.1.230] 2025-06-04
### 🐞 Fixed
- Gerätemarker wurden bei jeder Datenaktualisierung mehrfach gezeichnet → Marker-Layer vor dem
Neuladen jetzt korrekt geleert
- OverlappingMarkerSpiderfier (Spiderfy) bei überlappenden Geräten reaktiviert
### ♻️ Refactor
- Markerlogik in `useDynamicDeviceLayers.js` verbessert: Sichtbarkeit, Ersetzung und OMS-Handling
zentral geregelt
### 🔧 Version
- 📦 `appVersion.js` auf Version `1.1.230` erhöht
---
## [1.1.216] 2025-06-02
### Hinzugefügt
- Einheitliche Verwendung der Funktion `openInNewTab()` für Kontextmenü-Link bei Geräten, Linien und GMA.
- Kontextmenüpunkt „Station öffnen (Tab)“ öffnet nun korrekt `/devices/cpl.aspx?...` anstelle von z.B. `/cpl.aspx?...`.
- Einheitliche Verwendung der Funktion `openInNewTab()` für Kontextmenü-Link bei Geräten, Linien und
GMA.
- Kontextmenüpunkt „Station öffnen (Tab)“ öffnet nun korrekt `/devices/cpl.aspx?...` anstelle von
z.B. `/cpl.aspx?...`.
### Geändert
- Port 3000 wird aus generierten Links entfernt (auch in Entwicklungsumgebung).
- `setupPolylines.js`, `contextMenuUtils.js` und `useGmaMarkersLayer.js` angepasst, um einheitliche Navigation zu gewährleisten.
- `setupPolylines.js`, `contextMenuUtils.js` und `useGmaMarkersLayer.js` angepasst, um einheitliche
Navigation zu gewährleisten.
---
@@ -23,12 +45,14 @@ Alle bedeutenden Änderungen an diesem Projekt werden in dieser Datei dokumentie
### Hinzugefügt
- Leaflet-Kontextmenü für Geräte-Marker (Stations/Geräte) aktiviert
- Menüpunkt „Station öffnen (Tab)“ erscheint nun direkt im Marker-Kontextmenü, basierend auf `marker.options.link`
- Menüpunkt „Station öffnen (Tab)“ erscheint nun direkt im Marker-Kontextmenü, basierend auf
`marker.options.link`
- Redux-Abhängigkeit (`selectedDevice`) für Kontextmenü entfernt
### Geändert
- `addContextMenuToMarker` prüft nun direkt auf `marker.options.idDevice` und `link` anstatt globalen Zustand
- `addContextMenuToMarker` prüft nun direkt auf `marker.options.idDevice` und `link` anstatt
globalen Zustand
---
@@ -38,11 +62,13 @@ Alle bedeutenden Änderungen an diesem Projekt werden in dieser Datei dokumentie
- Dynamische Initialisierung aller Gerätegruppen-Layer (system-<IdSystem>) über GIS-Systemdaten.
- Automatische Sichtbarkeitssteuerung in `mapLayersSlice` basierend auf `IdSystem`.
- Mermaid-Diagramm zur Dokumentation der Architektur in `/docs/architecture/device-layer-connection-final.md`.
- Mermaid-Diagramm zur Dokumentation der Architektur in
`/docs/architecture/device-layer-connection-final.md`.
### Changed
- Vergleichslogik zwischen `gisStationsStaticDistrict[].System` und `gisSystemStatic[].IdSystem` von `Name` auf `IdSystem` umgestellt.
- Vergleichslogik zwischen `gisStationsStaticDistrict[].System` und `gisSystemStatic[].IdSystem` von
`Name` auf `IdSystem` umgestellt.
- `MapLayersControlPanel` verwendet jetzt konsistent `system-<IdSystem>` als Key.
- `useDynamicDeviceLayers` baut Layer-Gruppen und Marker-Zuordnung ebenfalls über `IdSystem` auf.
@@ -57,7 +83,8 @@ Alle bedeutenden Änderungen an diesem Projekt werden in dieser Datei dokumentie
### 🐞 Fixed
- Fehler behoben: Kontextmenü „Station öffnen (Tab)“ wurde bei Geräten/Stationen mehrfach angezeigt
- Ursache war doppelte Registrierung bei jedem Rechtsklick jetzt mit `contextMenuCreated`-Flag verhindert
- Ursache war doppelte Registrierung bei jedem Rechtsklick jetzt mit `contextMenuCreated`-Flag
verhindert
- Datei `createAndSetDevices.js` entsprechend angepasst
### 🔧 Version
@@ -66,13 +93,11 @@ Alle bedeutenden Änderungen an diesem Projekt werden in dieser Datei dokumentie
---
[1.1.190] 2025-05-27
🐞 Fixed
Dropdown im UI Widget MapLayersControlPanel zeigte keine Stations-/Bereichsnamen an
🔧 Fehler behoben: Zugriff auf GisStationsStaticDistrict.Points statt auf das Objekt selbst
[1.1.190] 2025-05-27 🐞 Fixed Dropdown im UI Widget MapLayersControlPanel zeigte keine
Stations-/Bereichsnamen an 🔧 Fehler behoben: Zugriff auf GisStationsStaticDistrict.Points statt auf
das Objekt selbst
📄 Dokumentation
Neue technische Markdown-Dokumentationen erstellt:
📄 Dokumentation Neue technische Markdown-Dokumentationen erstellt:
/docs/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.md
@@ -82,8 +107,7 @@ Neue technische Markdown-Dokumentationen erstellt:
/docs/components/uiWidgets/CoordinateInput.md
🔧 Version
📦 Version erhöht auf 1.1.190
🔧 Version 📦 Version erhöht auf 1.1.190
---
@@ -97,7 +121,8 @@ Neue technische Markdown-Dokumentationen erstellt:
### 🧠 Architektur
- `fetchGisLinesStatusService.js`, `fetchGisStationsStaticDistrictService.js`, `useGmaMarkersLayer.js`, `setupPolylines.js` u.a. angepasst
- `fetchGisLinesStatusService.js`, `fetchGisStationsStaticDistrictService.js`,
`useGmaMarkersLayer.js`, `setupPolylines.js` u.a. angepasst
- Links wie `Station öffnen (Tab)` oder WebService-URLs bauen sich nun dynamisch je nach basePath
### 📄 Dokumentation
@@ -118,7 +143,8 @@ Neue technische Markdown-Dokumentationen erstellt:
`/docs/pages/api/talas_v5_DB/priorityConfig.md`
- Beschreibt die API `/api/talas_v5_DB/priorityConfig`
- Enthält Beispielantwort, Datenstruktur, SQL, Fehlerhandling
- Besonderheit: Anwendung der Prioritätswerte (`level`) zur Sortierung von Leaflet-Markern bei Überlappung
- Besonderheit: Anwendung der Prioritätswerte (`level`) zur Sortierung von Leaflet-Markern bei
Überlappung
### 🧠 Architektur
@@ -162,8 +188,10 @@ Neue technische Markdown-Dokumentationen erstellt:
### 🔥 Removed
- Verzeichnis `/webServiceMockdata/` vollständig gelöscht
- Alle früheren Mock-Daten wurden durch einen realistischen Setup mit MySQL (Docker) und TALAS.web (unter IIS) ersetzt
- Kein fetch() oder Dateizugriff auf diese Daten mehr im Projekt vorhanden (geprüft via VSCode „Find in Files“)
- Alle früheren Mock-Daten wurden durch einen realistischen Setup mit MySQL (Docker) und TALAS.web
(unter IIS) ersetzt
- Kein fetch() oder Dateizugriff auf diese Daten mehr im Projekt vorhanden (geprüft via VSCode
„Find in Files“)
- Die Tests laufen nun gegen reale Backends (lokal & remote)
### 🧠 Architektur
@@ -203,13 +231,12 @@ Neue technische Markdown-Dokumentationen erstellt:
---
📦 [1.1.183] 2025-05-27
♻️ Refactor
Die Hilfsfunktion saveLineData() wurde vollständig entfernt:
📦 [1.1.183] 2025-05-27 ♻️ Refactor Die Hilfsfunktion saveLineData() wurde vollständig entfernt:
In markerUtils.js und poiUtils.js ersetzt durch updatePolylineCoordinatesThunk via Redux
Zentrale Dispatch-Hilfsfunktion savePolylineRedux() erstellt für alle Linienaktionen (Einfügen, Verschieben, Entfernen)
Zentrale Dispatch-Hilfsfunktion savePolylineRedux() erstellt für alle Linienaktionen (Einfügen,
Verschieben, Entfernen)
Einheitliche Verwendung des Redux-Dispatch in Utility-Dateien:
@@ -219,13 +246,11 @@ Fehlerbehandlung über .unwrap().catch(...) integriert
Map-Utilities und POI-Utilities sind nun Redux-kompatibel und testbar
✅ Clean
saveLineData aus mapUtils.js gelöscht
✅ Clean saveLineData aus mapUtils.js gelöscht
Alle Marker-Operationen speichern ihre Koordinaten ausschließlich über Redux
🧠 Architektur
Redux-Toolkit Standardstruktur umgesetzt:
🧠 Architektur Redux-Toolkit Standardstruktur umgesetzt:
updatePolylineCoordinatesService.js
@@ -233,10 +258,10 @@ updatePolylineCoordinatesThunk.js
Nutzung außerhalb React-Komponenten über store.dispatch(...)
Leaflet-Logik (z.B. marker.setLatLng(), map.removeLayer()) bleibt in Utility-Dateien Redux kümmert sich nur um Daten
Leaflet-Logik (z.B. marker.setLatLng(), map.removeLayer()) bleibt in Utility-Dateien Redux
kümmert sich nur um Daten
🔧 Version
📦 Version erhöht auf 1.1.183
🔧 Version 📦 Version erhöht auf 1.1.183
---
@@ -272,18 +297,18 @@ Leaflet-Logik (z.B. marker.setLatLng(), map.removeLayer()) bleibt in Utility-
---
[1.1.181] 2025-05-26
♻️ Refactor
setupPolylines.js von direktem fetch() auf sauberes Redux-Muster umgestellt:
[1.1.181] 2025-05-26 ♻️ Refactor setupPolylines.js von direktem fetch() auf sauberes Redux-Muster
umgestellt:
Statt fetch("/api/talas_v5_DB/gisLines/updateLineCoordinates") wird jetzt updatePolylineCoordinatesThunk() verwendet
Statt fetch("/api/talas_v5_DB/gisLines/updateLineCoordinates") wird jetzt
updatePolylineCoordinatesThunk() verwendet
Die Request-Daten werden an ein zentrales Service-Modul (updatePolylineCoordinatesService.js) übergeben
Die Request-Daten werden an ein zentrales Service-Modul (updatePolylineCoordinatesService.js)
übergeben
Async-Handling erfolgt über .unwrap().then().catch() zur besseren Fehlerkontrolle
🧠 Architektur
Einheitliches Redux-Schema umgesetzt:
🧠 Architektur Einheitliches Redux-Schema umgesetzt:
Service: updatePolylineCoordinatesService.js
@@ -293,19 +318,15 @@ Thunk: updatePolylineCoordinatesThunk.js
Redux Toolkit-konforme store.dispatch(...)-Verwendung in Utility-Datei (außerhalb von React-Kontext)
✅ Clean
Alle direkten fetch-Aufrufe aus setupPolylines.js entfernt
✅ Clean Alle direkten fetch-Aufrufe aus setupPolylines.js entfernt
Kein hartcodiertes URL-Handling mehr alles läuft über zentrale Redux-Logik
🔧 Version
📦 Version erhöht auf 1.1.181
🔧 Version 📦 Version erhöht auf 1.1.181
---
[1.1.180] 2025-05-26
♻️ Refactor
poiTypesSlice.js wurde bereinigt:
[1.1.180] 2025-05-26 ♻️ Refactor poiTypesSlice.js wurde bereinigt:
Alte createAsyncThunk-Definition entfernt (direkter fetch im Slice)
@@ -317,26 +338,24 @@ Service → Thunk → Slice
Keine Logik mehr im Slice selbst
Verwendete Komponenten (MapComponent, AddPOIModal) rufen jetzt korrekt dispatch(fetchPoiTypThunk()) auf
Verwendete Komponenten (MapComponent, AddPOIModal) rufen jetzt korrekt dispatch(fetchPoiTypThunk())
auf
🐞 Fixed
Fehler fetchPoiTypes is not a function behoben durch Entfernen des alten Imports aus poiTypesSlice
🐞 Fixed Fehler fetchPoiTypes is not a function behoben durch Entfernen des alten Imports aus
poiTypesSlice
Richtiger Import fetchPoiTypThunk nun überall verwendet
🧠 Architektur
Redux-Slice enthält nur noch Zustand & Status keine Abfragen mehr
🧠 Architektur Redux-Slice enthält nur noch Zustand & Status keine Abfragen mehr
fetchPoiTypService.js stellt fetch()-Logik bereit, Thunk übernimmt Fehlerbehandlung
🔧 Version
📦 Version erhöht auf 1.1.180
🔧 Version 📦 Version erhöht auf 1.1.180
---
[1.1.177] 2025-05-26
✨ UI-Verbesserung
POI-Tooltip auf der Karte wird jetzt bei Mouseover angezeigt (nicht mehr per Klick).
[1.1.177] 2025-05-26 ✨ UI-Verbesserung POI-Tooltip auf der Karte wird jetzt bei Mouseover
angezeigt (nicht mehr per Klick).
Tooltip ist einheitlich gestaltet mit Tailwind CSS:
@@ -350,46 +369,41 @@ Blaue Überschrift, graue Beschriftung
Einheitlicher Stil für alle Marker-Tooltips (setupPOIs.js angepasst).
✅ Clean
Alte bindPopup()-Logik entfernt kein manuelles Öffnen/Schließen mehr nötig
✅ Clean Alte bindPopup()-Logik entfernt kein manuelles Öffnen/Schließen mehr nötig
closePopup() aus mouseout-Handler entfernt
Tooltip verwendet sticky: true, folgt der Maus
🔧 Version
📦 Version erhöht auf 1.1.177
🔧 Version 📦 Version erhöht auf 1.1.177
---
[1.1.176] 2025-05-26
🐞 Fixed
Problem behoben, dass das Modal „POI hinzufügen“ nach erfolgreichem Submit nicht geschlossen wurde und die Meldung „Wird hinzugefügt…“ dauerhaft sichtbar blieb.
[1.1.176] 2025-05-26 🐞 Fixed Problem behoben, dass das Modal „POI hinzufügen“ nach erfolgreichem
Submit nicht geschlossen wurde und die Meldung „Wird hinzugefügt…“ dauerhaft sichtbar blieb.
Ursache: Die Service-Datei addPoiService.js verwendete die falsche URL /addLocation statt /addPoi.
Die unwrap()-Verwendung im AddPOIModal.js wurde beibehalten und korrekt abgeschlossen.
✅ Clean
resetAddPoiStatus() wird jetzt direkt nach onClose() dispatcht, um Status in Redux auf idle zurückzusetzen.
✅ Clean resetAddPoiStatus() wird jetzt direkt nach onClose() dispatcht, um Status in Redux auf idle
zurückzusetzen.
Ladeindikator „Wird hinzugefügt...“ wird zuverlässig entfernt.
Fehleranzeige funktioniert weiterhin über Redux (status === "failed" und error).
🧠 Architektur
Vollständige Redux-Anbindung des Modals über addPoiSlice, addPoiThunk, addPoiService und addPoi.js.
🧠 Architektur Vollständige Redux-Anbindung des Modals über addPoiSlice, addPoiThunk, addPoiService
und addPoi.js.
Thunk-Erfolg löst incrementTrigger() aus und synchronisiert die Marker auf der Karte durch erneutes Laden via fetchPoiMarkersThunk().
Thunk-Erfolg löst incrementTrigger() aus und synchronisiert die Marker auf der Karte durch erneutes
Laden via fetchPoiMarkersThunk().
🔧 Version
📦 Version erhöht auf 1.1.176
🔧 Version 📦 Version erhöht auf 1.1.176
---
[1.1.174] 2025-05-26
♻️ Refactor
useDrawLines.js vollständig auf Redux umgestellt:
[1.1.174] 2025-05-26 ♻️ Refactor useDrawLines.js vollständig auf Redux umgestellt:
Entfernt: direkter fetch("/api/talas_v5_DB/gisLines/readGisLines")
@@ -397,8 +411,7 @@ Stattdessen: Redux fetchGisLinesThunk() + selectGisLines verwendet
Datenverarbeitung der points erfolgt reaktiv über Redux-State
🧠 Architektur
useDrawLines.js verwendet jetzt:
🧠 Architektur useDrawLines.js verwendet jetzt:
fetchGisLinesService (Service)
@@ -408,21 +421,17 @@ gisLinesSlice mit Selektor selectGisLines
State wird automatisch im Redux verwaltet (loading/succeeded/failed)
✅ Cleanup
Promise-Kette .then().catch() durch useSelector-basierten Effekt ersetzt
✅ Cleanup Promise-Kette .then().catch() durch useSelector-basierten Effekt ersetzt
Fehlerausgabe bei ungültigen points strukturiert behandelt
Hook ist nun Redux-konform und testbar
🔧 Version
📦 Version erhöht auf 1.1.174
🔧 Version 📦 Version erhöht auf 1.1.174
---
[1.1.173] 2025-05-26
♻️ Refactor
useMapComponentState.js vollständig auf Redux umgestellt:
[1.1.173] 2025-05-26 ♻️ Refactor useMapComponentState.js vollständig auf Redux umgestellt:
Entfernt: lokale fetch(...)-Aufrufe
@@ -436,22 +445,19 @@ priorityConfig → über priorityConfigSlice + fetchPriorityConfigThunk
poiLayerVisible → direkter Zugriff über Redux-Zustand
🧠 Architektur
useMapComponentState.js ist jetzt rein selektorbasiert
🧠 Architektur useMapComponentState.js ist jetzt rein selektorbasiert
Standardstruktur Service → Thunk → Slice vollständig eingehalten
Komponente reagiert nur noch auf Redux-Status (z.B. poiTypStatus === "succeeded")
✅ Cleanup
useState() für priorityConfig und locationDeviceData entfernt
✅ Cleanup useState() für priorityConfig und locationDeviceData entfernt
deviceName wird direkt aus Redux-Daten abgeleitet
Selektor für poiLayerVisible wird direkt inline verwendet
🔧 Version
📦 Version erhöht auf 1.1.173
🔧 Version 📦 Version erhöht auf 1.1.173
---
@@ -460,7 +466,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### 🐞 Fixed
- POI-Icons wurden immer als `poi-marker-icon-4.png` dargestellt, egal welcher Typ
- Ursache: `setupPOIs.js` hat versehentlich `poi.idPoi === poi.idPoi` geprüft statt `poi.idPoiTyp === ...`
- Ursache: `setupPOIs.js` hat versehentlich `poi.idPoi === poi.idPoi` geprüft statt
`poi.idPoiTyp === ...`
### ✅ Clean
@@ -591,7 +598,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### 🐞 Fixed
- Fehler behoben: `userRights.includes(56)` schlug fehl, da Rechteobjekte `{ IdRight }` enthalten ersetzt durch `.some(r => r.IdRight === 56)`
- Fehler behoben: `userRights.includes(56)` schlug fehl, da Rechteobjekte `{ IdRight }` enthalten
ersetzt durch `.some(r => r.IdRight === 56)`
- Kontextmenü „POI bearbeiten“ wird jetzt korrekt angezeigt
- Modal öffnet sich nur noch bei gültiger Berechtigung (56)
@@ -630,7 +638,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### 🐞 Fixed
- Bug behoben: neu hinzugefügter POI zeigte Standard-Icon → Icon-Liste wird nach dem Hinzufügen erneut geladen
- Bug behoben: neu hinzugefügter POI zeigte Standard-Icon → Icon-Liste wird nach dem Hinzufügen
erneut geladen
### ✅ Clean
@@ -675,11 +684,13 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Removed
- `config.js` vollständig gelöscht keine Abhängigkeiten mehr im Projekt
- Funktion `isMockMode()` entfernt direkter Zugriff auf `process.env.NEXT_PUBLIC_USE_MOCK_API` ersetzt zentrale Hilfsfunktion
- Funktion `isMockMode()` entfernt direkter Zugriff auf `process.env.NEXT_PUBLIC_USE_MOCK_API`
ersetzt zentrale Hilfsfunktion
### Changed
- `fetchUserRightsService.js`, `fetchGisSystemStaticService.js` und `useMapComponentState.js` angepasst:
- `fetchUserRightsService.js`, `fetchGisSystemStaticService.js` und `useMapComponentState.js`
angepasst:
- Verwendet dynamisch `window.location` + `.env.local` statt config.js
- Verbesserte Übersichtlichkeit und zentrale Steuerung über `.env.local`
@@ -700,7 +711,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Entfernt
- `webserviceGisLinesStatusUrl` aus `config.js` entfernt URL wird zentral in `fetchGisLinesStatusService.js` aufgebaut.
- `webserviceGisLinesStatusUrl` aus `config.js` entfernt URL wird zentral in
`fetchGisLinesStatusService.js` aufgebaut.
### Geändert
@@ -718,7 +730,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Geändert
- `MapComponent.js` angepasst: `useLoadUserRights.js` entfernt, stattdessen Thunk + Selector verwendet
- `MapComponent.js` angepasst: `useLoadUserRights.js` entfernt, stattdessen Thunk + Selector
verwendet
- `fetchUserRightsThunk` direkt in `MapComponent.js` integriert
### Architektur
@@ -754,13 +767,15 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Changed
- 🔁 Geräte-/Stationsanzeige vollständig auf **Redux-Store** umgestellt:
- `createAndSetDevices.js` verwendet jetzt nur noch Redux-Selectoren (`selectGisStationsStaticDistrict`, `selectGisStationsStatusDistrict`)
- `createAndSetDevices.js` verwendet jetzt nur noch Redux-Selectoren
(`selectGisStationsStaticDistrict`, `selectGisStationsStatusDistrict`)
- Entfernt: direkter `fetch(...)`-Zugriff über `config.js`
- Kein Zugriff mehr auf `mapGisStationsStaticDistrictUrl` / `StatusDistrictUrl`
### Fixed
- ✅ Fehler "❌ Redux enthält keine gültigen Geräte-/Statusdaten!" gelöst durch korrekte Abfrage `state.gisStationsStaticDistrict.data.Points`
- ✅ Fehler "❌ Redux enthält keine gültigen Geräte-/Statusdaten!" gelöst durch korrekte Abfrage
`state.gisStationsStaticDistrict.data.Points`
- ✅ Marker erscheinen wieder zuverlässig durch saubere Trennung von Datenquelle und Darstellung
### Architecture
@@ -815,7 +830,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Refactored
- Entfernt: manuelle Fetch-Funktion und lokalen `isDataLoaded`-State für `DataSheet`
- Hinzugefügt: Anzeige des Panels erfolgt jetzt über Redux Store (`GisStationsStaticDistrict.Points`)
- Hinzugefügt: Anzeige des Panels erfolgt jetzt über Redux Store
(`GisStationsStaticDistrict.Points`)
- Verbesserte Kontrolle über Sichtbarkeit der Layer-Steuerung auf Basis von Store-Daten
---
@@ -829,7 +845,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
- Neuer Thunk: `fetchGisLinesThunk.js` unter `/redux/thunks/database/`
- Neuer Slice: `gisLinesSlice.js` mit Statusverwaltung (idle/loading/succeeded/failed)
- Redux-Selektor `selectGisLines` in `MapComponent.js` genutzt
- Direkter `fetch("/api/talas_v5_DB/gisLines/readGisLines")` entfernt und durch `dispatch(fetchGisLinesThunk())` ersetzt
- Direkter `fetch("/api/talas_v5_DB/gisLines/readGisLines")` entfernt und durch
`dispatch(fetchGisLinesThunk())` ersetzt
- Alle Linien-Daten werden nun zentral über Redux geladen und in `linePositions` umgewandelt
### Version
@@ -852,8 +869,10 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Fixed
- ❌ Fehler `fetchPriorityConfigThunk is not defined` behoben durch Import und richtigen Store-Zugriff
- ❌ Bug: Redux Slice zeigte keine Daten Ursache war fehlendes `dispatch` der Thunk-Funktion → behoben
- ❌ Fehler `fetchPriorityConfigThunk is not defined` behoben durch Import und richtigen
Store-Zugriff
- ❌ Bug: Redux Slice zeigte keine Daten Ursache war fehlendes `dispatch` der Thunk-Funktion →
behoben
### Cleanup
@@ -862,7 +881,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Architekturhinweis
- 🔄 Komponente wie `MapComponent` ist der Trigger:
Thunks, Services und Redux-Slices alleine führen nichts aus sie müssen durch ein `dispatch(...)` aktiviert werden.
Thunks, Services und Redux-Slices alleine führen nichts aus sie müssen durch ein `dispatch(...)`
aktiviert werden.
### Version
@@ -875,10 +895,13 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Changed
- 🧱 `fetchLocationDevices.js` wurde vollständig entfernt (lag zuvor unter `/redux/api/fromDB/`)
- Stattdessen wird der neue Service `fetchLocationDevicesService.js` in `/services/database/` verwendet
- Stattdessen wird der neue Service `fetchLocationDevicesService.js` in `/services/database/`
verwendet
- Neuer Thunk `fetchLocationDevicesThunk.js` unter `/redux/thunks/database/` eingeführt
- `locationDevicesFromDBSlice.js` ersetzt durch `locationDevicesSlice.js` mit Anbindung an den neuen Thunk
- `MapComponent.js` nutzt jetzt `dispatch(fetchLocationDevicesThunk())` zur Initialisierung der Geräte
- `locationDevicesFromDBSlice.js` ersetzt durch `locationDevicesSlice.js` mit Anbindung an den neuen
Thunk
- `MapComponent.js` nutzt jetzt `dispatch(fetchLocationDevicesThunk())` zur Initialisierung der
Geräte
### Cleanup
@@ -895,7 +918,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Cleanup
- 🧼 Veraltete GIS-API-Fetch-Dateien entfernt, da nun vollständig durch zentrale Redux-Architektur ersetzt:
- 🧼 Veraltete GIS-API-Fetch-Dateien entfernt, da nun vollständig durch zentrale Redux-Architektur
ersetzt:
- `fetchGisStationsMeasurements.js`
- `fetchGisStationsStatic.js`
- `fetchGisStationsStaticDistrict.js`
@@ -937,7 +961,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
- `fetchGisStationsStaticDistrict.js`
- `fetchGisStationsStatusDistrict.js`
- `fetchGisSystemStatic.js`
- Alle Datenquellen werden jetzt ausschließlich über zentrale Redux Thunks und zugehörige Services geladen
- Alle Datenquellen werden jetzt ausschließlich über zentrale Redux Thunks und zugehörige Services
geladen
- Alte Fetch-Struktur (`/redux/api/fromWebService`) vollständig obsolet
### Fixed
@@ -960,7 +985,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Changed
- 🔁 GIS-Datenquellen konsolidiert: Statt 5 werden jetzt nur 4 zentrale Redux-Slices verwendet:
- ✅ Beibehalten: `gisStationsMeasurements`, `gisStationsStaticDistrict`, `gisStationsStatusDistrict`, `gisSystemStatic`
- ✅ Beibehalten: `gisStationsMeasurements`, `gisStationsStaticDistrict`,
`gisStationsStatusDistrict`, `gisSystemStatic`
- ❌ Entfernt: `gisStationsStatic` (veraltet / wurde durch `StaticDistrict` ersetzt)
### Fixed
@@ -973,7 +999,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
- 🧼 Thunks, Services und Slices konsistent:
- Alle Slices verwenden jetzt zentrale Thunks
- Nicht benötigte Dateien (`fetchGisStatusStationsService.js`, `fetchGisStatusStationsThunk.js`, `gisStationsStaticSlice.js`) entfernt
- Nicht benötigte Dateien (`fetchGisStatusStationsService.js`, `fetchGisStatusStationsThunk.js`,
`gisStationsStaticSlice.js`) entfernt
- Redux DevTools zeigen saubere States mit `status: "succeeded"` für alle vier aktiven GIS-Quellen
---
@@ -1052,7 +1079,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
- `/services/database/` für eigene Next.js-APIs (Port 3000)
- `/services/utils/` für Hilfsfunktionen (z.B. `fetchWithTimeout`)
- Alle Service-Dateien konsistent benannt nach Schema: `fetchXyzService.js`
- Beispiel: `fetchGisStationsMeasurementsService.js`, `fetchPoiData.js`, `updateLocationInDatabase.js`
- Beispiel: `fetchGisStationsMeasurementsService.js`, `fetchPoiData.js`,
`updateLocationInDatabase.js`
### Motivation
@@ -1070,7 +1098,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
- `AddPoiModalWindowWrapper.js`
- `PoiUpdateModalWrapper.js`
- `PoiUpdateModalWindow.js`
- Ersetzt durch moderne Komponenten mit direkter Redux-Anbindung (`ShowAddStationPopup`, `PoiUpdateModal`)
- Ersetzt durch moderne Komponenten mit direkter Redux-Anbindung (`ShowAddStationPopup`,
`PoiUpdateModal`)
### Cleanup
@@ -1124,7 +1153,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Refactor
- 🔁 Vorbereitung zur Aufteilung von `useLineData.js` und `useMapComponentState.js` in separate Redux-Slices & spezialisierte Hooks
- 🔁 Vorbereitung zur Aufteilung von `useLineData.js` und `useMapComponentState.js` in separate
Redux-Slices & spezialisierte Hooks
- 🟡 Validierte React-Hooks beibehalten (z.B. Marker-Handling & Layer-Logik)
---
@@ -1155,7 +1185,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Changed
- Setup-Dateien (`node-v20.12.1-x64.msi`, `nssm.exe`, `ChromeStandaloneSetup64.exe`) aus dem Repository entfernt
- Setup-Dateien (`node-v20.12.1-x64.msi`, `nssm.exe`, `ChromeStandaloneSetup64.exe`) aus dem
Repository entfernt
- Stattdessen Verlinkung in `README.md` zu SharePoint-Ordner für interne Tool-Downloads eingefügt
- Projektstruktur aufgeräumt Installationsdateien blähen Git-Verlauf nicht mehr auf
- Hinweis in `README.md` zu Projektorganisation, Tool-Voraussetzungen und Linkstrategie ergänzt
@@ -1417,7 +1448,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
- `config.js` angepasst:
- Entfernt: `NEXT_PUBLIC_SERVER_URL` aus `.env.local`
- Dynamische Berechnung von `serverURL` anhand `window.location` + `NEXT_PUBLIC_API_PORT_MODE`
- Zugriff auf Webservices funktioniert jetzt automatisch abhängig von Entwicklungs-/Produktionsumgebung
- Zugriff auf Webservices funktioniert jetzt automatisch abhängig von
Entwicklungs-/Produktionsumgebung
### Added
@@ -1535,7 +1567,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Changed
- `idMap` und `idUser` werden nicht mehr aus `.env.local` gelesen, sondern ausschließlich über die URL übergeben (z.B. von TALAS.web).
- `idMap` und `idUser` werden nicht mehr aus `.env.local` gelesen, sondern ausschließlich über die
URL übergeben (z.B. von TALAS.web).
- Entfernt: Fallback-Variablen `NEXT_PUBLIC_DEFAULT_ID_MAP` und `NEXT_PUBLIC_DEFAULT_ID_USER`.
- Dokumentation in `docs/redux/api/fromWebService.md` entsprechend angepasst.

View File

@@ -1,19 +1,22 @@
// /components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js
import React, { useEffect, useState } from "react";
import { setSelectedArea } from "../../../redux/slices/selectedAreaSlice";
import EditModeToggle from "./EditModeToggle";
import { setSelectedArea } from "@/redux/slices/selectedAreaSlice";
import EditModeToggle from "@/components/uiWidgets/mapLayersControlPanel/EditModeToggle";
import { useSelector, useDispatch } from "react-redux";
import { selectPolylineVisible, setPolylineVisible } from "../../../redux/slices/database/polylines/polylineLayerVisibleSlice";
import { selectGisSystemStatic } from "../../../redux/slices/webservice/gisSystemStaticSlice";
import { selectGisStationsStaticDistrict } from "../../../redux/slices/webservice/gisStationsStaticDistrictSlice";
import { selectMapLayersState, setLayerVisibility } from "../../../redux/slices/mapLayersSlice";
import { setVisible } from "../../../redux/slices/database/pois/poiLayerVisibleSlice";
import { incrementZoomTrigger } from "../../../redux/slices/zoomTriggerSlice";
import {
selectPolylineVisible,
setPolylineVisible,
} from "@/redux/slices/database/polylines/polylineLayerVisibleSlice";
import { selectGisSystemStatic } from "@/redux/slices/webservice/gisSystemStaticSlice";
import { selectGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice";
import { selectMapLayersState, setLayerVisibility } from "@/redux/slices/mapLayersSlice";
import { setVisible } from "@/redux/slices/database/pois/poiLayerVisibleSlice";
import { incrementZoomTrigger } from "@/redux/slices/zoomTriggerSlice";
function MapLayersControlPanel() {
const [editMode, setEditMode] = useState(false); // Zustand für editMode
const poiVisible = useSelector((state) => state.poiLayerVisible.visible);
const setPoiVisible = (value) => dispatch(setVisible(value));
const poiVisible = useSelector(state => state.poiLayerVisible.visible);
const setPoiVisible = value => dispatch(setVisible(value));
const dispatch = useDispatch();
const mapLayersVisibility = useSelector(selectMapLayersState);
const [stationListing, setStationListing] = useState([]);
@@ -23,14 +26,17 @@ function MapLayersControlPanel() {
const polylineVisible = useSelector(selectPolylineVisible);
const handlePolylineCheckboxChange = (event) => {
const handlePolylineCheckboxChange = event => {
const checked = event.target.checked;
dispatch(setPolylineVisible(checked));
localStorage.setItem("polylineVisible", checked);
if (checked) {
dispatch(setLayerVisibility({ layer: "TALAS", visibility: true }));
localStorage.setItem("mapLayersVisibility", JSON.stringify({ ...mapLayersVisibility, TALAS: true }));
localStorage.setItem(
"mapLayersVisibility",
JSON.stringify({ ...mapLayersVisibility, TALAS: true })
);
}
};
@@ -49,7 +55,7 @@ function MapLayersControlPanel() {
const storedMapLayersVisibility = localStorage.getItem("mapLayersVisibility");
if (storedMapLayersVisibility) {
const parsedVisibility = JSON.parse(storedMapLayersVisibility);
Object.keys(parsedVisibility).forEach((key) => {
Object.keys(parsedVisibility).forEach(key => {
dispatch(setLayerVisibility({ layer: key, visibility: parsedVisibility[key] }));
});
}
@@ -59,18 +65,20 @@ function MapLayersControlPanel() {
setEditMode(storedEditMode === "true");
}, [setPoiVisible, dispatch]); // ✅ `setMapLayersVisibility` entfernt
const handleAreaChange = (event) => {
const handleAreaChange = event => {
const selectedIndex = event.target.options.selectedIndex;
const areaName = event.target.options[selectedIndex].text;
dispatch(setSelectedArea(areaName));
};
useEffect(() => {
const allowedSystems = Array.isArray(GisSystemStatic) ? new Set(GisSystemStatic.filter((system) => system.Allow === 1).map((system) => system.IdSystem)) : new Set();
const allowedSystems = Array.isArray(GisSystemStatic)
? new Set(GisSystemStatic.filter(system => system.Allow === 1).map(system => system.IdSystem))
: new Set();
const seenNames = new Set();
const filteredAreas = GisStationsStaticDistrict?.Points?.length
? GisStationsStaticDistrict.Points.filter((item) => {
? GisStationsStaticDistrict.Points.filter(item => {
const isUnique = !seenNames.has(item.Area_Name) && allowedSystems.has(item.System);
if (isUnique) {
seenNames.add(item.Area_Name);
@@ -88,7 +96,7 @@ function MapLayersControlPanel() {
const seenSystemNames = new Set();
const filteredSystems = Array.isArray(GisSystemStatic)
? GisSystemStatic.filter((item) => {
? GisSystemStatic.filter(item => {
const isUnique = !seenSystemNames.has(item.Name) && item.Allow === 1;
if (isUnique) {
seenSystemNames.add(item.Name);
@@ -111,7 +119,10 @@ function MapLayersControlPanel() {
const { checked } = event.target;
dispatch(setLayerVisibility({ layer: key, visibility: checked }));
localStorage.setItem("mapLayersVisibility", JSON.stringify({ ...mapLayersVisibility, [key]: checked }));
localStorage.setItem(
"mapLayersVisibility",
JSON.stringify({ ...mapLayersVisibility, [key]: checked })
);
setTimeout(() => {
const event = new Event("visibilityChanged");
@@ -119,7 +130,7 @@ function MapLayersControlPanel() {
}, 0);
};
const handlePoiCheckboxChange = (event) => {
const handlePoiCheckboxChange = event => {
const { checked } = event.target;
setPoiVisible(checked);
localStorage.setItem("poiVisible", checked); // Store POI visibility in localStorage
@@ -145,12 +156,15 @@ function MapLayersControlPanel() {
}
if (!GisStationsStaticDistrict.Points || !Array.isArray(GisStationsStaticDistrict.Points)) {
console.warn("⚠️ GisStationsStaticDistrict.Points ist nicht vorhanden oder kein Array.", GisStationsStaticDistrict);
console.warn(
"⚠️ GisStationsStaticDistrict.Points ist nicht vorhanden oder kein Array.",
GisStationsStaticDistrict
);
return;
}
const seenNames = new Set();
const filteredAreas = GisStationsStaticDistrict.Points.filter((item) => {
const filteredAreas = GisStationsStaticDistrict.Points.filter(item => {
if (!item.Area_Name) return false; // Sicherstellen, dass Area_Name existiert
const isUnique = !seenNames.has(item.Area_Name);
if (isUnique) {
@@ -164,12 +178,20 @@ function MapLayersControlPanel() {
//---------------------------
return (
<div id="mainDataSheet" className="absolute top-3 right-3 w-1/6 min-w-[300px] max-w-[400px] z-10 bg-white p-2 rounded-lg shadow-lg">
<div
id="mainDataSheet"
className="absolute top-3 right-3 w-1/6 min-w-[300px] max-w-[400px] z-10 bg-white p-2 rounded-lg shadow-lg"
>
<div className="flex flex-col gap-4 p-4">
<div className="flex items-center justify-between space-x-2">
<select onChange={handleAreaChange} id="stationListing" className="border-solid-1 p-2 rounded ml-1 font-semibold" style={{ minWidth: "150px", maxWidth: "200px" }}>
<select
onChange={handleAreaChange}
id="stationListing"
className="border-solid-1 p-2 rounded ml-1 font-semibold"
style={{ minWidth: "150px", maxWidth: "200px" }}
>
<option value="Station wählen">Station wählen</option>
{stationListing.map((station) => (
{stationListing.map(station => (
<option key={station.id} value={station.id}>
{station.name}
</option>
@@ -177,19 +199,24 @@ function MapLayersControlPanel() {
</select>
<div className="flex items-center space-x-2">
<EditModeToggle />
<img src="/img/expand-icon.svg" alt="Expand" className="h-6 w-6 cursor-pointer" onClick={handleIconClick} />
<img
src="/img/expand-icon.svg"
alt="Expand"
className="h-6 w-6 cursor-pointer"
onClick={handleIconClick}
/>
</div>
</div>
{/* Checkboxen mit Untermenüs */}
<div className="flex flex-col gap-2">
{systemListing.map((system) => (
{systemListing.map(system => (
<div key={system.id} className="flex flex-col">
<div className="flex items-center">
<input
type="checkbox"
checked={mapLayersVisibility[system.key] || false}
onChange={(e) => handleCheckboxChange(system.key, e)}
onChange={e => handleCheckboxChange(system.key, e)}
id={`system-${system.id}`}
disabled={editMode} // Checkbox deaktiviert, wenn editMode aktiv ist
/>
@@ -218,7 +245,12 @@ function MapLayersControlPanel() {
))}
<div className="flex items-center">
<input type="checkbox" checked={poiVisible} onChange={handlePoiCheckboxChange} id="poi-checkbox" />
<input
type="checkbox"
checked={poiVisible}
onChange={handlePoiCheckboxChange}
id="poi-checkbox"
/>
<label htmlFor="poi-checkbox" className="text-sm ml-2">
POIs
</label>

View File

@@ -1,2 +1,2 @@
// /config/appVersion
export const APP_VERSION = "1.1.230";
export const APP_VERSION = "1.1.231";

View File

@@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from "react";
import L from "leaflet";
import { createAndSetDevices } from "../utils/devices/createAndSetDevices";
import { checkOverlappingMarkers } from "../utils/mapUtils";
import plusRoundIcon from "../components/icons/devices/overlapping/PlusRoundIcon";
import plusRoundIcon from "@/components/icons/devices/overlapping/PlusRoundIcon";
/**
* Dynamisch GIS-System-Marker erstellen & Sichtbarkeit steuern.
@@ -31,14 +31,27 @@ const useDynamicDeviceLayers = (map, GisSystemStatic, mapLayersVisibility, prior
createAndSetDevices(
IdSystem,
newMarkers => {
setMarkerStates(prev => ({ ...prev, [key]: newMarkers }));
const oldMarkers = markerStates[key];
// ❌ NICHT direkt zur Karte hinzufügen
// Sichtbarkeit folgt im 2. useEffect
checkOverlappingMarkers(map, newMarkers, plusRoundIcon, oms);
// Entferne alte Marker aus Karte und OMS
if (oldMarkers && Array.isArray(oldMarkers)) {
oldMarkers.forEach(marker => {
if (map.hasLayer(marker)) {
map.removeLayer(marker);
}
if (oms) {
oms.removeMarker(marker);
}
});
}
// Neue Marker setzen
setMarkerStates(prev => ({ ...prev, [key]: newMarkers }));
},
GisSystemStatic,
priorityConfig
priorityConfig,
undefined,
oms
);
});
}, [map, GisSystemStatic, priorityConfig]);

View File

@@ -4,22 +4,22 @@ import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
// Redux: POI-Typen
import { fetchPoiTypThunk } from "../redux/thunks/database/pois/fetchPoiTypThunk";
import { selectPoiTypData, selectPoiTypStatus } from "../redux/slices/database/pois/poiTypSlice";
import { fetchPoiTypThunk } from "@/redux/thunks/database/pois/fetchPoiTypThunk";
import { selectPoiTypData, selectPoiTypStatus } from "@/redux/slices/database/pois/poiTypSlice";
// Redux: GIS Geräte
import { fetchGisStationsStaticDistrictThunk } from "../redux/thunks/webservice/fetchGisStationsStaticDistrictThunk";
import { selectGisStationsStaticDistrict } from "../redux/slices/webservice/gisStationsStaticDistrictSlice";
import { fetchGisStationsStaticDistrictThunk } from "@/redux/thunks/webservice/fetchGisStationsStaticDistrictThunk";
import { selectGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice";
// Redux: priorityConfig
import { fetchPriorityConfigThunk } from "../redux/thunks/database/fetchPriorityConfigThunk";
import { selectPriorityConfig } from "../redux/slices/database/priorityConfigSlice";
import { fetchPriorityConfigThunk } from "@/redux/thunks/database/fetchPriorityConfigThunk";
import { selectPriorityConfig } from "@/redux/slices/database/priorityConfigSlice";
export const useMapComponentState = () => {
const dispatch = useDispatch();
// Redux: Sichtbarkeit des POI-Layers
const poiLayerVisible = useSelector((state) => state.poiLayerVisible.visible);
const poiLayerVisible = useSelector(state => state.poiLayerVisible.visible);
// Redux: POI-Typen
const poiTypData = useSelector(selectPoiTypData);

View File

@@ -1,12 +1,12 @@
// /utils/devices/createAndSetDevices.js
import L from "leaflet";
import "leaflet.smooth_marker_bouncing";
import { store } from "../../redux/store.js";
import { setSelectedDevice } from "../../redux/slices/selectedDeviceSlice.js";
import { selectGisStationsStaticDistrict } from "../../redux/slices/webservice/gisStationsStaticDistrictSlice.js";
import { selectGisStationsStatusDistrict } from "../../redux/slices/webservice/gisStationsStatusDistrictSlice.js";
import { selectGisStationsMeasurements } from "../../redux/slices/webservice/gisStationsMeasurementsSlice.js";
import { addContextMenuToMarker } from "../../utils/contextMenuUtils";
import { store } from "@/redux/store.js";
import { setSelectedDevice } from "@/redux/slices/selectedDeviceSlice.js";
import { selectGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice.js";
import { selectGisStationsStatusDistrict } from "@/redux/slices/webservice/gisStationsStatusDistrictSlice.js";
import { selectGisStationsMeasurements } from "@/redux/slices/webservice/gisStationsMeasurementsSlice.js";
import { addContextMenuToMarker } from "@/utils/contextMenuUtils";
const determinePriority = (iconPath, priorityConfig) => {
for (let priority of priorityConfig) {
@@ -17,7 +17,14 @@ const determinePriority = (iconPath, priorityConfig) => {
return 5;
};
export const createAndSetDevices = async (systemId, setMarkersFunction, GisSystemStatic, priorityConfig, measurements) => {
export const createAndSetDevices = async (
systemId,
setMarkersFunction,
GisSystemStatic,
priorityConfig,
measurements,
oms // 🔁 OMS für Spiderfy hinzugefügt
) => {
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
try {
@@ -28,20 +35,24 @@ export const createAndSetDevices = async (systemId, setMarkersFunction, GisSyste
if (!staticDistrictData?.Points?.length || !statusDistrictData?.length) return;
const statisMap = new Map(statusDistrictData.map((s) => [s.IdLD, s]));
const statisMap = new Map(statusDistrictData.map(s => [s.IdLD, s]));
const measurementsMap = new Map();
measurementData?.forEach((m) => {
measurementData?.forEach(m => {
if (!measurementsMap.has(m.IdLD)) {
measurementsMap.set(m.IdLD, m);
}
});
const activeStations = staticDistrictData.Points.filter((station) => station.System === systemId && station.Active === 1);
const activeStations = staticDistrictData.Points.filter(
station => station.System === systemId && station.Active === 1
);
const markersData = activeStations.map((station) => {
const markersData = activeStations.map(station => {
const statis = statisMap.get(station.IdLD);
const messung = measurementsMap.get(station.IdLD);
const iconPath = statis ? `img/icons/${statis.Na}-marker-icon-${station.Icon}.png` : `img/icons/marker-icon-${station.Icon}.png`;
const iconPath = statis
? `img/icons/${statis.Na}-marker-icon-${station.Icon}.png`
: `img/icons/marker-icon-${station.Icon}.png`;
const priority = determinePriority(iconPath, priorityConfig);
const zIndexOffset = 100 * (5 - priority);
@@ -60,19 +71,23 @@ export const createAndSetDevices = async (systemId, setMarkersFunction, GisSyste
idDevice: station.IdLD,
});
// ✅ Popups nur für Statusdaten
// ✅ Popup
let popupContent = `
<div class="bg-white rounded-lg">
<span class="text-lg font-semibold text-gray-900">${station.LD_Name}</span>
<span class="text-md font-bold text-gray-800">${station.Device}</span><br>
<span class="text-gray-800"><strong>${station.Area_Short}</strong> (${station.Area_Name})</span><br>
<span class="text-gray-800"><strong>${station.Location_Short}</strong> (${station.Location_Name})</span>
<span class="text-gray-800"><strong>${station.Area_Short}</strong> (${
station.Area_Name
})</span><br>
<span class="text-gray-800"><strong>${station.Location_Short}</strong> (${
station.Location_Name
})</span>
<div class="mt-2">
${statusDistrictData
.filter((status) => status.IdLD === station.IdLD)
.filter(status => status.IdLD === station.IdLD)
.reverse()
.map(
(status) => `
status => `
<div class="flex items-center my-1">
<div class="w-2 h-2 mr-2 inline-block rounded-full" style="background-color: ${status.Co};"></div>
${status.Me}&nbsp;<span style="color: ${status.Co};">(${status.Na})</span>
@@ -81,9 +96,10 @@ export const createAndSetDevices = async (systemId, setMarkersFunction, GisSyste
.join("")}
</div>
</div>`;
marker.bindPopup(popupContent);
// ✅ Tooltip im GMA-Stil mit Messwerten
// ✅ Tooltip (nur für System 11)
if (station.System === 11 && messung) {
const lt = messung["LT"] ?? "-";
const fbt = messung["FBT"] ?? "-";
@@ -132,6 +148,11 @@ export const createAndSetDevices = async (systemId, setMarkersFunction, GisSyste
addContextMenuToMarker(marker);
// ✅ OverlappingMarkerSpiderfier hinzufügen
if (oms) {
oms.addMarker(marker);
}
return marker;
});