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 ## [1.1.216] 2025-06-02
### Hinzugefügt ### Hinzugefügt
- Einheitliche Verwendung der Funktion `openInNewTab()` für Kontextmenü-Link bei Geräten, Linien und GMA. - Einheitliche Verwendung der Funktion `openInNewTab()` für Kontextmenü-Link bei Geräten, Linien und
- Kontextmenüpunkt „Station öffnen (Tab)“ öffnet nun korrekt `/devices/cpl.aspx?...` anstelle von z.B. `/cpl.aspx?...`. GMA.
- Kontextmenüpunkt „Station öffnen (Tab)“ öffnet nun korrekt `/devices/cpl.aspx?...` anstelle von
z.B. `/cpl.aspx?...`.
### Geändert ### Geändert
- Port 3000 wird aus generierten Links entfernt (auch in Entwicklungsumgebung). - 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 ### Hinzugefügt
- Leaflet-Kontextmenü für Geräte-Marker (Stations/Geräte) aktiviert - 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 - Redux-Abhängigkeit (`selectedDevice`) für Kontextmenü entfernt
### Geändert ### 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. - Dynamische Initialisierung aller Gerätegruppen-Layer (system-<IdSystem>) über GIS-Systemdaten.
- Automatische Sichtbarkeitssteuerung in `mapLayersSlice` basierend auf `IdSystem`. - 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 ### 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. - `MapLayersControlPanel` verwendet jetzt konsistent `system-<IdSystem>` als Key.
- `useDynamicDeviceLayers` baut Layer-Gruppen und Marker-Zuordnung ebenfalls über `IdSystem` auf. - `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 ### 🐞 Fixed
- Fehler behoben: Kontextmenü „Station öffnen (Tab)“ wurde bei Geräten/Stationen mehrfach angezeigt - 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 - Datei `createAndSetDevices.js` entsprechend angepasst
### 🔧 Version ### 🔧 Version
@@ -66,13 +93,11 @@ Alle bedeutenden Änderungen an diesem Projekt werden in dieser Datei dokumentie
--- ---
[1.1.190] 2025-05-27 [1.1.190] 2025-05-27 🐞 Fixed Dropdown im UI Widget MapLayersControlPanel zeigte keine
🐞 Fixed Stations-/Bereichsnamen an 🔧 Fehler behoben: Zugriff auf GisStationsStaticDistrict.Points statt auf
Dropdown im UI Widget MapLayersControlPanel zeigte keine Stations-/Bereichsnamen an das Objekt selbst
🔧 Fehler behoben: Zugriff auf GisStationsStaticDistrict.Points statt auf das Objekt selbst
📄 Dokumentation 📄 Dokumentation Neue technische Markdown-Dokumentationen erstellt:
Neue technische Markdown-Dokumentationen erstellt:
/docs/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.md /docs/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.md
@@ -82,8 +107,7 @@ Neue technische Markdown-Dokumentationen erstellt:
/docs/components/uiWidgets/CoordinateInput.md /docs/components/uiWidgets/CoordinateInput.md
🔧 Version 🔧 Version 📦 Version erhöht auf 1.1.190
📦 Version erhöht auf 1.1.190
--- ---
@@ -97,7 +121,8 @@ Neue technische Markdown-Dokumentationen erstellt:
### 🧠 Architektur ### 🧠 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 - Links wie `Station öffnen (Tab)` oder WebService-URLs bauen sich nun dynamisch je nach basePath
### 📄 Dokumentation ### 📄 Dokumentation
@@ -118,7 +143,8 @@ Neue technische Markdown-Dokumentationen erstellt:
`/docs/pages/api/talas_v5_DB/priorityConfig.md` `/docs/pages/api/talas_v5_DB/priorityConfig.md`
- Beschreibt die API `/api/talas_v5_DB/priorityConfig` - Beschreibt die API `/api/talas_v5_DB/priorityConfig`
- Enthält Beispielantwort, Datenstruktur, SQL, Fehlerhandling - 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 ### 🧠 Architektur
@@ -162,8 +188,10 @@ Neue technische Markdown-Dokumentationen erstellt:
### 🔥 Removed ### 🔥 Removed
- Verzeichnis `/webServiceMockdata/` vollständig gelöscht - 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 - Alle früheren Mock-Daten wurden durch einen realistischen Setup mit MySQL (Docker) und TALAS.web
- Kein fetch() oder Dateizugriff auf diese Daten mehr im Projekt vorhanden (geprüft via VSCode „Find in Files“) (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) - Die Tests laufen nun gegen reale Backends (lokal & remote)
### 🧠 Architektur ### 🧠 Architektur
@@ -203,13 +231,12 @@ Neue technische Markdown-Dokumentationen erstellt:
--- ---
📦 [1.1.183] 2025-05-27 📦 [1.1.183] 2025-05-27 ♻️ Refactor Die Hilfsfunktion saveLineData() wurde vollständig entfernt:
♻️ Refactor
Die Hilfsfunktion saveLineData() wurde vollständig entfernt:
In markerUtils.js und poiUtils.js ersetzt durch updatePolylineCoordinatesThunk via Redux 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: 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 Map-Utilities und POI-Utilities sind nun Redux-kompatibel und testbar
✅ Clean ✅ Clean saveLineData aus mapUtils.js gelöscht
saveLineData aus mapUtils.js gelöscht
Alle Marker-Operationen speichern ihre Koordinaten ausschließlich über Redux Alle Marker-Operationen speichern ihre Koordinaten ausschließlich über Redux
🧠 Architektur 🧠 Architektur Redux-Toolkit Standardstruktur umgesetzt:
Redux-Toolkit Standardstruktur umgesetzt:
updatePolylineCoordinatesService.js updatePolylineCoordinatesService.js
@@ -233,10 +258,10 @@ updatePolylineCoordinatesThunk.js
Nutzung außerhalb React-Komponenten über store.dispatch(...) 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 📦 Version erhöht auf 1.1.183
📦 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 [1.1.181] 2025-05-26 ♻️ Refactor setupPolylines.js von direktem fetch() auf sauberes Redux-Muster
♻️ Refactor umgestellt:
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 Async-Handling erfolgt über .unwrap().then().catch() zur besseren Fehlerkontrolle
🧠 Architektur 🧠 Architektur Einheitliches Redux-Schema umgesetzt:
Einheitliches Redux-Schema umgesetzt:
Service: updatePolylineCoordinatesService.js Service: updatePolylineCoordinatesService.js
@@ -293,19 +318,15 @@ Thunk: updatePolylineCoordinatesThunk.js
Redux Toolkit-konforme store.dispatch(...)-Verwendung in Utility-Datei (außerhalb von React-Kontext) Redux Toolkit-konforme store.dispatch(...)-Verwendung in Utility-Datei (außerhalb von React-Kontext)
✅ Clean ✅ Clean Alle direkten fetch-Aufrufe aus setupPolylines.js entfernt
Alle direkten fetch-Aufrufe aus setupPolylines.js entfernt
Kein hartcodiertes URL-Handling mehr alles läuft über zentrale Redux-Logik Kein hartcodiertes URL-Handling mehr alles läuft über zentrale Redux-Logik
🔧 Version 🔧 Version 📦 Version erhöht auf 1.1.181
📦 Version erhöht auf 1.1.181
--- ---
[1.1.180] 2025-05-26 [1.1.180] 2025-05-26 ♻️ Refactor poiTypesSlice.js wurde bereinigt:
♻️ Refactor
poiTypesSlice.js wurde bereinigt:
Alte createAsyncThunk-Definition entfernt (direkter fetch im Slice) Alte createAsyncThunk-Definition entfernt (direkter fetch im Slice)
@@ -317,26 +338,24 @@ Service → Thunk → Slice
Keine Logik mehr im Slice selbst 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 🐞 Fixed Fehler fetchPoiTypes is not a function behoben durch Entfernen des alten Imports aus
Fehler fetchPoiTypes is not a function behoben durch Entfernen des alten Imports aus poiTypesSlice poiTypesSlice
Richtiger Import fetchPoiTypThunk nun überall verwendet Richtiger Import fetchPoiTypThunk nun überall verwendet
🧠 Architektur 🧠 Architektur Redux-Slice enthält nur noch Zustand & Status keine Abfragen mehr
Redux-Slice enthält nur noch Zustand & Status keine Abfragen mehr
fetchPoiTypService.js stellt fetch()-Logik bereit, Thunk übernimmt Fehlerbehandlung fetchPoiTypService.js stellt fetch()-Logik bereit, Thunk übernimmt Fehlerbehandlung
🔧 Version 🔧 Version 📦 Version erhöht auf 1.1.180
📦 Version erhöht auf 1.1.180
--- ---
[1.1.177] 2025-05-26 [1.1.177] 2025-05-26 ✨ UI-Verbesserung POI-Tooltip auf der Karte wird jetzt bei Mouseover
✨ UI-Verbesserung angezeigt (nicht mehr per Klick).
POI-Tooltip auf der Karte wird jetzt bei Mouseover angezeigt (nicht mehr per Klick).
Tooltip ist einheitlich gestaltet mit Tailwind CSS: 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). Einheitlicher Stil für alle Marker-Tooltips (setupPOIs.js angepasst).
✅ Clean ✅ Clean Alte bindPopup()-Logik entfernt kein manuelles Öffnen/Schließen mehr nötig
Alte bindPopup()-Logik entfernt kein manuelles Öffnen/Schließen mehr nötig
closePopup() aus mouseout-Handler entfernt closePopup() aus mouseout-Handler entfernt
Tooltip verwendet sticky: true, folgt der Maus Tooltip verwendet sticky: true, folgt der Maus
🔧 Version 🔧 Version 📦 Version erhöht auf 1.1.177
📦 Version erhöht auf 1.1.177
--- ---
[1.1.176] 2025-05-26 [1.1.176] 2025-05-26 🐞 Fixed Problem behoben, dass das Modal „POI hinzufügen“ nach erfolgreichem
🐞 Fixed Submit nicht geschlossen wurde und die Meldung „Wird hinzugefügt…“ dauerhaft sichtbar blieb.
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. Ursache: Die Service-Datei addPoiService.js verwendete die falsche URL /addLocation statt /addPoi.
Die unwrap()-Verwendung im AddPOIModal.js wurde beibehalten und korrekt abgeschlossen. Die unwrap()-Verwendung im AddPOIModal.js wurde beibehalten und korrekt abgeschlossen.
✅ Clean ✅ Clean resetAddPoiStatus() wird jetzt direkt nach onClose() dispatcht, um Status in Redux auf idle
resetAddPoiStatus() wird jetzt direkt nach onClose() dispatcht, um Status in Redux auf idle zurückzusetzen. zurückzusetzen.
Ladeindikator „Wird hinzugefügt...“ wird zuverlässig entfernt. Ladeindikator „Wird hinzugefügt...“ wird zuverlässig entfernt.
Fehleranzeige funktioniert weiterhin über Redux (status === "failed" und error). Fehleranzeige funktioniert weiterhin über Redux (status === "failed" und error).
🧠 Architektur 🧠 Architektur Vollständige Redux-Anbindung des Modals über addPoiSlice, addPoiThunk, addPoiService
Vollständige Redux-Anbindung des Modals über addPoiSlice, addPoiThunk, addPoiService und addPoi.js. 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 📦 Version erhöht auf 1.1.176
📦 Version erhöht auf 1.1.176
--- ---
[1.1.174] 2025-05-26 [1.1.174] 2025-05-26 ♻️ Refactor useDrawLines.js vollständig auf Redux umgestellt:
♻️ Refactor
useDrawLines.js vollständig auf Redux umgestellt:
Entfernt: direkter fetch("/api/talas_v5_DB/gisLines/readGisLines") 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 Datenverarbeitung der points erfolgt reaktiv über Redux-State
🧠 Architektur 🧠 Architektur useDrawLines.js verwendet jetzt:
useDrawLines.js verwendet jetzt:
fetchGisLinesService (Service) fetchGisLinesService (Service)
@@ -408,21 +421,17 @@ gisLinesSlice mit Selektor selectGisLines
State wird automatisch im Redux verwaltet (loading/succeeded/failed) State wird automatisch im Redux verwaltet (loading/succeeded/failed)
✅ Cleanup ✅ Cleanup Promise-Kette .then().catch() durch useSelector-basierten Effekt ersetzt
Promise-Kette .then().catch() durch useSelector-basierten Effekt ersetzt
Fehlerausgabe bei ungültigen points strukturiert behandelt Fehlerausgabe bei ungültigen points strukturiert behandelt
Hook ist nun Redux-konform und testbar Hook ist nun Redux-konform und testbar
🔧 Version 🔧 Version 📦 Version erhöht auf 1.1.174
📦 Version erhöht auf 1.1.174
--- ---
[1.1.173] 2025-05-26 [1.1.173] 2025-05-26 ♻️ Refactor useMapComponentState.js vollständig auf Redux umgestellt:
♻️ Refactor
useMapComponentState.js vollständig auf Redux umgestellt:
Entfernt: lokale fetch(...)-Aufrufe Entfernt: lokale fetch(...)-Aufrufe
@@ -436,22 +445,19 @@ priorityConfig → über priorityConfigSlice + fetchPriorityConfigThunk
poiLayerVisible → direkter Zugriff über Redux-Zustand poiLayerVisible → direkter Zugriff über Redux-Zustand
🧠 Architektur 🧠 Architektur useMapComponentState.js ist jetzt rein selektorbasiert
useMapComponentState.js ist jetzt rein selektorbasiert
Standardstruktur Service → Thunk → Slice vollständig eingehalten Standardstruktur Service → Thunk → Slice vollständig eingehalten
Komponente reagiert nur noch auf Redux-Status (z.B. poiTypStatus === "succeeded") Komponente reagiert nur noch auf Redux-Status (z.B. poiTypStatus === "succeeded")
✅ Cleanup ✅ Cleanup useState() für priorityConfig und locationDeviceData entfernt
useState() für priorityConfig und locationDeviceData entfernt
deviceName wird direkt aus Redux-Daten abgeleitet deviceName wird direkt aus Redux-Daten abgeleitet
Selektor für poiLayerVisible wird direkt inline verwendet Selektor für poiLayerVisible wird direkt inline verwendet
🔧 Version 🔧 Version 📦 Version erhöht auf 1.1.173
📦 Version erhöht auf 1.1.173
--- ---
@@ -460,7 +466,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### 🐞 Fixed ### 🐞 Fixed
- POI-Icons wurden immer als `poi-marker-icon-4.png` dargestellt, egal welcher Typ - 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 ### ✅ Clean
@@ -591,7 +598,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### 🐞 Fixed ### 🐞 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 - Kontextmenü „POI bearbeiten“ wird jetzt korrekt angezeigt
- Modal öffnet sich nur noch bei gültiger Berechtigung (56) - Modal öffnet sich nur noch bei gültiger Berechtigung (56)
@@ -630,7 +638,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### 🐞 Fixed ### 🐞 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 ### ✅ Clean
@@ -675,11 +684,13 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Removed ### Removed
- `config.js` vollständig gelöscht keine Abhängigkeiten mehr im Projekt - `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 ### 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 - Verwendet dynamisch `window.location` + `.env.local` statt config.js
- Verbesserte Übersichtlichkeit und zentrale Steuerung über `.env.local` - Verbesserte Übersichtlichkeit und zentrale Steuerung über `.env.local`
@@ -700,7 +711,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Entfernt ### 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 ### Geändert
@@ -718,7 +730,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Geändert ### 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 - `fetchUserRightsThunk` direkt in `MapComponent.js` integriert
### Architektur ### Architektur
@@ -754,13 +767,15 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Changed ### Changed
- 🔁 Geräte-/Stationsanzeige vollständig auf **Redux-Store** umgestellt: - 🔁 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` - Entfernt: direkter `fetch(...)`-Zugriff über `config.js`
- Kein Zugriff mehr auf `mapGisStationsStaticDistrictUrl` / `StatusDistrictUrl` - Kein Zugriff mehr auf `mapGisStationsStaticDistrictUrl` / `StatusDistrictUrl`
### Fixed ### 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 - ✅ Marker erscheinen wieder zuverlässig durch saubere Trennung von Datenquelle und Darstellung
### Architecture ### Architecture
@@ -815,7 +830,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Refactored ### Refactored
- Entfernt: manuelle Fetch-Funktion und lokalen `isDataLoaded`-State für `DataSheet` - 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 - 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 Thunk: `fetchGisLinesThunk.js` unter `/redux/thunks/database/`
- Neuer Slice: `gisLinesSlice.js` mit Statusverwaltung (idle/loading/succeeded/failed) - Neuer Slice: `gisLinesSlice.js` mit Statusverwaltung (idle/loading/succeeded/failed)
- Redux-Selektor `selectGisLines` in `MapComponent.js` genutzt - 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 - Alle Linien-Daten werden nun zentral über Redux geladen und in `linePositions` umgewandelt
### Version ### Version
@@ -852,8 +869,10 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Fixed ### Fixed
- ❌ Fehler `fetchPriorityConfigThunk is not defined` behoben durch Import und richtigen Store-Zugriff - ❌ Fehler `fetchPriorityConfigThunk is not defined` behoben durch Import und richtigen
- ❌ Bug: Redux Slice zeigte keine Daten Ursache war fehlendes `dispatch` der Thunk-Funktion → behoben Store-Zugriff
- ❌ Bug: Redux Slice zeigte keine Daten Ursache war fehlendes `dispatch` der Thunk-Funktion →
behoben
### Cleanup ### Cleanup
@@ -862,7 +881,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Architekturhinweis ### Architekturhinweis
- 🔄 Komponente wie `MapComponent` ist der Trigger: - 🔄 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 ### Version
@@ -875,10 +895,13 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Changed ### Changed
- 🧱 `fetchLocationDevices.js` wurde vollständig entfernt (lag zuvor unter `/redux/api/fromDB/`) - 🧱 `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 - Neuer Thunk `fetchLocationDevicesThunk.js` unter `/redux/thunks/database/` eingeführt
- `locationDevicesFromDBSlice.js` ersetzt durch `locationDevicesSlice.js` mit Anbindung an den neuen Thunk - `locationDevicesFromDBSlice.js` ersetzt durch `locationDevicesSlice.js` mit Anbindung an den neuen
- `MapComponent.js` nutzt jetzt `dispatch(fetchLocationDevicesThunk())` zur Initialisierung der Geräte Thunk
- `MapComponent.js` nutzt jetzt `dispatch(fetchLocationDevicesThunk())` zur Initialisierung der
Geräte
### Cleanup ### Cleanup
@@ -895,7 +918,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Cleanup ### 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` - `fetchGisStationsMeasurements.js`
- `fetchGisStationsStatic.js` - `fetchGisStationsStatic.js`
- `fetchGisStationsStaticDistrict.js` - `fetchGisStationsStaticDistrict.js`
@@ -937,7 +961,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
- `fetchGisStationsStaticDistrict.js` - `fetchGisStationsStaticDistrict.js`
- `fetchGisStationsStatusDistrict.js` - `fetchGisStationsStatusDistrict.js`
- `fetchGisSystemStatic.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 - Alte Fetch-Struktur (`/redux/api/fromWebService`) vollständig obsolet
### Fixed ### Fixed
@@ -960,7 +985,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Changed ### Changed
- 🔁 GIS-Datenquellen konsolidiert: Statt 5 werden jetzt nur 4 zentrale Redux-Slices verwendet: - 🔁 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) - ❌ Entfernt: `gisStationsStatic` (veraltet / wurde durch `StaticDistrict` ersetzt)
### Fixed ### Fixed
@@ -973,7 +999,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
- 🧼 Thunks, Services und Slices konsistent: - 🧼 Thunks, Services und Slices konsistent:
- Alle Slices verwenden jetzt zentrale Thunks - 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 - 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/database/` für eigene Next.js-APIs (Port 3000)
- `/services/utils/` für Hilfsfunktionen (z.B. `fetchWithTimeout`) - `/services/utils/` für Hilfsfunktionen (z.B. `fetchWithTimeout`)
- Alle Service-Dateien konsistent benannt nach Schema: `fetchXyzService.js` - Alle Service-Dateien konsistent benannt nach Schema: `fetchXyzService.js`
- Beispiel: `fetchGisStationsMeasurementsService.js`, `fetchPoiData.js`, `updateLocationInDatabase.js` - Beispiel: `fetchGisStationsMeasurementsService.js`, `fetchPoiData.js`,
`updateLocationInDatabase.js`
### Motivation ### Motivation
@@ -1070,7 +1098,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
- `AddPoiModalWindowWrapper.js` - `AddPoiModalWindowWrapper.js`
- `PoiUpdateModalWrapper.js` - `PoiUpdateModalWrapper.js`
- `PoiUpdateModalWindow.js` - `PoiUpdateModalWindow.js`
- Ersetzt durch moderne Komponenten mit direkter Redux-Anbindung (`ShowAddStationPopup`, `PoiUpdateModal`) - Ersetzt durch moderne Komponenten mit direkter Redux-Anbindung (`ShowAddStationPopup`,
`PoiUpdateModal`)
### Cleanup ### Cleanup
@@ -1124,7 +1153,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Refactor ### 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) - 🟡 Validierte React-Hooks beibehalten (z.B. Marker-Handling & Layer-Logik)
--- ---
@@ -1155,7 +1185,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Changed ### 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 - 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 - Projektstruktur aufgeräumt Installationsdateien blähen Git-Verlauf nicht mehr auf
- Hinweis in `README.md` zu Projektorganisation, Tool-Voraussetzungen und Linkstrategie ergänzt - 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: - `config.js` angepasst:
- Entfernt: `NEXT_PUBLIC_SERVER_URL` aus `.env.local` - Entfernt: `NEXT_PUBLIC_SERVER_URL` aus `.env.local`
- Dynamische Berechnung von `serverURL` anhand `window.location` + `NEXT_PUBLIC_API_PORT_MODE` - 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 ### Added
@@ -1535,7 +1567,8 @@ Selektor für poiLayerVisible wird direkt inline verwendet
### Changed ### 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`. - Entfernt: Fallback-Variablen `NEXT_PUBLIC_DEFAULT_ID_MAP` und `NEXT_PUBLIC_DEFAULT_ID_USER`.
- Dokumentation in `docs/redux/api/fromWebService.md` entsprechend angepasst. - Dokumentation in `docs/redux/api/fromWebService.md` entsprechend angepasst.

View File

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

View File

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

View File

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

View File

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