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
|
||||
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
|
||||
die Daten von DB auch mit WebSocket gelöst werden
|
||||
|
||||
- [ ] TODO: POI bearbeiten funktioniert es nicht
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// /config/appVersion
|
||||
export const APP_VERSION = "1.1.251";
|
||||
export const APP_VERSION = "1.1.252";
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
# 🧠 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
|
||||
|
||||
- **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:**
|
||||
Funktionen (z. B. POI bearbeiten) basieren auf Benutzerrechten (`IdRight`) vom Server.
|
||||
- **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
|
||||
- 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):
|
||||
MapComponent.js ruft folgenden Hook auf:
|
||||
Jetzt (dynamisch & Redux-basiert): MapComponent.js ruft folgenden Hook auf:
|
||||
|
||||
js
|
||||
Copy
|
||||
Edit
|
||||
const { markerStates, layerRefs } = useDynamicDeviceLayers(map, GisSystemStatic, mapLayersVisibility, priorityConfig, oms);
|
||||
useDynamicDeviceLayers.js verarbeitet die GisSystemStatic-Liste:
|
||||
js Copy Edit 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.
|
||||
|
||||
Die Marker werden erstellt durch:
|
||||
|
||||
js
|
||||
Copy
|
||||
Edit
|
||||
createAndSetDevices(...) // Systemweise Marker erzeugen
|
||||
createAndSetDevices.js:
|
||||
js Copy Edit createAndSetDevices(...) // Systemweise Marker erzeugen createAndSetDevices.js:
|
||||
|
||||
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:
|
||||
|
||||
js
|
||||
Copy
|
||||
Edit
|
||||
setMarkerStates((prev) => ({ ...prev, [Name]: newMarkers }));
|
||||
MapComponent.js hat dann:
|
||||
js Copy Edit setMarkerStates((prev) => ({ ...prev, [Name]: newMarkers })); MapComponent.js hat dann:
|
||||
|
||||
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.
|
||||
|
||||
@@ -202,3 +197,90 @@ flowchart TD
|
||||
A2 --> I1[Map aktualisiert Marker]
|
||||
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
|
||||
import L from "leaflet";
|
||||
import { toast } from "react-toastify";
|
||||
import circleIcon from "../components/CircleIcon";
|
||||
import circleIcon from "@/components/gisPolylines/icons/CircleIcon";
|
||||
import { store } from "../redux/store";
|
||||
import { updatePolylineCoordinatesThunk } from "../redux/thunks/database/polylines/updatePolylineCoordinatesThunk";
|
||||
import { redrawPolyline } from "./mapUtils";
|
||||
import { updatePolylineCoordinatesThunk } from "@/redux/thunks/database/polylines/updatePolylineCoordinatesThunk";
|
||||
import { redrawPolyline } from "@/utils/mapUtils";
|
||||
import { cleanupMarkers } from "@/utils/common/cleanupMarkers";
|
||||
|
||||
const savePolylineRedux = lineData => {
|
||||
@@ -57,37 +57,3 @@ export const removeMarker = (marker, lineData, currentZoom, currentCenter) => {
|
||||
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
|
||||
import L from "leaflet";
|
||||
import { toast } from "react-toastify";
|
||||
import circleIcon from "../components/gisPolylines/icons/CircleIcon.js";
|
||||
import { redrawPolyline } from "./polylines/redrawPolyline.js";
|
||||
import circleIcon from "@/components/gisPolylines/icons/CircleIcon.js";
|
||||
import { redrawPolyline } from "@/utils/polylines/redrawPolyline.js";
|
||||
|
||||
import { store } from "../redux/store";
|
||||
import { updatePolylineCoordinatesThunk } from "../redux/thunks/database/polylines/updatePolylineCoordinatesThunk";
|
||||
@@ -67,3 +67,37 @@ export const updateMarkerPosition = (newCoords, lineData, marker) => {
|
||||
if (!marker) return;
|
||||
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
|
||||
import { parsePoint } from "./geometryUtils";
|
||||
import { handleEditPoi } from "./poiUtils";
|
||||
import { updateLocationInDatabaseService } from "../services/database/updateLocationInDatabaseService";
|
||||
import { setSelectedPoi, clearSelectedPoi } from "../redux/slices/database/pois/selectedPoiSlice";
|
||||
import { parsePoint } from "@/utils/geometryUtils";
|
||||
import { handleEditPoi } from "@/utils/poiUtils";
|
||||
import { updateLocationInDatabaseService } from "@/services/database/updateLocationInDatabaseService";
|
||||
import { setSelectedPoi, clearSelectedPoi } from "@/redux/slices/database/pois/selectedPoiSlice";
|
||||
import { cleanupMarkers } from "@/utils/common/cleanupMarkers";
|
||||
|
||||
export const setupPOIs = async (
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"IdLD": 50922,
|
||||
"Modul": 8,
|
||||
"DpName": "KUE08_Messwertalarm",
|
||||
"ModulName": "Test9",
|
||||
"ModulName": "Test11",
|
||||
"ModulTyp": "Kü705-FO",
|
||||
"Message": "KÜG 08: Überspannung gehend",
|
||||
"Level": 3,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"IdL": 24101,
|
||||
"IdDP": 3,
|
||||
"Na": "FBT",
|
||||
"Val": "13",
|
||||
"Val": "15",
|
||||
"Unit": "°C",
|
||||
"Gr": "GMA",
|
||||
"Area_Name": "Rastede"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"LD_Name": "CPL Ismail31",
|
||||
"LD_Name": "CPL Ismail",
|
||||
"IdLD": 50922,
|
||||
"Device": "CPL V3.5 mit 24 Kü",
|
||||
"Link": "cpl.aspx?ver=35&kue=24&id=50922",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"Na": "system",
|
||||
"Le": 4,
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Eingang DE 01 kommend",
|
||||
"Me": "Eingang DE 01 kommend test2",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user