fix: POI Bearbeiten
This commit is contained in:
4
TODO.md
4
TODO.md
@@ -44,6 +44,8 @@
|
|||||||
Polling-Mechanismus → unbedingt prüfen, ob `clearInterval()` im useEffect-Cleanup enthalten
|
Polling-Mechanismus → unbedingt prüfen, ob `clearInterval()` im useEffect-Cleanup enthalten
|
||||||
ist.
|
ist.
|
||||||
|
|
||||||
-[ ] TODO: WebSocket für webService erstellen , falls etwas geändert wird dann soll aktualisiert,
|
-[x] TODO: WebSocket für webService erstellen , falls etwas geändert wird dann soll aktualisiert,
|
||||||
optimiert besser als setInterval, zuerst nur für TALAS.web WebServices erstellen, irgendwann soll
|
optimiert besser als setInterval, zuerst nur für TALAS.web WebServices erstellen, irgendwann soll
|
||||||
die Daten von DB auch mit WebSocket gelöst werden
|
die Daten von DB auch mit WebSocket gelöst werden
|
||||||
|
|
||||||
|
- [ ] TODO: POI bearbeiten funktioniert es nicht
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
// /config/appVersion
|
// /config/appVersion
|
||||||
export const APP_VERSION = "1.1.251";
|
export const APP_VERSION = "1.1.252";
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
# 🧠 Architekturübersicht – NodeMap
|
# 🧠 Architekturübersicht – NodeMap
|
||||||
|
|
||||||
Dieses Dokument beschreibt die technische Gesamtarchitektur des Projekts **NodeMap**, einer kartenbasierten Webanwendung zur Anzeige, Bearbeitung und Verwaltung von GIS-Daten, POIs und Gerätestatus.
|
Dieses Dokument beschreibt die technische Gesamtarchitektur des Projekts **NodeMap**, einer
|
||||||
|
kartenbasierten Webanwendung zur Anzeige, Bearbeitung und Verwaltung von GIS-Daten, POIs und
|
||||||
|
Gerätestatus.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -64,7 +66,8 @@ Dieses Dokument beschreibt die technische Gesamtarchitektur des Projekts **NodeM
|
|||||||
## 🧩 Besonderheiten
|
## 🧩 Besonderheiten
|
||||||
|
|
||||||
- **Konfigurierbarer basePath:**
|
- **Konfigurierbarer basePath:**
|
||||||
Pfad wie `/talas5` ist optional und kann per `NEXT_PUBLIC_BASE_PATH` in `.env.local` gesetzt werden.
|
Pfad wie `/talas5` ist optional und kann per `NEXT_PUBLIC_BASE_PATH` in `.env.local` gesetzt
|
||||||
|
werden.
|
||||||
- **Rechteabhängige UI:**
|
- **Rechteabhängige UI:**
|
||||||
Funktionen (z. B. POI bearbeiten) basieren auf Benutzerrechten (`IdRight`) vom Server.
|
Funktionen (z. B. POI bearbeiten) basieren auf Benutzerrechten (`IdRight`) vom Server.
|
||||||
- **Zentrale Komponentensteuerung:**
|
- **Zentrale Komponentensteuerung:**
|
||||||
@@ -78,7 +81,8 @@ Dieses Dokument beschreibt die technische Gesamtarchitektur des Projekts **NodeM
|
|||||||
|
|
||||||
- Version ist in `appVersion.js` definiert → wird über `NEXT_PUBLIC_APP_VERSION` eingeblendet
|
- Version ist in `appVersion.js` definiert → wird über `NEXT_PUBLIC_APP_VERSION` eingeblendet
|
||||||
- Build erfolgt via `npm run build`, Auslieferung über `.next/`
|
- Build erfolgt via `npm run build`, Auslieferung über `.next/`
|
||||||
- Nicht benötigte Dateien wie `__tests__`, `docs/`, `scripts/` etc. werden nicht in den Build aufgenommen
|
- Nicht benötigte Dateien wie `__tests__`, `docs/`, `scripts/` etc. werden nicht in den Build
|
||||||
|
aufgenommen
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -141,24 +145,17 @@ flowchart TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Jetzt (dynamisch & Redux-basiert):
|
Jetzt (dynamisch & Redux-basiert): MapComponent.js ruft folgenden Hook auf:
|
||||||
MapComponent.js ruft folgenden Hook auf:
|
|
||||||
|
|
||||||
js
|
js Copy Edit const { markerStates, layerRefs } = useDynamicDeviceLayers(map, GisSystemStatic,
|
||||||
Copy
|
mapLayersVisibility, priorityConfig, oms); useDynamicDeviceLayers.js verarbeitet die
|
||||||
Edit
|
GisSystemStatic-Liste:
|
||||||
const { markerStates, layerRefs } = useDynamicDeviceLayers(map, GisSystemStatic, mapLayersVisibility, priorityConfig, oms);
|
|
||||||
useDynamicDeviceLayers.js verarbeitet die GisSystemStatic-Liste:
|
|
||||||
|
|
||||||
Jedes System (z. B. "TALAS", "ECI", "Cisco") bekommt einen eigenen Marker-Layer.
|
Jedes System (z. B. "TALAS", "ECI", "Cisco") bekommt einen eigenen Marker-Layer.
|
||||||
|
|
||||||
Die Marker werden erstellt durch:
|
Die Marker werden erstellt durch:
|
||||||
|
|
||||||
js
|
js Copy Edit createAndSetDevices(...) // Systemweise Marker erzeugen createAndSetDevices.js:
|
||||||
Copy
|
|
||||||
Edit
|
|
||||||
createAndSetDevices(...) // Systemweise Marker erzeugen
|
|
||||||
createAndSetDevices.js:
|
|
||||||
|
|
||||||
Filtert alle Stations aus staticDistrictData, deren System === IdSystem.
|
Filtert alle Stations aus staticDistrictData, deren System === IdSystem.
|
||||||
|
|
||||||
@@ -170,11 +167,7 @@ Ruft setMarkersFunction(markers) auf → Übergibt die Marker zurück an den Hoo
|
|||||||
|
|
||||||
Der Hook speichert:
|
Der Hook speichert:
|
||||||
|
|
||||||
js
|
js Copy Edit setMarkerStates((prev) => ({ ...prev, [Name]: newMarkers })); MapComponent.js hat dann:
|
||||||
Copy
|
|
||||||
Edit
|
|
||||||
setMarkerStates((prev) => ({ ...prev, [Name]: newMarkers }));
|
|
||||||
MapComponent.js hat dann:
|
|
||||||
|
|
||||||
Zugriff auf alle Marker dynamisch über markerStates (ein Objekt mit Schlüssel = Systemname)
|
Zugriff auf alle Marker dynamisch über markerStates (ein Objekt mit Schlüssel = Systemname)
|
||||||
|
|
||||||
@@ -182,9 +175,11 @@ Sichtbarkeit und OverlappingMarkerSpiderfier werden damit verarbeitet.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
🔁 Die Geräte-Marker sind nicht mehr fest codiert, sondern werden dynamisch erzeugt anhand der Webservice-Daten GisSystemStatic.
|
🔁 Die Geräte-Marker sind nicht mehr fest codiert, sondern werden dynamisch erzeugt anhand der
|
||||||
|
Webservice-Daten GisSystemStatic.
|
||||||
|
|
||||||
🔄 Sichtbarkeit (Checkbox im Control Panel) löst ein Event visibilityChanged aus → MapComponent reagiert und rendert Marker neu.
|
🔄 Sichtbarkeit (Checkbox im Control Panel) löst ein Event visibilityChanged aus → MapComponent
|
||||||
|
reagiert und rendert Marker neu.
|
||||||
|
|
||||||
🕷️ Überlappende Marker werden mit checkOverlappingMarkers + PlusRoundIcon verarbeitet.
|
🕷️ Überlappende Marker werden mit checkOverlappingMarkers + PlusRoundIcon verarbeitet.
|
||||||
|
|
||||||
@@ -202,3 +197,90 @@ flowchart TD
|
|||||||
A2 --> I1[Map aktualisiert Marker]
|
A2 --> I1[Map aktualisiert Marker]
|
||||||
A2 --> I2[checkOverlappingMarkers mit OMS]
|
A2 --> I2[checkOverlappingMarkers mit OMS]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
10.06.2025
|
||||||
|
|
||||||
|
# Datenfluss-Konzept: WebSocket ↔ Redux ↔ UI
|
||||||
|
|
||||||
|
Dieses Dokument beschreibt den technischen Ablauf des Live-Datenflusses im NodeMap-Projekt, um neue
|
||||||
|
Entwickler\:innen beim Onboarding zu unterstützen.
|
||||||
|
|
||||||
|
## ᵀᵃᵗᵉᵖᵏᴼᵏᴼᵉ: Architekturübersicht
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
participant WS_Server as WebSocket Server
|
||||||
|
participant WebService as TALAS WebService
|
||||||
|
participant Browser
|
||||||
|
participant ReduxStore as Redux Store
|
||||||
|
participant UI as React Leaflet UI
|
||||||
|
|
||||||
|
loop Alle 5 Sekunden
|
||||||
|
WS_Server->>WebService: fetch endpointX
|
||||||
|
alt Daten haben sich geändert
|
||||||
|
WS_Server-->>Browser: emit 'endpointXUpdated'
|
||||||
|
Browser->>ReduxStore: dispatch(fetchEndpointXThunk())
|
||||||
|
ReduxStore->>WebService: fetch endpointX
|
||||||
|
WebService-->>ReduxStore: JSON response
|
||||||
|
ReduxStore-->>UI: update Slice
|
||||||
|
UI-->>User: re-render Markers
|
||||||
|
else Keine Änderung
|
||||||
|
WS_Server-->>WS_Server: keine Aktion
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Beteiligte Komponenten
|
||||||
|
|
||||||
|
### WebSocket Server (`server.js`)
|
||||||
|
|
||||||
|
- Ruft regelmäßig (`setInterval`) die Webservice-Endpunkte auf.
|
||||||
|
- Erkennt Änderungen durch JSON-Vergleich (`JSON.stringify`).
|
||||||
|
- Sendet WebSocket-Events nur bei echten Änderungen.
|
||||||
|
|
||||||
|
### Client: MapComponent
|
||||||
|
|
||||||
|
- Hört auf `socket.on("endpointXUpdated")`.
|
||||||
|
- Ruft dann gezielt den passenden Redux-Thunk auf (z. B. `fetchGisLinesStatusThunk`).
|
||||||
|
|
||||||
|
### Redux Store & Thunks
|
||||||
|
|
||||||
|
- Jeder Endpunkt besitzt:
|
||||||
|
|
||||||
|
- einen `Service` (API-Fetch)
|
||||||
|
- einen `Thunk` (Redux-Logik)
|
||||||
|
- einen `Slice` (State-Verwaltung)
|
||||||
|
|
||||||
|
### React UI (Leaflet Map)
|
||||||
|
|
||||||
|
- Beobachtet relevante Redux-Slices via `useSelector()`.
|
||||||
|
- Aktualisiert Marker, Tooltip und Popup über `createAndSetDevices()` und
|
||||||
|
`useDynamicDeviceLayers()`.
|
||||||
|
|
||||||
|
## Beispiel-Endpunkte
|
||||||
|
|
||||||
|
| Endpunktname | WebSocket Event | Redux Thunk |
|
||||||
|
| --------------------------- | ---------------------------------- | --------------------------------------- |
|
||||||
|
| `GisLinesStatus` | `GisLinesStatusUpdated` | `fetchGisLinesStatusThunk()` |
|
||||||
|
| `GisStationsMeasurements` | `GisStationsMeasurementsUpdated` | `fetchGisStationsMeasurementsThunk()` |
|
||||||
|
| `GisStationsStaticDistrict` | `GisStationsStaticDistrictUpdated` | `fetchGisStationsStaticDistrictThunk()` |
|
||||||
|
| `GisStationsStatusDistrict` | `GisStationsStatusDistrictUpdated` | `fetchGisStationsStatusDistrictThunk()` |
|
||||||
|
|
||||||
|
## Vorteile
|
||||||
|
|
||||||
|
- UI aktualisiert sich nur bei echten Datenänderungen → weniger Re-Renders.
|
||||||
|
- Live-Synchronisation zwischen Datenquelle und Anzeige.
|
||||||
|
- Skalierbar für beliebige Endpunkte.
|
||||||
|
|
||||||
|
## ToDo/Erweiterungen
|
||||||
|
|
||||||
|
- Automatische Reconnect-Logik für WebSocket.
|
||||||
|
- Anzeige des letzten Update-Zeitpunkts in UI.
|
||||||
|
- Logging-UI für WebSocket-Messages zur Diagnose.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> Letzte Änderung: `{{heutiges Datum}}` von Ismail Ali
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// /utils/markerUtils.js
|
// /utils/markerUtils.js
|
||||||
import L from "leaflet";
|
import L from "leaflet";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import circleIcon from "../components/CircleIcon";
|
import circleIcon from "@/components/gisPolylines/icons/CircleIcon";
|
||||||
import { store } from "../redux/store";
|
import { store } from "../redux/store";
|
||||||
import { updatePolylineCoordinatesThunk } from "../redux/thunks/database/polylines/updatePolylineCoordinatesThunk";
|
import { updatePolylineCoordinatesThunk } from "@/redux/thunks/database/polylines/updatePolylineCoordinatesThunk";
|
||||||
import { redrawPolyline } from "./mapUtils";
|
import { redrawPolyline } from "@/utils/mapUtils";
|
||||||
import { cleanupMarkers } from "@/utils/common/cleanupMarkers";
|
import { cleanupMarkers } from "@/utils/common/cleanupMarkers";
|
||||||
|
|
||||||
const savePolylineRedux = lineData => {
|
const savePolylineRedux = lineData => {
|
||||||
@@ -57,37 +57,3 @@ export const removeMarker = (marker, lineData, currentZoom, currentCenter) => {
|
|||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handleEditPoi = (
|
|
||||||
marker,
|
|
||||||
userRights,
|
|
||||||
setCurrentPoiData,
|
|
||||||
setShowPoiUpdateModal,
|
|
||||||
fetchPoiData,
|
|
||||||
toast
|
|
||||||
) => {
|
|
||||||
if (!Array.isArray(userRights)) {
|
|
||||||
toast.error("Benutzerrechte sind ungültig.", {
|
|
||||||
position: "top-center",
|
|
||||||
autoClose: 5000,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!userRights.some(r => r.IdRight === 56)) {
|
|
||||||
toast.error("Benutzer hat keine Berechtigung zum Bearbeiten.", {
|
|
||||||
position: "top-center",
|
|
||||||
autoClose: 5000,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCurrentPoiData({
|
|
||||||
idPoi: marker.options.id,
|
|
||||||
name: marker.options.name,
|
|
||||||
description: marker.options.description,
|
|
||||||
});
|
|
||||||
|
|
||||||
fetchPoiData(marker.options.id);
|
|
||||||
setShowPoiUpdateModal(true);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// /utils/poiUtils.js
|
// /utils/poiUtils.js
|
||||||
import L from "leaflet";
|
import L from "leaflet";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import circleIcon from "../components/gisPolylines/icons/CircleIcon.js";
|
import circleIcon from "@/components/gisPolylines/icons/CircleIcon.js";
|
||||||
import { redrawPolyline } from "./polylines/redrawPolyline.js";
|
import { redrawPolyline } from "@/utils/polylines/redrawPolyline.js";
|
||||||
|
|
||||||
import { store } from "../redux/store";
|
import { store } from "../redux/store";
|
||||||
import { updatePolylineCoordinatesThunk } from "../redux/thunks/database/polylines/updatePolylineCoordinatesThunk";
|
import { updatePolylineCoordinatesThunk } from "../redux/thunks/database/polylines/updatePolylineCoordinatesThunk";
|
||||||
@@ -67,3 +67,37 @@ export const updateMarkerPosition = (newCoords, lineData, marker) => {
|
|||||||
if (!marker) return;
|
if (!marker) return;
|
||||||
marker.setLatLng([newCoords.lat, newCoords.lng]);
|
marker.setLatLng([newCoords.lat, newCoords.lng]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const handleEditPoi = (
|
||||||
|
marker,
|
||||||
|
userRights,
|
||||||
|
setCurrentPoiData,
|
||||||
|
setShowPoiUpdateModal,
|
||||||
|
fetchPoiData,
|
||||||
|
toast
|
||||||
|
) => {
|
||||||
|
if (!Array.isArray(userRights)) {
|
||||||
|
toast.error("Benutzerrechte sind ungültig.", {
|
||||||
|
position: "top-center",
|
||||||
|
autoClose: 5000,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userRights.some(r => r.IdRight === 56)) {
|
||||||
|
toast.error("Benutzer hat keine Berechtigung zum Bearbeiten.", {
|
||||||
|
position: "top-center",
|
||||||
|
autoClose: 5000,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentPoiData({
|
||||||
|
idPoi: marker.options.id,
|
||||||
|
name: marker.options.name,
|
||||||
|
description: marker.options.description,
|
||||||
|
});
|
||||||
|
|
||||||
|
fetchPoiData(marker.options.id);
|
||||||
|
setShowPoiUpdateModal(true);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// utils/setupPOIs.js
|
// utils/setupPOIs.js
|
||||||
import { parsePoint } from "./geometryUtils";
|
import { parsePoint } from "@/utils/geometryUtils";
|
||||||
import { handleEditPoi } from "./poiUtils";
|
import { handleEditPoi } from "@/utils/poiUtils";
|
||||||
import { updateLocationInDatabaseService } from "../services/database/updateLocationInDatabaseService";
|
import { updateLocationInDatabaseService } from "@/services/database/updateLocationInDatabaseService";
|
||||||
import { setSelectedPoi, clearSelectedPoi } from "../redux/slices/database/pois/selectedPoiSlice";
|
import { setSelectedPoi, clearSelectedPoi } from "@/redux/slices/database/pois/selectedPoiSlice";
|
||||||
import { cleanupMarkers } from "@/utils/common/cleanupMarkers";
|
import { cleanupMarkers } from "@/utils/common/cleanupMarkers";
|
||||||
|
|
||||||
export const setupPOIs = async (
|
export const setupPOIs = async (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
"Modul": 8,
|
"Modul": 8,
|
||||||
"DpName": "KUE08_Messwertalarm",
|
"DpName": "KUE08_Messwertalarm",
|
||||||
"ModulName": "Test9",
|
"ModulName": "Test11",
|
||||||
"ModulTyp": "Kü705-FO",
|
"ModulTyp": "Kü705-FO",
|
||||||
"Message": "KÜG 08: Überspannung gehend",
|
"Message": "KÜG 08: Überspannung gehend",
|
||||||
"Level": 3,
|
"Level": 3,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"IdL": 24101,
|
"IdL": 24101,
|
||||||
"IdDP": 3,
|
"IdDP": 3,
|
||||||
"Na": "FBT",
|
"Na": "FBT",
|
||||||
"Val": "13",
|
"Val": "15",
|
||||||
"Unit": "°C",
|
"Unit": "°C",
|
||||||
"Gr": "GMA",
|
"Gr": "GMA",
|
||||||
"Area_Name": "Rastede"
|
"Area_Name": "Rastede"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"LD_Name": "CPL Ismail31",
|
"LD_Name": "CPL Ismail",
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
"Device": "CPL V3.5 mit 24 Kü",
|
"Device": "CPL V3.5 mit 24 Kü",
|
||||||
"Link": "cpl.aspx?ver=35&kue=24&id=50922",
|
"Link": "cpl.aspx?ver=35&kue=24&id=50922",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"Na": "system",
|
"Na": "system",
|
||||||
"Le": 4,
|
"Le": 4,
|
||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 01 kommend",
|
"Me": "Eingang DE 01 kommend test2",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user