diff --git a/CHANGELOG.md b/CHANGELOG.md index ad94af0f5..ab79c57a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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-) ü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-` 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. diff --git a/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js b/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js index 5f9b8340c..8c6298d2c 100644 --- a/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js +++ b/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js @@ -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 ( -
+
- - {stationListing.map((station) => ( + {stationListing.map(station => ( @@ -177,19 +199,24 @@ function MapLayersControlPanel() {
- Expand + Expand
{/* Checkboxen mit Untermenüs */}
- {systemListing.map((system) => ( + {systemListing.map(system => (
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() { ))}
- + diff --git a/config/appVersion.js b/config/appVersion.js index b018a82f1..c679f1db5 100644 --- a/config/appVersion.js +++ b/config/appVersion.js @@ -1,2 +1,2 @@ // /config/appVersion -export const APP_VERSION = "1.1.230"; +export const APP_VERSION = "1.1.231"; diff --git a/hooks/useDynamicDeviceLayers.js b/hooks/useDynamicDeviceLayers.js index 598e3077f..7af95cae5 100644 --- a/hooks/useDynamicDeviceLayers.js +++ b/hooks/useDynamicDeviceLayers.js @@ -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]); diff --git a/hooks/useMapComponentState.js b/hooks/useMapComponentState.js index 61bbff89e..646760860 100644 --- a/hooks/useMapComponentState.js +++ b/hooks/useMapComponentState.js @@ -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); diff --git a/utils/devices/createAndSetDevices.js b/utils/devices/createAndSetDevices.js index 11bd28db1..3013fc6d8 100644 --- a/utils/devices/createAndSetDevices.js +++ b/utils/devices/createAndSetDevices.js @@ -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 = `
${station.LD_Name} ${station.Device}
- ${station.Area_Short} (${station.Area_Name})
- ${station.Location_Short} (${station.Location_Name}) + ${station.Area_Short} (${ + station.Area_Name + })
+ ${station.Location_Short} (${ + station.Location_Name + })
${statusDistrictData - .filter((status) => status.IdLD === station.IdLD) + .filter(status => status.IdLD === station.IdLD) .reverse() .map( - (status) => ` + status => `
${status.Me} (${status.Na}) @@ -81,9 +96,10 @@ export const createAndSetDevices = async (systemId, setMarkersFunction, GisSyste .join("")}
`; + 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; });