Compare commits
61 Commits
feat/docs
...
3a9b436352
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a9b436352 | ||
|
|
7b881e80c2 | ||
|
|
cc19a0a466 | ||
|
|
f200d0bb20 | ||
|
|
75a0ab000f | ||
|
|
4d2a94ffea | ||
|
|
239ad82e46 | ||
|
|
a2d3338624 | ||
|
|
598acb8441 | ||
|
|
f8512c485e | ||
|
|
fff2754b14 | ||
|
|
61ed542ea4 | ||
|
|
4befddd440 | ||
|
|
5b7868145c | ||
|
|
0a3c4c208f | ||
|
|
8cf520bb2c | ||
|
|
3896381a8f | ||
|
|
a013c07394 | ||
|
|
f2a322a91b | ||
|
|
bf7b62d110 | ||
|
|
c44a755077 | ||
|
|
aea439f135 | ||
|
|
c6871692aa | ||
|
|
e7192a7623 | ||
|
|
f11f64d4d7 | ||
|
|
da21cba186 | ||
|
|
d179c152c0 | ||
|
|
2da79c9318 | ||
|
|
2066cbb9e8 | ||
|
|
c8a14ee873 | ||
|
|
44b29469b9 | ||
|
|
ee5319a928 | ||
|
|
d68b17c382 | ||
|
|
22692f8153 | ||
|
|
819fde9605 | ||
|
|
ded0a9d5da | ||
|
|
4161bfa948 | ||
|
|
680c643ea5 | ||
|
|
7c525c11a1 | ||
|
|
9f05a4f63b | ||
|
|
26d869f029 | ||
|
|
0ea8e090d2 | ||
|
|
9a2b438eaf | ||
|
|
bf4fc95b8e | ||
|
|
db147543d9 | ||
|
|
418651a2af | ||
|
|
bf5ee377a4 | ||
|
|
5c06abc85e | ||
|
|
7c89d8dae5 | ||
|
|
85266aa1ed | ||
|
|
854eff668a | ||
|
|
8073c787bc | ||
|
|
4ee6d42a61 | ||
|
|
53c670feba | ||
|
|
d8567a9928 | ||
|
|
6d33be56c0 | ||
|
|
4755cefdd7 | ||
|
|
37aec649ee | ||
|
|
9d7a696f91 | ||
|
|
56a2e305f1 | ||
|
|
e1bfe7496b |
@@ -7,7 +7,6 @@ DB_NAME=talas_v5
|
|||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
|
|
||||||
# Public Settings (Client braucht IP/Domain) , Variablen mit dem Präfix "NEXT_PUBLIC" ist in Browser sichtbar
|
# Public Settings (Client braucht IP/Domain) , Variablen mit dem Präfix "NEXT_PUBLIC" ist in Browser sichtbar
|
||||||
NEXT_PUBLIC_DEBUG_LOG=true
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -20,9 +19,8 @@ NEXT_PUBLIC_USE_MOCKS=true
|
|||||||
# Ein Unterordner in der dort hinter liegenden Ordnerstruktur (z.B. http://talasserver/talas5/nodemap/api/talas_v5_DB/ usw.)
|
# Ein Unterordner in der dort hinter liegenden Ordnerstruktur (z.B. http://talasserver/talas5/nodemap/api/talas_v5_DB/ usw.)
|
||||||
# kann bleiben da der Kunde diesen Unterordner talas:v5_db nicht ändert.
|
# kann bleiben da der Kunde diesen Unterordner talas:v5_db nicht ändert.
|
||||||
#Füge in deiner .env.local Datei die folgende Zeile hinzu wenn du einen Unterordner verwenden möchtest mit entsprechende Bezeichnung.
|
#Füge in deiner .env.local Datei die folgende Zeile hinzu wenn du einen Unterordner verwenden möchtest mit entsprechende Bezeichnung.
|
||||||
# z.B. http://10.10.0.13/talas5/index.aspx -> NEXT_PUBLIC_BASE_PATH=/talas5
|
# z.B. http://10.10.0.13/talas5/index.aspx -> basePath in config.json auf /talas5 setzen
|
||||||
# z.B. http://10.10.0.13/xyz/index.aspx -> NEXT_PUBLIC_BASE_PATH=/xyz
|
# z.B. http://10.10.0.13/xyz/index.aspx -> basePath in config.json auf /xyz setzen
|
||||||
NEXT_PUBLIC_BASE_PATH=/talas5
|
# basePath wird jetzt in public/config.json gepflegt
|
||||||
# Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH=
|
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.1.300
|
NEXT_PUBLIC_APP_VERSION=1.1.361
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ DB_NAME=talas_v5
|
|||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
|
|
||||||
# Public Settings (Client braucht IP/Domain) , Variablen mit dem Präfix "NEXT_PUBLIC" ist in Browser sichtbar
|
# Public Settings (Client braucht IP/Domain) , Variablen mit dem Präfix "NEXT_PUBLIC" ist in Browser sichtbar
|
||||||
NEXT_PUBLIC_DEBUG_LOG=false
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -20,10 +19,9 @@ NEXT_PUBLIC_USE_MOCKS=false
|
|||||||
# Ein Unterordner in der dort hinter liegenden Ordnerstruktur (z.B. http://talasserver/talas5/nodemap/api/talas_v5_DB/ usw.)
|
# Ein Unterordner in der dort hinter liegenden Ordnerstruktur (z.B. http://talasserver/talas5/nodemap/api/talas_v5_DB/ usw.)
|
||||||
# kann bleiben da der Kunde diesen Unterordner talas:v5_db nicht ändert.
|
# kann bleiben da der Kunde diesen Unterordner talas:v5_db nicht ändert.
|
||||||
#Füge in deiner .env.local Datei die folgende Zeile hinzu wenn du einen Unterordner verwenden möchtest mit entsprechende Bezeichnung.
|
#Füge in deiner .env.local Datei die folgende Zeile hinzu wenn du einen Unterordner verwenden möchtest mit entsprechende Bezeichnung.
|
||||||
# z.B. http://10.10.0.13/talas5/index.aspx -> NEXT_PUBLIC_BASE_PATH=/talas5
|
# z.B. http://10.10.0.13/talas5/index.aspx -> basePath in config.json auf /talas5 setzen
|
||||||
# z.B. http://10.10.0.13/xyz/index.aspx -> NEXT_PUBLIC_BASE_PATH=/xyz
|
# z.B. http://10.10.0.13/xyz/index.aspx -> basePath in config.json auf /xyz setzen
|
||||||
NEXT_PUBLIC_BASE_PATH=/talas5
|
# basePath wird jetzt in public/config.json gepflegt
|
||||||
# Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH=
|
|
||||||
|
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.1.300
|
NEXT_PUBLIC_APP_VERSION=1.1.361
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
echo "🔄 Version wird automatisch erhöht (bumpVersion.js)..."
|
echo "🔄 Version wird automatisch erhöht (bumpVersion.js)..."
|
||||||
|
|
||||||
# Version automatisch erhöhen
|
# Version automatisch erhöhen
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ das Objekt selbst
|
|||||||
### ♻️ Refactor
|
### ♻️ Refactor
|
||||||
|
|
||||||
- Alle hartkodierten `/talas5/`-Pfadangaben entfernt
|
- Alle hartkodierten `/talas5/`-Pfadangaben entfernt
|
||||||
- Dynamischer `basePath` eingeführt über `.env.local → NEXT_PUBLIC_BASE_PATH`
|
- Dynamischer `basePath` eingeführt über `public/config.json → basePath`
|
||||||
- Unterstützt jetzt auch den Betrieb ohne Unterverzeichnis
|
- Unterstützt jetzt auch den Betrieb ohne Unterverzeichnis
|
||||||
|
|
||||||
### 🧠 Architektur
|
### 🧠 Architektur
|
||||||
|
|||||||
28
README.md
@@ -48,6 +48,30 @@ User-ID.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## ⚙️ Kartenquellen-Konfiguration (public/config.json)
|
||||||
|
|
||||||
|
Die Datei `public/config.json` steuert, welche Kartenquelle (z.B. OSM oder lokale Tiles) für die
|
||||||
|
Leaflet-Karte verwendet wird.
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"//info": "tileSources: 'local' für offline, 'osm' für online",
|
||||||
|
"tileSources": {
|
||||||
|
"local": "http://localhost/talas5/TileMap/mapTiles/{z}/{x}/{y}.png",
|
||||||
|
"osm": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
},
|
||||||
|
"active": "osm"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Mit `active` kann zwischen Online- und Offline-Karten umgeschaltet werden.
|
||||||
|
- Die Datei wird beim Start der App automatisch geladen.
|
||||||
|
- Für Offline-Betrieb muss das lokale Kartenmaterial vorhanden sein (siehe Installationsanleitung).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🧰 Erstinstallation auf Server
|
## 🧰 Erstinstallation auf Server
|
||||||
|
|
||||||
### Voraussetzungen
|
### Voraussetzungen
|
||||||
@@ -61,8 +85,8 @@ User-ID.
|
|||||||
(Server-IP mit Port 3000)
|
(Server-IP mit Port 3000)
|
||||||
- Browser: Chrome ab Version 125.0.6420.142 empfohlen
|
- Browser: Chrome ab Version 125.0.6420.142 empfohlen
|
||||||
- Karten Material vorhanden in: `C:\inetpub\wwwroot\talas5\TileMap\mapTiles`
|
- Karten Material vorhanden in: `C:\inetpub\wwwroot\talas5\TileMap\mapTiles`
|
||||||

|
 Falls nicht vorhanden hier downloaden:
|
||||||
Falls nicht vorhanden hier downloaden: http://10.10.0.28/produkte/TALAS.map/mapTiles.zip
|
http://10.10.0.28/produkte/TALAS.map/mapTiles.zip
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
6
Start-Dev.ps1
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Navigiere zum Verzeichnis deines Projekts
|
||||||
|
cd 'C:\inetpub\wwwroot\talas5\nodeMap'
|
||||||
|
|
||||||
|
# F<>hre den npm Befehl aus
|
||||||
|
npm start
|
||||||
|
|
||||||
1
StartNodeApp.bat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PowerShell -ExecutionPolicy Bypass -File "C:\inetpub\wwwroot\talas5\nodeMap\Start-Dev.ps1"
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
describe("setupPOIs Icon-Mapping intern", () => {
|
|
||||||
it("ordnet korrektes Icon anhand idPoi zu", () => {
|
|
||||||
const mockPoiData = [{ idPoi: 7, path: "poi-marker-icon-2.png" }];
|
|
||||||
const iconMap = new Map();
|
|
||||||
mockPoiData.forEach((item) => iconMap.set(item.idPoi, item.path));
|
|
||||||
const result = iconMap.get(7);
|
|
||||||
expect(result).toBe("poi-marker-icon-2.png");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("gibt undefined zurück wenn idPoi nicht existiert", () => {
|
|
||||||
const iconMap = new Map();
|
|
||||||
iconMap.set(1, "icon-1.png");
|
|
||||||
const result = iconMap.get(99);
|
|
||||||
expect(result).toBeUndefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { getDebugLog } from "@/utils/configUtils.js";
|
||||||
// /hooks/layers/useAreaMarkersLayer.js
|
// /hooks/layers/useAreaMarkersLayer.js
|
||||||
import { useEffect, useState, useRef } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
import L from "leaflet";
|
import L from "leaflet";
|
||||||
@@ -22,7 +23,13 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => {
|
|||||||
const updateMarkersVisibility = () => {
|
const updateMarkersVisibility = () => {
|
||||||
if (!map || areaMarkers.length === 0) return;
|
if (!map || areaMarkers.length === 0) return;
|
||||||
|
|
||||||
const mapLayersVisibility = JSON.parse(localStorage.getItem("mapLayersVisibility")) || {};
|
// Kartenspezifischer localStorage-Key verwenden
|
||||||
|
const mapId = localStorage.getItem("currentMapId");
|
||||||
|
const userId = localStorage.getItem("currentUserId");
|
||||||
|
const mapStorageKey =
|
||||||
|
mapId && userId ? `mapLayersVisibility_m${mapId}_u${userId}` : "mapLayersVisibility";
|
||||||
|
|
||||||
|
const mapLayersVisibility = JSON.parse(localStorage.getItem(mapStorageKey)) || {};
|
||||||
const areAllLayersInvisible = Object.values(mapLayersVisibility).every(v => !v);
|
const areAllLayersInvisible = Object.values(mapLayersVisibility).every(v => !v);
|
||||||
|
|
||||||
if (areAllLayersInvisible === prevVisibility.current) return;
|
if (areAllLayersInvisible === prevVisibility.current) return;
|
||||||
@@ -42,7 +49,8 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => {
|
|||||||
updateMarkersVisibility();
|
updateMarkersVisibility();
|
||||||
|
|
||||||
const handleStorageChange = event => {
|
const handleStorageChange = event => {
|
||||||
if (event.key === "mapLayersVisibility") {
|
// Überwache sowohl den alten als auch kartenspezifische Keys
|
||||||
|
if (event.key === "mapLayersVisibility" || event.key?.startsWith("mapLayersVisibility_")) {
|
||||||
updateMarkersVisibility();
|
updateMarkersVisibility();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -64,10 +72,11 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => {
|
|||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
|
const editMode = localStorage.getItem("editMode") === "true";
|
||||||
const markers = data.map(item => {
|
const markers = data.map(item => {
|
||||||
const marker = L.marker([item.x, item.y], {
|
const marker = L.marker([item.x, item.y], {
|
||||||
icon: customIcon,
|
icon: customIcon,
|
||||||
draggable: true,
|
draggable: editMode,
|
||||||
customType: "areaMarker",
|
customType: "areaMarker",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -81,24 +90,26 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
marker.on("dragend", async e => {
|
if (editMode) {
|
||||||
const { lat, lng } = e.target.getLatLng();
|
marker.on("dragend", async e => {
|
||||||
try {
|
const { lat, lng } = e.target.getLatLng();
|
||||||
await dispatch(
|
try {
|
||||||
updateAreaThunk({
|
await dispatch(
|
||||||
idLocation: item.idLocation,
|
updateAreaThunk({
|
||||||
idMap: item.idMaps,
|
idLocation: item.idLocation,
|
||||||
newCoords: { x: lat, y: lng },
|
idMap: item.idMaps,
|
||||||
})
|
newCoords: { x: lat, y: lng },
|
||||||
).unwrap();
|
})
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
).unwrap();
|
||||||
console.log("✔️ Koordinaten erfolgreich aktualisiert:", { lat, lng });
|
if (getDebugLog()) {
|
||||||
|
console.log("✔️ Koordinaten erfolgreich aktualisiert:", { lat, lng });
|
||||||
|
}
|
||||||
|
onUpdateSuccess?.(); // optionaler Callback
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Fehler beim Aktualisieren der Koordinaten:", error);
|
||||||
}
|
}
|
||||||
onUpdateSuccess?.(); // optionaler Callback
|
});
|
||||||
} catch (error) {
|
}
|
||||||
console.error("❌ Fehler beim Aktualisieren der Koordinaten:", error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return marker;
|
return marker;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { getDebugLog } from "@/utils/configUtils.js";
|
||||||
// components/contextmenu/useMapContextMenu.js
|
// components/contextmenu/useMapContextMenu.js
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { zoomIn, zoomOut, centerHere } from "../../utils/zoomAndCenterUtils";
|
import { zoomIn, zoomOut, centerHere } from "../../utils/zoomAndCenterUtils";
|
||||||
@@ -71,7 +72,7 @@ const addItemsToMapContextMenu = (
|
|||||||
if (!menuItemAdded && map && map.contextmenu) {
|
if (!menuItemAdded && map && map.contextmenu) {
|
||||||
const editMode = localStorage.getItem("editMode") === "true";
|
const editMode = localStorage.getItem("editMode") === "true";
|
||||||
if (editMode) {
|
if (editMode) {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("editMode localStorage:", localStorage.getItem("editMode"));
|
console.log("editMode localStorage:", localStorage.getItem("editMode"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,30 +30,30 @@ const useDynamicDeviceLayers = (map, GisSystemStatic, mapLayersVisibility, prior
|
|||||||
if (!map || GisSystemStatic.length === 0) return;
|
if (!map || GisSystemStatic.length === 0) return;
|
||||||
|
|
||||||
GisSystemStatic.forEach(({ Name, IdSystem }) => {
|
GisSystemStatic.forEach(({ Name, IdSystem }) => {
|
||||||
const key = `system-${IdSystem}`; // Einheitlicher Key
|
const key = `system-${IdSystem}`;
|
||||||
|
// LayerGroup immer komplett neu erstellen, um doppelte Marker zu verhindern
|
||||||
if (!layerRefs.current[key]) {
|
if (layerRefs.current[key]) {
|
||||||
layerRefs.current[key] = new L.LayerGroup().addTo(map);
|
if (map.hasLayer(layerRefs.current[key])) {
|
||||||
|
map.removeLayer(layerRefs.current[key]);
|
||||||
|
}
|
||||||
|
layerRefs.current[key].clearLayers();
|
||||||
|
delete layerRefs.current[key];
|
||||||
}
|
}
|
||||||
|
layerRefs.current[key] = new L.LayerGroup();
|
||||||
|
layerRefs.current[key].addTo(map);
|
||||||
|
|
||||||
createAndSetDevices(
|
createAndSetDevices(
|
||||||
IdSystem,
|
IdSystem,
|
||||||
newMarkers => {
|
newMarkers => {
|
||||||
const oldMarkers = markerStates[key];
|
// Füge neue Marker der LayerGroup hinzu (nur Geräte-Marker)
|
||||||
|
if (layerRefs.current[key]) {
|
||||||
// Entferne alte Marker aus Karte und OMS
|
layerRefs.current[key].clearLayers();
|
||||||
if (oldMarkers && Array.isArray(oldMarkers)) {
|
// Nur eindeutige Marker hinzufügen
|
||||||
oldMarkers.forEach(marker => {
|
const uniqueMarkers = Array.isArray(newMarkers) ? Array.from(new Set(newMarkers)) : [];
|
||||||
if (map.hasLayer(marker)) {
|
uniqueMarkers.forEach(marker => {
|
||||||
map.removeLayer(marker);
|
marker.addTo(layerRefs.current[key]);
|
||||||
}
|
|
||||||
if (oms) {
|
|
||||||
oms.removeMarker(marker);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Neue Marker setzen
|
|
||||||
setMarkerStates(prev => ({ ...prev, [key]: newMarkers }));
|
setMarkerStates(prev => ({ ...prev, [key]: newMarkers }));
|
||||||
},
|
},
|
||||||
GisSystemStatic,
|
GisSystemStatic,
|
||||||
@@ -69,20 +69,21 @@ const useDynamicDeviceLayers = (map, GisSystemStatic, mapLayersVisibility, prior
|
|||||||
if (!map) return;
|
if (!map) return;
|
||||||
const editMode = localStorage.getItem("editMode") === "true";
|
const editMode = localStorage.getItem("editMode") === "true";
|
||||||
|
|
||||||
Object.entries(markerStates).forEach(([key, markers]) => {
|
Object.entries(layerRefs.current).forEach(([key, layerGroup]) => {
|
||||||
const isVisible = mapLayersVisibility[key];
|
const isVisible = mapLayersVisibility[key] ?? true;
|
||||||
markers.forEach(marker => {
|
if (editMode || isVisible === false) {
|
||||||
const hasLayer = map.hasLayer(marker);
|
if (map.hasLayer(layerGroup)) {
|
||||||
if (editMode || !isVisible) {
|
map.removeLayer(layerGroup);
|
||||||
if (hasLayer) map.removeLayer(marker);
|
|
||||||
} else {
|
|
||||||
if (!hasLayer) marker.addTo(map);
|
|
||||||
}
|
}
|
||||||
});
|
} else if (isVisible === true) {
|
||||||
|
if (!map.hasLayer(layerGroup)) {
|
||||||
|
layerGroup.addTo(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Overlapping-Check bleibt wie gehabt
|
||||||
const allMarkers = Object.values(markerStates).filter(Array.isArray).flat();
|
const allMarkers = Object.values(markerStates).filter(Array.isArray).flat();
|
||||||
|
|
||||||
checkOverlappingMarkers(map, allMarkers, plusRoundIcon);
|
checkOverlappingMarkers(map, allMarkers, plusRoundIcon);
|
||||||
}, [map, markerStates, mapLayersVisibility]);
|
}, [map, markerStates, mapLayersVisibility]);
|
||||||
|
|
||||||
|
|||||||
0
components/hooks/useStationCache.js
Normal file
@@ -6,10 +6,13 @@ import "leaflet-contextmenu/dist/leaflet.contextmenu.css";
|
|||||||
import "leaflet-contextmenu";
|
import "leaflet-contextmenu";
|
||||||
import "leaflet.smooth_marker_bouncing";
|
import "leaflet.smooth_marker_bouncing";
|
||||||
import "react-toastify/dist/ReactToastify.css";
|
import "react-toastify/dist/ReactToastify.css";
|
||||||
import { InformationCircleIcon } from "@heroicons/react/20/solid";
|
import { Icon } from "@iconify/react";
|
||||||
import PoiUpdateModal from "@/components/pois/poiUpdateModal/PoiUpdateModal.js";
|
import PoiUpdateModal from "@/components/pois/poiUpdateModal/PoiUpdateModal.js";
|
||||||
import { ToastContainer, toast } from "react-toastify";
|
import { ToastContainer, toast } from "react-toastify";
|
||||||
import plusRoundIcon from "../icons/devices/overlapping/PlusRoundIcon.js";
|
import plusRoundIcon from "../icons/devices/overlapping/PlusRoundIcon.js";
|
||||||
|
import StartIcon from "@/components/gisPolylines/icons/StartIcon.js";
|
||||||
|
import EndIcon from "@/components/gisPolylines/icons/EndIcon.js";
|
||||||
|
import CircleIcon from "@/components/gisPolylines/icons/CircleIcon.js";
|
||||||
import { restoreMapSettings, checkOverlappingMarkers } from "../../utils/mapUtils.js";
|
import { restoreMapSettings, checkOverlappingMarkers } from "../../utils/mapUtils.js";
|
||||||
|
|
||||||
import addItemsToMapContextMenu from "@/components/contextmenu/useMapContextMenu.js";
|
import addItemsToMapContextMenu from "@/components/contextmenu/useMapContextMenu.js";
|
||||||
@@ -21,6 +24,7 @@ import { useMapComponentState } from "@/components/hooks/useMapComponentState.js
|
|||||||
import CoordinatePopup from "@/components/contextmenu/CoordinatePopup.js";
|
import CoordinatePopup from "@/components/contextmenu/CoordinatePopup.js";
|
||||||
//----------Ui Widgets----------------
|
//----------Ui Widgets----------------
|
||||||
import MapLayersControlPanel from "@/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js";
|
import MapLayersControlPanel from "@/components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js";
|
||||||
|
import BaseMapPanel from "@/components/uiWidgets/baseMapPanel/BaseMapPanel.js";
|
||||||
import CoordinateInput from "@/components/uiWidgets/CoordinateInput.js";
|
import CoordinateInput from "@/components/uiWidgets/CoordinateInput.js";
|
||||||
import VersionInfoModal from "@/components/uiWidgets/VersionInfoModal.js";
|
import VersionInfoModal from "@/components/uiWidgets/VersionInfoModal.js";
|
||||||
//----------Daten aus API--------------------
|
//----------Daten aus API--------------------
|
||||||
@@ -35,7 +39,9 @@ import { useSelector, useDispatch } from "react-redux";
|
|||||||
import { setSelectedPoi } from "@/redux/slices/database/pois/selectedPoiSlice.js";
|
import { setSelectedPoi } from "@/redux/slices/database/pois/selectedPoiSlice.js";
|
||||||
import { setDisabled } from "@/redux/slices/database/polylines/polylineEventsDisabledSlice.js";
|
import { setDisabled } from "@/redux/slices/database/polylines/polylineEventsDisabledSlice.js";
|
||||||
import { setMapId, setUserId } from "@/redux/slices/urlParameterSlice";
|
import { setMapId, setUserId } from "@/redux/slices/urlParameterSlice";
|
||||||
import { selectMapLayersState } from "@/redux/slices/mapLayersSlice";
|
import { selectMapLayersState, setLayerVisibility } from "@/redux/slices/mapLayersSlice";
|
||||||
|
import { setSelectedArea } from "@/redux/slices/selectedAreaSlice";
|
||||||
|
import { incrementZoomTrigger } from "@/redux/slices/zoomTriggerSlice";
|
||||||
import { setCurrentPoi } from "@/redux/slices/database/pois/currentPoiSlice.js";
|
import { setCurrentPoi } from "@/redux/slices/database/pois/currentPoiSlice.js";
|
||||||
import { selectGisLines } from "@/redux/slices/database/polylines/gisLinesSlice";
|
import { selectGisLines } from "@/redux/slices/database/polylines/gisLinesSlice";
|
||||||
import { selectGisLinesStatus } from "@/redux/slices/webservice/gisLinesStatusSlice";
|
import { selectGisLinesStatus } from "@/redux/slices/webservice/gisLinesStatusSlice";
|
||||||
@@ -54,6 +60,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
selectPolylineVisible,
|
selectPolylineVisible,
|
||||||
setPolylineVisible,
|
setPolylineVisible,
|
||||||
|
initializePolylineFromLocalStorageThunk,
|
||||||
} from "@/redux/slices/database/polylines/polylineLayerVisibleSlice.js";
|
} from "@/redux/slices/database/polylines/polylineLayerVisibleSlice.js";
|
||||||
import { selectGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice.js";
|
import { selectGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice.js";
|
||||||
import {
|
import {
|
||||||
@@ -84,6 +91,7 @@ import { monitorHeapWithRedux } from "@/utils/common/monitorMemory";
|
|||||||
import { io } from "socket.io-client";
|
import { io } from "socket.io-client";
|
||||||
|
|
||||||
import { setGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice.js";
|
import { setGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice.js";
|
||||||
|
import { getDebugLog } from "../../utils/configUtils";
|
||||||
//-----------------------------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------------------------
|
||||||
const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||||
//-------------------------------
|
//-------------------------------
|
||||||
@@ -96,6 +104,14 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
const countdownActive = useSelector(state => state.polylineContextMenu.countdownActive);
|
const countdownActive = useSelector(state => state.polylineContextMenu.countdownActive);
|
||||||
const isPolylineContextMenuOpen = useSelector(state => state.polylineContextMenu.isOpen);
|
const isPolylineContextMenuOpen = useSelector(state => state.polylineContextMenu.isOpen);
|
||||||
const polylineVisible = useSelector(selectPolylineVisible);
|
const polylineVisible = useSelector(selectPolylineVisible);
|
||||||
|
const polylineInitialized = useSelector(state => state.polylineLayerVisible.isInitialized);
|
||||||
|
const GisSystemStatic = useSelector(selectGisSystemStatic);
|
||||||
|
// Prüfen, ob TALAS (IdSystem 1) erlaubt ist
|
||||||
|
const isTalasAllowed = Array.isArray(GisSystemStatic)
|
||||||
|
? GisSystemStatic.some(
|
||||||
|
system => system.IdSystem === 1 && system.Allow === 1 && system.Map === 1
|
||||||
|
)
|
||||||
|
: false;
|
||||||
|
|
||||||
const isPoiTypLoaded = useSelector(state => state.poiTypes.status === "succeeded");
|
const isPoiTypLoaded = useSelector(state => state.poiTypes.status === "succeeded");
|
||||||
const statusMeasurements = useSelector(state => state.gisStationsMeasurements.status);
|
const statusMeasurements = useSelector(state => state.gisStationsMeasurements.status);
|
||||||
@@ -107,7 +123,6 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
const zoomTrigger = useSelector(state => state.zoomTrigger.trigger);
|
const zoomTrigger = useSelector(state => state.zoomTrigger.trigger);
|
||||||
const poiReadTrigger = useSelector(state => state.poiReadFromDbTrigger.trigger);
|
const poiReadTrigger = useSelector(state => state.poiReadFromDbTrigger.trigger);
|
||||||
const GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict);
|
const GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict);
|
||||||
const GisSystemStatic = useSelector(selectGisSystemStatic);
|
|
||||||
const gisSystemStaticStatus = useSelector(state => state.gisSystemStatic.status);
|
const gisSystemStaticStatus = useSelector(state => state.gisSystemStatic.status);
|
||||||
const polylineEventsDisabled = useSelector(state => state.polylineEventsDisabled.disabled);
|
const polylineEventsDisabled = useSelector(state => state.polylineEventsDisabled.disabled);
|
||||||
const mapLayersVisibility = useSelector(selectMapLayersState) || {};
|
const mapLayersVisibility = useSelector(selectMapLayersState) || {};
|
||||||
@@ -139,6 +154,48 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
const mapRef = useRef(null); // Referenz auf das DIV-Element der Karte
|
const mapRef = useRef(null); // Referenz auf das DIV-Element der Karte
|
||||||
const [map, setMap] = useState(null); // Zustand der Karteninstanz
|
const [map, setMap] = useState(null); // Zustand der Karteninstanz
|
||||||
const [oms, setOms] = useState(null); // State für OMS-Instanz
|
const [oms, setOms] = useState(null); // State für OMS-Instanz
|
||||||
|
// Sichtbarkeit der App-Info-Karte (unten links)
|
||||||
|
const [showAppInfoCard, setShowAppInfoCard] = useState(() => {
|
||||||
|
try {
|
||||||
|
const v = localStorage.getItem("showAppInfoCard");
|
||||||
|
return v === null ? true : v === "true";
|
||||||
|
} catch (_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Sichtbarkeit des Layer-Kontrollpanels (oben rechts)
|
||||||
|
const [showLayersPanel, setShowLayersPanel] = useState(() => {
|
||||||
|
try {
|
||||||
|
const v = localStorage.getItem("showLayersPanel");
|
||||||
|
return v === null ? true : v === "true";
|
||||||
|
} catch (_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Sichtbarkeit des Base-Map Panels (oben rechts, unter Toolbar)
|
||||||
|
const [showBaseMapPanel, setShowBaseMapPanel] = useState(() => {
|
||||||
|
try {
|
||||||
|
const v = localStorage.getItem("showBaseMapPanel");
|
||||||
|
return v === null ? false : v === "true";
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Sichtbarkeit der Koordinaten-Suche (Lupe)
|
||||||
|
const [showCoordinateInput, setShowCoordinateInput] = useState(() => {
|
||||||
|
try {
|
||||||
|
const v = localStorage.getItem("showCoordinateInput");
|
||||||
|
return v === null ? false : v === "true";
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Flag, ob Nutzer die Polyline-Checkbox manuell betätigt hat
|
||||||
|
// Nutzer-Flag global auf window, damit auch Redux darauf zugreifen kann
|
||||||
|
if (typeof window !== "undefined" && window.userToggledPolyline === undefined) {
|
||||||
|
window.userToggledPolyline = false;
|
||||||
|
}
|
||||||
|
|
||||||
//-----userRights----------------
|
//-----userRights----------------
|
||||||
const isRightsLoaded = useSelector(
|
const isRightsLoaded = useSelector(
|
||||||
@@ -170,6 +227,14 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
const [popupCoordinates, setPopupCoordinates] = useState(null);
|
const [popupCoordinates, setPopupCoordinates] = useState(null);
|
||||||
const [popupVisible, setPopupVisible] = useState(false);
|
const [popupVisible, setPopupVisible] = useState(false);
|
||||||
const [poiData, setPoiData] = useState([]);
|
const [poiData, setPoiData] = useState([]);
|
||||||
|
// Edit mode state mirrors MapLayersControlPanel's behavior
|
||||||
|
const [editMode, setEditMode] = useState(() => {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem("editMode") === "true";
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const openVersionInfoModal = () => {
|
const openVersionInfoModal = () => {
|
||||||
setShowVersionInfoModal(true);
|
setShowVersionInfoModal(true);
|
||||||
@@ -191,6 +256,31 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Persistiere Sichtbarkeit der App-Info-Karte
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem("showAppInfoCard", String(showAppInfoCard));
|
||||||
|
} catch (_) {}
|
||||||
|
}, [showAppInfoCard]);
|
||||||
|
// Persistiere Sichtbarkeit des Layer-Panels
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem("showLayersPanel", String(showLayersPanel));
|
||||||
|
} catch (_) {}
|
||||||
|
}, [showLayersPanel]);
|
||||||
|
// Persistiere Sichtbarkeit des Base-Map Panels
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem("showBaseMapPanel", String(showBaseMapPanel));
|
||||||
|
} catch (_) {}
|
||||||
|
}, [showBaseMapPanel]);
|
||||||
|
// Persistiere Sichtbarkeit der Koordinaten-Suche
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem("showCoordinateInput", String(showCoordinateInput));
|
||||||
|
} catch (_) {}
|
||||||
|
}, [showCoordinateInput]);
|
||||||
|
|
||||||
//--------------------------------------------
|
//--------------------------------------------
|
||||||
|
|
||||||
const handleCoordinatesSubmit = coords => {
|
const handleCoordinatesSubmit = coords => {
|
||||||
@@ -200,6 +290,16 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
//-----------------------------Map Initialisierung----------------
|
//-----------------------------Map Initialisierung----------------
|
||||||
|
// Default map options for Leaflet
|
||||||
|
const mapOptions = {
|
||||||
|
center: currentCenter,
|
||||||
|
zoom: currentZoom,
|
||||||
|
zoomControl: true,
|
||||||
|
contextmenu: true,
|
||||||
|
contextmenuWidth: 180,
|
||||||
|
contextmenuItems: [],
|
||||||
|
};
|
||||||
|
|
||||||
useInitializeMap(
|
useInitializeMap(
|
||||||
map,
|
map,
|
||||||
mapRef,
|
mapRef,
|
||||||
@@ -208,10 +308,85 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
setMenuItemAdded,
|
setMenuItemAdded,
|
||||||
addItemsToMapContextMenu,
|
addItemsToMapContextMenu,
|
||||||
hasRights,
|
hasRights,
|
||||||
value => dispatch(setDisabled(value))
|
value => dispatch(setDisabled(value)),
|
||||||
|
mapOptions // pass mapOptions
|
||||||
);
|
);
|
||||||
|
|
||||||
//-------------------------React Hooks--------------------------------
|
//-------------------------React Hooks--------------------------------
|
||||||
|
|
||||||
|
// URL-Parameter extrahieren und kartenspezifische localStorage-Keys verwenden
|
||||||
|
useEffect(() => {
|
||||||
|
// Immer beim Umschalten der Kabelstrecken-Checkbox prüfen!
|
||||||
|
if (map) {
|
||||||
|
if (!polylineVisible) {
|
||||||
|
map.eachLayer(layer => {
|
||||||
|
// Entferne alle Marker mit StartIcon, EndIcon oder CircleIcon (Stützpunkt)
|
||||||
|
if (
|
||||||
|
layer instanceof L.Marker &&
|
||||||
|
layer.options &&
|
||||||
|
layer.options.icon &&
|
||||||
|
(layer.options.icon === StartIcon ||
|
||||||
|
layer.options.icon === EndIcon ||
|
||||||
|
layer.options.icon === CircleIcon)
|
||||||
|
) {
|
||||||
|
map.removeLayer(layer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Initialisierung der Layer-Visibility und Polyline-Redux-State nur beim Mount
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const mapId = params.get("m");
|
||||||
|
const userId = params.get("u");
|
||||||
|
|
||||||
|
if (mapId && userId) {
|
||||||
|
// Speichere aktuelle Map- und User-ID
|
||||||
|
localStorage.setItem("currentMapId", mapId);
|
||||||
|
localStorage.setItem("currentUserId", userId);
|
||||||
|
|
||||||
|
// Kartenspezifischer localStorage-Key
|
||||||
|
const mapStorageKey = `mapLayersVisibility_m${mapId}_u${userId}`;
|
||||||
|
const storedMapLayersVisibility = localStorage.getItem(mapStorageKey);
|
||||||
|
|
||||||
|
if (storedMapLayersVisibility) {
|
||||||
|
try {
|
||||||
|
const parsedVisibility = JSON.parse(storedMapLayersVisibility);
|
||||||
|
// Nur initial setzen, wenn Nutzer noch nicht manuell eingegriffen hat
|
||||||
|
if (!userToggledPolyline.current) {
|
||||||
|
Object.keys(parsedVisibility).forEach(key => {
|
||||||
|
dispatch(setLayerVisibility({ layer: key, visibility: parsedVisibility[key] }));
|
||||||
|
});
|
||||||
|
console.log(
|
||||||
|
`🔄 mapLayersVisibility für Map ${mapId}/User ${userId} geladen:`,
|
||||||
|
parsedVisibility
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Fehler beim Laden von mapLayersVisibility:", error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`📝 Keine gespeicherten Einstellungen für Map ${mapId}/User ${userId} gefunden`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Redux Polyline Sichtbarkeit initialisieren (map/user spezifisch)
|
||||||
|
if (!userToggledPolyline.current) {
|
||||||
|
dispatch(initializePolylineFromLocalStorageThunk());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [dispatch, polylineVisible, map]);
|
||||||
|
// Callback für Checkbox-Umschaltung (Kabelstrecken)
|
||||||
|
const handlePolylineCheckboxChange = useCallback(
|
||||||
|
checked => {
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
window.userToggledPolyline = true;
|
||||||
|
}
|
||||||
|
dispatch(setPolylineVisible(checked));
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (linesData && Array.isArray(linesData)) {
|
if (linesData && Array.isArray(linesData)) {
|
||||||
const transformed = linesData.map(item => ({
|
const transformed = linesData.map(item => ({
|
||||||
@@ -222,6 +397,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
setLinePositions(transformed);
|
setLinePositions(transformed);
|
||||||
}
|
}
|
||||||
}, [linesData]);
|
}, [linesData]);
|
||||||
|
|
||||||
//--------------------------------------------
|
//--------------------------------------------
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchPoiIconsDataThunk());
|
dispatch(fetchPoiIconsDataThunk());
|
||||||
@@ -309,72 +485,106 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
//Tooltip an mouse position anzeigen für die Linien
|
//Tooltip an mouse position anzeigen für die Linien
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!map) return;
|
if (!map) return;
|
||||||
|
console.log(
|
||||||
|
"[MapComponent/useEffect] polylineVisible:",
|
||||||
|
polylineVisible,
|
||||||
|
"isTalasAllowed:",
|
||||||
|
isTalasAllowed,
|
||||||
|
"poiLayerVisible:",
|
||||||
|
poiLayerVisible
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wenn TALAS nicht erlaubt ist, Polyline-Checkbox und Anzeige deaktivieren
|
||||||
|
if (!isTalasAllowed) {
|
||||||
|
cleanupPolylinesForMemory(polylines, map);
|
||||||
|
setPolylines([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Die Sichtbarkeit der Polylines hängt nur noch vom Redux-Slice ab
|
||||||
// vorherige Marker & Polylinien vollständig bereinigen
|
// vorherige Marker & Polylinien vollständig bereinigen
|
||||||
|
(Array.isArray(markers) ? markers : []).forEach(marker => {
|
||||||
markers.forEach(marker => {
|
|
||||||
marker.remove();
|
marker.remove();
|
||||||
});
|
});
|
||||||
cleanupPolylinesForMemory(polylines, map);
|
cleanupPolylinesForMemory(polylines, map);
|
||||||
|
console.log("[MapComponent/useEffect] Nach cleanupPolylinesForMemory, polylines:", polylines);
|
||||||
|
|
||||||
// Setze neue Marker und Polylinien mit den aktuellen Daten
|
// Setze neue Marker und Polylinien mit den aktuellen Daten (asynchron!)
|
||||||
const { markers: newMarkers, polylines: newPolylines } = setupPolylines(
|
const updatePolylines = async () => {
|
||||||
map,
|
if (polylineVisible) {
|
||||||
linePositions,
|
const { markers: newMarkers, polylines: newPolylines } = await setupPolylines(
|
||||||
lineColors,
|
map,
|
||||||
tooltipContents,
|
linePositions,
|
||||||
setNewCoords,
|
lineColors,
|
||||||
tempMarker,
|
tooltipContents,
|
||||||
currentZoom,
|
setNewCoords,
|
||||||
currentCenter,
|
tempMarker,
|
||||||
polylineVisible // kommt aus Redux
|
currentZoom,
|
||||||
);
|
currentCenter,
|
||||||
|
polylineVisible
|
||||||
|
);
|
||||||
|
|
||||||
newPolylines.forEach((polyline, index) => {
|
(Array.isArray(newPolylines) ? newPolylines : []).forEach((polyline, index) => {
|
||||||
const tooltipContent =
|
const tooltipContent =
|
||||||
tooltipContents[`${linePositions[index].idLD}-${linePositions[index].idModul}`] ||
|
tooltipContents[`${linePositions[index].idLD}-${linePositions[index].idModul}`] ||
|
||||||
"Die Linie ist noch nicht in Webservice vorhanden oder bekommt keine Daten";
|
"Die Linie ist noch nicht in Webservice vorhanden oder bekommt keine Daten";
|
||||||
|
|
||||||
polyline.bindTooltip(tooltipContent, {
|
polyline.bindTooltip(tooltipContent, {
|
||||||
permanent: false,
|
permanent: false,
|
||||||
direction: "auto",
|
direction: "auto",
|
||||||
sticky: true,
|
sticky: true,
|
||||||
offset: [20, 0],
|
offset: [20, 0],
|
||||||
pane: "tooltipPane",
|
pane: "tooltipPane",
|
||||||
});
|
});
|
||||||
|
|
||||||
polyline.on("mouseover", e => {
|
polyline.on("mouseover", e => {
|
||||||
const tooltip = polyline.getTooltip();
|
const tooltip = polyline.getTooltip();
|
||||||
if (tooltip) {
|
if (tooltip) {
|
||||||
const mousePos = e.containerPoint;
|
const mousePos = e.containerPoint;
|
||||||
const mapSize = map.getSize();
|
const mapSize = map.getSize();
|
||||||
|
|
||||||
let direction = "right";
|
let direction = "right";
|
||||||
|
|
||||||
if (mousePos.x > mapSize.x - 100) {
|
if (mousePos.x > mapSize.x - 100) {
|
||||||
direction = "left";
|
direction = "left";
|
||||||
} else if (mousePos.x < 100) {
|
} else if (mousePos.x < 100) {
|
||||||
direction = "right";
|
direction = "right";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mousePos.y > mapSize.y - 100) {
|
if (mousePos.y > mapSize.y - 100) {
|
||||||
direction = "top";
|
direction = "top";
|
||||||
} else if (mousePos.y < 100) {
|
} else if (mousePos.y < 100) {
|
||||||
direction = "bottom";
|
direction = "bottom";
|
||||||
}
|
}
|
||||||
|
|
||||||
tooltip.options.direction = direction;
|
tooltip.options.direction = direction;
|
||||||
polyline.openTooltip(e.latlng);
|
polyline.openTooltip(e.latlng);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
polyline.on("mouseout", () => {
|
||||||
|
polyline.closeTooltip();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
cleanupMarkers(markers, oms);
|
||||||
|
setMarkers(newMarkers);
|
||||||
|
setPolylines(newPolylines);
|
||||||
|
console.log("[MapComponent/useEffect] setPolylines (sichtbar):", newPolylines);
|
||||||
|
} else {
|
||||||
|
// Entferne wirklich alle Polylinien-Layer von der Karte
|
||||||
|
if (map) {
|
||||||
|
map.eachLayer(layer => {
|
||||||
|
if (layer instanceof L.Polyline) {
|
||||||
|
map.removeLayer(layer);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
cleanupPolylinesForMemory(polylines, map);
|
||||||
|
setPolylines([]);
|
||||||
polyline.on("mouseout", () => {
|
console.log("[MapComponent/useEffect] setPolylines ([]), alle Polylinien entfernt");
|
||||||
polyline.closeTooltip();
|
}
|
||||||
});
|
};
|
||||||
});
|
updatePolylines();
|
||||||
cleanupMarkers(markers, oms);
|
|
||||||
setMarkers(newMarkers);
|
|
||||||
setPolylines(newPolylines);
|
|
||||||
}, [
|
}, [
|
||||||
map,
|
map,
|
||||||
linePositions,
|
linePositions,
|
||||||
@@ -384,6 +594,8 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
newCoords,
|
newCoords,
|
||||||
tempMarker,
|
tempMarker,
|
||||||
polylineVisible,
|
polylineVisible,
|
||||||
|
isTalasAllowed,
|
||||||
|
poiLayerVisible,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//--------------------------------------------
|
//--------------------------------------------
|
||||||
@@ -391,7 +603,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
//Test in useEffect
|
//Test in useEffect
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (map) {
|
if (map) {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("🗺️ Map-Einstellungen werden wiederhergestellt...");
|
console.log("🗺️ Map-Einstellungen werden wiederhergestellt...");
|
||||||
}
|
}
|
||||||
restoreMapSettings(map);
|
restoreMapSettings(map);
|
||||||
@@ -400,7 +612,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
//--------------------------------------------
|
//--------------------------------------------
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (map) {
|
if (map) {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("map in MapComponent: ", map);
|
console.log("map in MapComponent: ", map);
|
||||||
}
|
}
|
||||||
const handleMapMoveEnd = event => {
|
const handleMapMoveEnd = event => {
|
||||||
@@ -433,7 +645,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
const station = points.find(s => s.Area_Name === selectedArea);
|
const station = points.find(s => s.Area_Name === selectedArea);
|
||||||
|
|
||||||
if (station) {
|
if (station) {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("📌 Gefundene Station:", station);
|
console.log("📌 Gefundene Station:", station);
|
||||||
}
|
}
|
||||||
map.flyTo([station.X, station.Y], 14);
|
map.flyTo([station.X, station.Y], 14);
|
||||||
@@ -479,7 +691,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
//--------------------------------------------
|
//--------------------------------------------
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (map) {
|
if (map) {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("6- Karteninstanz (map) wurde jetzt erfolgreich initialisiert");
|
console.log("6- Karteninstanz (map) wurde jetzt erfolgreich initialisiert");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -492,7 +704,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
map.whenReady(() => {
|
map.whenReady(() => {
|
||||||
timeoutId = setTimeout(() => {
|
timeoutId = setTimeout(() => {
|
||||||
if (map.contextmenu) {
|
if (map.contextmenu) {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("Contextmenu ist vorhanden");
|
console.log("Contextmenu ist vorhanden");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -523,7 +735,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
const handleLocationUpdate = async (idLocation, idMap, newCoords) => {
|
const handleLocationUpdate = async (idLocation, idMap, newCoords) => {
|
||||||
try {
|
try {
|
||||||
await dispatch(updateAreaThunk({ idLocation, idMap, newCoords })).unwrap();
|
await dispatch(updateAreaThunk({ idLocation, idMap, newCoords })).unwrap();
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("Koordinaten erfolgreich aktualisiert:", result);
|
console.log("Koordinaten erfolgreich aktualisiert:", result);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -547,14 +759,14 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
// Entferne alle Marker aus der Karte
|
// Entferne alle Marker aus der Karte
|
||||||
if (!map) return; // Sicherstellen, dass map existiert
|
if (!map) return; // Sicherstellen, dass map existiert
|
||||||
|
|
||||||
areaMarkers.forEach(marker => {
|
(Array.isArray(areaMarkers) ? areaMarkers : []).forEach(marker => {
|
||||||
if (map.hasLayer(marker)) {
|
if (map.hasLayer(marker)) {
|
||||||
map.removeLayer(marker);
|
map.removeLayer(marker);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Wenn editMode aktiviert ist, füge die Marker hinzu und aktiviere Dragging
|
// Wenn editMode aktiviert ist, füge die Marker hinzu und aktiviere Dragging
|
||||||
areaMarkers.forEach(marker => {
|
(Array.isArray(areaMarkers) ? areaMarkers : []).forEach(marker => {
|
||||||
if (!map.hasLayer(marker)) {
|
if (!map.hasLayer(marker)) {
|
||||||
marker.addTo(map); // Layer hinzufügen
|
marker.addTo(map); // Layer hinzufügen
|
||||||
}
|
}
|
||||||
@@ -615,11 +827,10 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
//--------------------------------------------
|
//--------------------------------------------
|
||||||
// Beim ersten Client-Render den Wert aus localStorage laden
|
|
||||||
useEffect(() => {
|
// (Initialisierung erfolgt in MapLayersControlPanel)
|
||||||
const storedPolylineVisible = localStorage.getItem("polylineVisible") === "true";
|
//--------------------------------------------
|
||||||
dispatch(setPolylineVisible(storedPolylineVisible));
|
// MapComponent reagiert nicht mehr direkt auf localStorage-Events für polylineVisible
|
||||||
}, [dispatch]);
|
|
||||||
//--------------------------------------------
|
//--------------------------------------------
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (statusStaticDistrict === "idle") {
|
if (statusStaticDistrict === "idle") {
|
||||||
@@ -693,7 +904,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
// console.log(`⏳ Redux Countdown: ${countdown} Sekunden`);
|
// console.log(`⏳ Redux Countdown: ${countdown} Sekunden`);
|
||||||
|
|
||||||
if (countdown <= 2) {
|
if (countdown <= 2) {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("🚀 Kontextmenü wird wegen Countdown < 2 geschlossen.");
|
console.log("🚀 Kontextmenü wird wegen Countdown < 2 geschlossen.");
|
||||||
}
|
}
|
||||||
dispatch(closePolylineContextMenu());
|
dispatch(closePolylineContextMenu());
|
||||||
@@ -738,7 +949,9 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (poiTypStatus === "succeeded" && Array.isArray(poiTypData)) {
|
if (poiTypStatus === "succeeded" && Array.isArray(poiTypData)) {
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
poiTypData.forEach(item => map.set(item.idPoiTyp, item.name));
|
(Array.isArray(poiTypData) ? poiTypData : []).forEach(item =>
|
||||||
|
map.set(item.idPoiTyp, item.name)
|
||||||
|
);
|
||||||
setPoiTypMap(map);
|
setPoiTypMap(map);
|
||||||
}
|
}
|
||||||
}, [poiTypData, poiTypStatus]);
|
}, [poiTypData, poiTypStatus]);
|
||||||
@@ -749,30 +962,6 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
}
|
}
|
||||||
}, [poiIconsData, poiIconsStatus]);
|
}, [poiIconsData, poiIconsStatus]);
|
||||||
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
||||||
useEffect(() => {
|
|
||||||
if (!map) return;
|
|
||||||
|
|
||||||
const editMode = localStorage.getItem("editMode") === "true";
|
|
||||||
|
|
||||||
Object.entries(markerStates).forEach(([systemName, markers]) => {
|
|
||||||
const isVisible = mapLayersVisibility[systemName];
|
|
||||||
markers.forEach(marker => {
|
|
||||||
const hasLayer = map.hasLayer(marker);
|
|
||||||
if (editMode || !isVisible) {
|
|
||||||
if (hasLayer) map.removeLayer(marker);
|
|
||||||
} else {
|
|
||||||
if (!hasLayer) marker.addTo(map);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// optional für alle zusammen
|
|
||||||
const allMarkers = Object.values(markerStates)
|
|
||||||
.filter(entry => Array.isArray(entry))
|
|
||||||
.flat();
|
|
||||||
|
|
||||||
checkOverlappingMarkers(map, allMarkers, plusRoundIcon);
|
|
||||||
}, [map, markerStates, mapLayersVisibility]);
|
|
||||||
|
|
||||||
//----------------------------------------------
|
//----------------------------------------------
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -848,7 +1037,45 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
}, [GisStationsStaticDistrict]);
|
}, [GisStationsStaticDistrict]);
|
||||||
const { Points = [] } = useSelector(selectGisStationsStaticDistrict);
|
const { Points = [] } = useSelector(selectGisStationsStaticDistrict);
|
||||||
useEffect(() => {}, [triggerUpdate]);
|
useEffect(() => {}, [triggerUpdate]);
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("📊 GisSystemStatic:", GisSystemStatic);
|
||||||
|
}, [GisSystemStatic]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (Array.isArray(GisSystemStatic)) {
|
||||||
|
(Array.isArray(GisSystemStatic) ? GisSystemStatic : []).forEach(system => {
|
||||||
|
const key = `system-${system.IdSystem}`;
|
||||||
|
if (!(key in mapLayersVisibility)) {
|
||||||
|
dispatch(setLayerVisibility({ key, value: true })); // Sichtbarkeit aktivieren
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [GisSystemStatic, mapLayersVisibility, dispatch]);
|
||||||
|
|
||||||
//---------------------------------------------
|
//---------------------------------------------
|
||||||
|
//--------------------------------------------
|
||||||
|
// Expand handler (same behavior as MapLayersControlPanel expand icon)
|
||||||
|
const handleExpandClick = () => {
|
||||||
|
dispatch(setSelectedArea("Station wählen"));
|
||||||
|
dispatch(incrementZoomTrigger());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Toggle edit mode (same logic as EditModeToggle component)
|
||||||
|
const hasEditRight = Array.isArray(userRights)
|
||||||
|
? userRights.includes?.(56) || userRights.some?.(r => r?.IdRight === 56)
|
||||||
|
: false;
|
||||||
|
const toggleEditMode = () => {
|
||||||
|
if (!hasEditRight) return;
|
||||||
|
const next = !editMode;
|
||||||
|
setEditMode(next);
|
||||||
|
try {
|
||||||
|
localStorage.setItem("editMode", String(next));
|
||||||
|
} catch (_) {}
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//--------------------------------------------
|
//--------------------------------------------
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -903,28 +1130,136 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{GisStationsStaticDistrict && GisStationsStaticDistrict.Points?.length > 0 && (
|
{GisStationsStaticDistrict &&
|
||||||
<MapLayersControlPanel className="z-50" />
|
GisStationsStaticDistrict.Points?.length > 0 &&
|
||||||
)}
|
showLayersPanel && (
|
||||||
|
<MapLayersControlPanel
|
||||||
|
className="z-50"
|
||||||
|
handlePolylineCheckboxChange={handlePolylineCheckboxChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<CoordinateInput onCoordinatesSubmit={handleCoordinatesSubmit} />
|
{showCoordinateInput && <CoordinateInput onCoordinatesSubmit={handleCoordinatesSubmit} />}
|
||||||
<div id="map" ref={mapRef} className="z-0" style={{ height: "100vh", width: "100vw" }}></div>
|
<div id="map" ref={mapRef} className="z-0" style={{ height: "100vh", width: "100vw" }}></div>
|
||||||
|
{/* Top-right controls: layers, info, expand, edit, and base map stack */}
|
||||||
|
<div className="absolute top-3 right-3 z-50 pointer-events-auto flex items-center gap-2">
|
||||||
|
<button
|
||||||
|
onClick={toggleEditMode}
|
||||||
|
aria-label={editMode ? "Bearbeitungsmodus deaktivieren" : "Bearbeitungsmodus aktivieren"}
|
||||||
|
className={`rounded-full shadow p-1 ${
|
||||||
|
hasEditRight
|
||||||
|
? "bg-white/90 hover:bg-white"
|
||||||
|
: "bg-white/60 cursor-not-allowed opacity-50"
|
||||||
|
}`}
|
||||||
|
title={
|
||||||
|
hasEditRight
|
||||||
|
? editMode
|
||||||
|
? "Bearbeitungsmodus deaktivieren"
|
||||||
|
: "Bearbeitungsmodus aktivieren"
|
||||||
|
: "Keine Bearbeitungsrechte"
|
||||||
|
}
|
||||||
|
disabled={!hasEditRight}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={editMode ? "material-symbols:edit-off-rounded" : "material-symbols:edit-rounded"}
|
||||||
|
className="h-8 w-8 text-blue-900"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleExpandClick}
|
||||||
|
aria-label="Karte auf Standardansicht"
|
||||||
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
|
title="Karte auf Standardansicht"
|
||||||
|
>
|
||||||
|
<img src="/img/expand-icon.svg" alt="Expand" className="h-8 w-8" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowBaseMapPanel(v => !v)}
|
||||||
|
aria-label={
|
||||||
|
showBaseMapPanel ? "Kartenhintergrund ausblenden" : "Kartenhintergrund wählen"
|
||||||
|
}
|
||||||
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
|
title={showBaseMapPanel ? "Kartenhintergrund ausblenden" : "Kartenhintergrund wählen"}
|
||||||
|
>
|
||||||
|
<Icon icon="material-symbols:layers-rounded" className="h-8 w-8 text-blue-900" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowLayersPanel(v => !v)}
|
||||||
|
aria-label={showLayersPanel ? "Layer-Panel ausblenden" : "Layer-Panel einblenden"}
|
||||||
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
|
title={showLayersPanel ? "Layer-Panel ausblenden" : "Layer-Panel einblenden"}
|
||||||
|
>
|
||||||
|
<Icon icon="material-symbols:menu-rounded" className="h-8 w-8 text-blue-900" />
|
||||||
|
</button>
|
||||||
|
{/* Lupe: Koordinaten-Suche ein-/ausblenden */}
|
||||||
|
<button
|
||||||
|
onClick={() => setShowCoordinateInput(v => !v)}
|
||||||
|
aria-label={
|
||||||
|
showCoordinateInput ? "Koordinatensuche ausblenden" : "Koordinatensuche einblenden"
|
||||||
|
}
|
||||||
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
|
title={
|
||||||
|
showCoordinateInput ? "Koordinatensuche ausblenden" : "Koordinatensuche einblenden"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon icon="material-symbols:search-rounded" className="h-8 w-8 text-blue-900" />
|
||||||
|
</button>
|
||||||
|
{/* Marker-Icon (line-md) */}
|
||||||
|
<button
|
||||||
|
onClick={() => {}}
|
||||||
|
aria-label="Marker"
|
||||||
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
|
title="Marker"
|
||||||
|
>
|
||||||
|
<Icon icon="line-md:map-marker-filled" className="h-8 w-8 text-blue-900" />
|
||||||
|
</button>
|
||||||
|
{/* Alarm-Icon (mdi) */}
|
||||||
|
<button
|
||||||
|
onClick={() => {}}
|
||||||
|
aria-label="Alarm"
|
||||||
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
|
title="Alarm"
|
||||||
|
>
|
||||||
|
<Icon icon="mdi:alarm-light-outline" className="h-8 w-8 text-blue-900" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowAppInfoCard(v => !v)}
|
||||||
|
aria-label={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
||||||
|
className="rounded-full bg-white/90 hover:bg-white shadow p-1"
|
||||||
|
title={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="material-symbols:info-rounded"
|
||||||
|
className="text-blue-900 h-8 w-8 pr-1"
|
||||||
|
title={showAppInfoCard ? "Info ausblenden" : "Info einblenden"}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{showBaseMapPanel && map && (
|
||||||
|
<BaseMapPanel map={map} onClose={() => setShowBaseMapPanel(false)} />
|
||||||
|
)}
|
||||||
<CoordinatePopup isOpen={isPopupOpen} coordinates={currentCoordinates} onClose={closePopup} />
|
<CoordinatePopup isOpen={isPopupOpen} coordinates={currentCoordinates} onClose={closePopup} />
|
||||||
|
|
||||||
<div className="absolute bottom-3 left-3 w-72 p-4 bg-white rounded-lg shadow-md z-50">
|
{showAppInfoCard && (
|
||||||
<div className="flex justify-between items-center">
|
<div className="absolute bottom-3 left-3 w-72 p-4 bg-white rounded-lg shadow-md z-50">
|
||||||
<div>
|
<div className="flex justify-between items-center">
|
||||||
<span className="text-black text-lg font-semibold"> TALAS.Map </span>
|
<div>
|
||||||
<br />
|
<span className="text-black text-lg font-semibold"> TALAS.Map </span>
|
||||||
<span className="text-black text-lg">Version {appVersion}</span>
|
<br />
|
||||||
</div>
|
<span className="text-black text-lg">Version {appVersion}</span>
|
||||||
<div>
|
</div>
|
||||||
<button onClick={openVersionInfoModal}>
|
<div>
|
||||||
<InformationCircleIcon className="text-blue-900 h-8 w-8 pr-1" title="Weitere Infos" />
|
<button onClick={openVersionInfoModal}>
|
||||||
</button>
|
<Icon
|
||||||
|
icon="material-symbols:info-rounded"
|
||||||
|
className="text-blue-900 h-8 w-8 pr-1"
|
||||||
|
title="Weitere Infos"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<VersionInfoModal
|
<VersionInfoModal
|
||||||
showVersionInfoModal={showVersionInfoModal}
|
showVersionInfoModal={showVersionInfoModal}
|
||||||
closeVersionInfoModal={closeVersionInfoModal}
|
closeVersionInfoModal={closeVersionInfoModal}
|
||||||
|
|||||||
@@ -2,12 +2,58 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { initializeMap } from "../../../utils/initializeMap";
|
import { initializeMap } from "../../../utils/initializeMap";
|
||||||
|
|
||||||
const useInitializeMap = (map, mapRef, setMap, setOms, setMenuItemAdded, addItemsToMapContextMenu, hasRights, setPolylineEventsDisabled) => {
|
const useInitializeMap = (
|
||||||
|
map,
|
||||||
|
mapRef,
|
||||||
|
setMap,
|
||||||
|
setOms,
|
||||||
|
setMenuItemAdded,
|
||||||
|
addItemsToMapContextMenu,
|
||||||
|
hasRights,
|
||||||
|
setPolylineEventsDisabled,
|
||||||
|
mapOptions
|
||||||
|
) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mapRef.current && !map) {
|
let cancelled = false;
|
||||||
initializeMap(mapRef, setMap, setOms, setMenuItemAdded, addItemsToMapContextMenu, hasRights, setPolylineEventsDisabled);
|
function tryInit(firstAttempt = true) {
|
||||||
|
if (cancelled) return;
|
||||||
|
// Only try to initialize if mapRef.current is ready and in DOM
|
||||||
|
if (
|
||||||
|
mapRef.current &&
|
||||||
|
mapRef.current instanceof HTMLElement &&
|
||||||
|
document.body.contains(mapRef.current) &&
|
||||||
|
!map &&
|
||||||
|
!mapRef.current._leaflet_id
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const result = initializeMap(
|
||||||
|
mapRef.current, // pass DOM node, not ref
|
||||||
|
setMenuItemAdded,
|
||||||
|
addItemsToMapContextMenu,
|
||||||
|
hasRights,
|
||||||
|
setPolylineEventsDisabled,
|
||||||
|
firstAttempt // log error only on first real attempt
|
||||||
|
);
|
||||||
|
if (result && result.map && result.oms) {
|
||||||
|
setMap(result.map);
|
||||||
|
setOms(result.oms);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn("Map initialization error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!map && !cancelled) {
|
||||||
|
// If not ready, just retry after a short delay, do not call initializeMap at all
|
||||||
|
setTimeout(() => tryInit(false), 50);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [mapRef, map, hasRights, setPolylineEventsDisabled]);
|
tryInit(true);
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [mapRef, map, hasRights, setPolylineEventsDisabled, mapOptions]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useInitializeMap;
|
export default useInitializeMap;
|
||||||
|
|||||||
163
components/uiWidgets/baseMapPanel/BaseMapPanel.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
// components/uiWidgets/baseMapPanel/BaseMapPanel.js
|
||||||
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import L from "leaflet";
|
||||||
|
import { Icon } from "@iconify/react";
|
||||||
|
|
||||||
|
// Minimal, safe defaults (no API key required). You can extend via config later.
|
||||||
|
const DEFAULT_BASE_LAYERS = [
|
||||||
|
{
|
||||||
|
id: "osm-standard",
|
||||||
|
name: "Standard",
|
||||||
|
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||||
|
attribution: "© OpenStreetMap contributors",
|
||||||
|
minZoom: 0,
|
||||||
|
maxZoom: 19,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "osm-humanitarian",
|
||||||
|
name: "Humanitarian",
|
||||||
|
url: "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",
|
||||||
|
attribution: "© OpenStreetMap contributors, Humanitarian OpenStreetMap Team",
|
||||||
|
minZoom: 0,
|
||||||
|
maxZoom: 19,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "cyclosm",
|
||||||
|
name: "CyclOSM",
|
||||||
|
url: "https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png",
|
||||||
|
attribution: "© OpenStreetMap contributors, CyclOSM",
|
||||||
|
minZoom: 0,
|
||||||
|
maxZoom: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "carto-light",
|
||||||
|
name: "Carto Light",
|
||||||
|
url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
|
||||||
|
attribution: "© OpenStreetMap contributors, © CARTO",
|
||||||
|
subdomains: "abcd",
|
||||||
|
minZoom: 0,
|
||||||
|
maxZoom: 20,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function getCurrentTileLayer(map) {
|
||||||
|
let found = null;
|
||||||
|
if (!map) return null;
|
||||||
|
map.eachLayer(layer => {
|
||||||
|
if (!found && layer instanceof L.TileLayer) {
|
||||||
|
found = layer;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BaseMapPanel({ map, onSelect, onClose, initialId }) {
|
||||||
|
const [activeId, setActiveId] = useState(initialId || null);
|
||||||
|
const layerCacheRef = useRef({});
|
||||||
|
const bases = useMemo(() => {
|
||||||
|
try {
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
const cfg = window.__leafletConfig;
|
||||||
|
if (cfg && cfg.tileSources) {
|
||||||
|
return Object.entries(cfg.tileSources).map(([key, ts]) => ({
|
||||||
|
id: key,
|
||||||
|
name: ts.name || key,
|
||||||
|
url: ts.url,
|
||||||
|
attribution: ts.attribution || "© OpenStreetMap contributors",
|
||||||
|
minZoom: ts.minZoom ?? cfg.minZoom ?? 0,
|
||||||
|
maxZoom: ts.maxZoom ?? cfg.maxZoom ?? 19,
|
||||||
|
subdomains: ts.subdomains,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
return DEFAULT_BASE_LAYERS;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const applyBase = id => {
|
||||||
|
if (!map) return;
|
||||||
|
const base = bases.find(b => b.id === id) || bases[0];
|
||||||
|
if (!base) return;
|
||||||
|
|
||||||
|
// Remove current tile layer
|
||||||
|
const current = getCurrentTileLayer(map);
|
||||||
|
if (current) {
|
||||||
|
try {
|
||||||
|
map.removeLayer(current);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get or create the new layer
|
||||||
|
let nextLayer = layerCacheRef.current[id];
|
||||||
|
if (!nextLayer) {
|
||||||
|
nextLayer = L.tileLayer(base.url, {
|
||||||
|
attribution: base.attribution,
|
||||||
|
subdomains: base.subdomains || "abc",
|
||||||
|
tileSize: 256,
|
||||||
|
minZoom: base.minZoom ?? 0,
|
||||||
|
maxZoom: base.maxZoom ?? 19,
|
||||||
|
noWrap: true,
|
||||||
|
// Ensure base tiles stay behind overlays
|
||||||
|
zIndex: 1,
|
||||||
|
});
|
||||||
|
layerCacheRef.current[id] = nextLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextLayer.addTo(map);
|
||||||
|
try {
|
||||||
|
if (typeof map.setMinZoom === "function") map.setMinZoom(base.minZoom ?? 0);
|
||||||
|
if (typeof map.setMaxZoom === "function") map.setMaxZoom(base.maxZoom ?? 19);
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
window.__tileSourceMinZoom = base.minZoom ?? 0;
|
||||||
|
window.__tileSourceMaxZoom = base.maxZoom ?? 19;
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
setActiveId(id);
|
||||||
|
try {
|
||||||
|
localStorage.setItem("baseMapId", id);
|
||||||
|
} catch (_) {}
|
||||||
|
onSelect && onSelect(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const saved = (() => {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem("baseMapId");
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
const targetId = initialId || saved || bases[0]?.id;
|
||||||
|
if (targetId) {
|
||||||
|
applyBase(targetId);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [map]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="absolute top-16 right-3 z-50 w-64 bg-white rounded-lg shadow-lg p-3">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<h3 className="text-sm font-semibold">Map Layers</h3>
|
||||||
|
<button onClick={onClose} aria-label="Schließen" title="Schließen">
|
||||||
|
<Icon icon="material-symbols:close-rounded" className="h-5 w-5 text-gray-700" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{bases.map(b => (
|
||||||
|
<button
|
||||||
|
key={b.id}
|
||||||
|
onClick={() => applyBase(b.id)}
|
||||||
|
className={`text-left rounded-md border p-2 hover:bg-gray-50 ${
|
||||||
|
activeId === b.id ? "ring-2 ring-blue-500" : ""
|
||||||
|
}`}
|
||||||
|
title={b.name}
|
||||||
|
>
|
||||||
|
<div className="font-medium text-sm">{b.name}</div>
|
||||||
|
<div className="text-[10px] text-gray-500 truncate">{b.url}</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// /components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js
|
// /components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js
|
||||||
|
import { getDebugLog } from "../../../utils/configUtils";
|
||||||
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 "@/components/uiWidgets/mapLayersControlPanel/EditModeToggle";
|
import EditModeToggle from "@/components/uiWidgets/mapLayersControlPanel/EditModeToggle";
|
||||||
@@ -13,8 +14,10 @@ import { selectMapLayersState, setLayerVisibility } from "@/redux/slices/mapLaye
|
|||||||
import { setVisible } from "@/redux/slices/database/pois/poiLayerVisibleSlice";
|
import { setVisible } from "@/redux/slices/database/pois/poiLayerVisibleSlice";
|
||||||
import { incrementZoomTrigger } from "@/redux/slices/zoomTriggerSlice";
|
import { incrementZoomTrigger } from "@/redux/slices/zoomTriggerSlice";
|
||||||
|
|
||||||
function MapLayersControlPanel() {
|
function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
|
||||||
const [editMode, setEditMode] = useState(false); // Zustand für editMode
|
const [editMode, setEditMode] = useState(false); // Zustand für editMode
|
||||||
|
const [localStorageLoaded, setLocalStorageLoaded] = useState(false); // Tracking ob localStorage geladen wurde
|
||||||
|
const kabelstreckenVisible = useSelector(selectPolylineVisible); // Nur noch Redux
|
||||||
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();
|
||||||
@@ -24,46 +27,60 @@ function MapLayersControlPanel() {
|
|||||||
const GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict) || [];
|
const GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict) || [];
|
||||||
const GisSystemStatic = useSelector(selectGisSystemStatic) || [];
|
const GisSystemStatic = useSelector(selectGisSystemStatic) || [];
|
||||||
|
|
||||||
const polylineVisible = useSelector(selectPolylineVisible);
|
// Debug: Kabelstrecken state verfolgen
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("🎯 kabelstreckenVisible state changed to:", kabelstreckenVisible);
|
||||||
|
}, [kabelstreckenVisible]);
|
||||||
|
|
||||||
const handlePolylineCheckboxChange = event => {
|
// Prüfen, ob TALAS (IdSystem 1) erlaubt & sichtbar auf Karte (Allow + Map)
|
||||||
const checked = event.target.checked;
|
const isTalasAllowed = Array.isArray(GisSystemStatic)
|
||||||
dispatch(setPolylineVisible(checked));
|
? GisSystemStatic.some(
|
||||||
localStorage.setItem("polylineVisible", checked);
|
system => system.IdSystem === 1 && system.Allow === 1 && system.Map === 1
|
||||||
|
)
|
||||||
|
: false;
|
||||||
|
|
||||||
if (checked) {
|
// handlePolylineCheckboxChange kommt jetzt als Prop von MapComponent
|
||||||
dispatch(setLayerVisibility({ layer: "TALAS", visibility: true }));
|
|
||||||
localStorage.setItem(
|
|
||||||
"mapLayersVisibility",
|
|
||||||
JSON.stringify({ ...mapLayersVisibility, TALAS: true })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// LocalStorage Werte laden
|
// LocalStorage Werte beim ersten Laden der Komponente wiederherstellen (nur für POI und mapLayersVisibility, nicht mehr für Kabelstrecken)
|
||||||
const storedPoiVisible = localStorage.getItem("poiVisible");
|
const storedPoiVisible = localStorage.getItem("poiVisible");
|
||||||
if (storedPoiVisible !== null) {
|
if (storedPoiVisible !== null) {
|
||||||
setPoiVisible(storedPoiVisible === "true");
|
setPoiVisible(storedPoiVisible === "true");
|
||||||
}
|
}
|
||||||
const storedPolylineVisible = localStorage.getItem("polylineVisible");
|
|
||||||
if (storedPolylineVisible !== null) {
|
|
||||||
dispatch(setPolylineVisible(storedPolylineVisible === "true"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Layer-Sichtbarkeiten aus localStorage laden
|
// Kartenspezifischer localStorage-Key verwenden
|
||||||
const storedMapLayersVisibility = localStorage.getItem("mapLayersVisibility");
|
const mapId = localStorage.getItem("currentMapId");
|
||||||
|
const userId = localStorage.getItem("currentUserId");
|
||||||
|
const mapStorageKey =
|
||||||
|
mapId && userId ? `mapLayersVisibility_m${mapId}_u${userId}` : "mapLayersVisibility";
|
||||||
|
|
||||||
|
const storedMapLayersVisibility = localStorage.getItem(mapStorageKey);
|
||||||
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] }));
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// Initialisiere mapLayersVisibility basierend auf Allow & Map (nur Systeme mit Map===1 anzeigen)
|
||||||
|
if (Array.isArray(GisSystemStatic)) {
|
||||||
|
const initialVisibility = {};
|
||||||
|
GisSystemStatic.forEach(system => {
|
||||||
|
const systemKey = `system-${system.IdSystem}`;
|
||||||
|
const visibility = system.Allow === 1 && system.Map === 1;
|
||||||
|
if (visibility) {
|
||||||
|
initialVisibility[systemKey] = visibility;
|
||||||
|
dispatch(setLayerVisibility({ layer: systemKey, visibility }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
localStorage.setItem(mapStorageKey, JSON.stringify(initialVisibility));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditMode lesen
|
|
||||||
const storedEditMode = localStorage.getItem("editMode");
|
const storedEditMode = localStorage.getItem("editMode");
|
||||||
setEditMode(storedEditMode === "true");
|
setEditMode(storedEditMode === "true");
|
||||||
}, [setPoiVisible, dispatch]); // ✅ `setMapLayersVisibility` entfernt
|
|
||||||
|
setLocalStorageLoaded(true);
|
||||||
|
}, []); // Läuft nur beim Mount
|
||||||
|
|
||||||
const handleAreaChange = event => {
|
const handleAreaChange = event => {
|
||||||
const selectedIndex = event.target.options.selectedIndex;
|
const selectedIndex = event.target.options.selectedIndex;
|
||||||
@@ -72,8 +89,13 @@ function MapLayersControlPanel() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Allowed systems jetzt nach Allow && Map filtern
|
||||||
const allowedSystems = Array.isArray(GisSystemStatic)
|
const allowedSystems = Array.isArray(GisSystemStatic)
|
||||||
? new Set(GisSystemStatic.filter(system => system.Allow === 1).map(system => system.IdSystem))
|
? new Set(
|
||||||
|
GisSystemStatic.filter(system => system.Allow === 1 && system.Map === 1).map(
|
||||||
|
system => system.IdSystem
|
||||||
|
)
|
||||||
|
)
|
||||||
: new Set();
|
: new Set();
|
||||||
|
|
||||||
const seenNames = new Set();
|
const seenNames = new Set();
|
||||||
@@ -97,7 +119,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 && item.Map === 1; // <— Map Bedingung hinzugefügt
|
||||||
if (isUnique) {
|
if (isUnique) {
|
||||||
seenSystemNames.add(item.Name);
|
seenSystemNames.add(item.Name);
|
||||||
}
|
}
|
||||||
@@ -108,8 +130,8 @@ function MapLayersControlPanel() {
|
|||||||
setSystemListing(
|
setSystemListing(
|
||||||
filteredSystems.map((system, index) => ({
|
filteredSystems.map((system, index) => ({
|
||||||
id: index + 1,
|
id: index + 1,
|
||||||
name: system.Name, // Verwende den Originalnamen für die Anzeige
|
name: system.Name, // Anzeige
|
||||||
key: `system-${system.IdSystem}`, // Internen Schlüssel für die MapLayersVisibility-Logik
|
key: `system-${system.IdSystem}`, // interner Schlüssel
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}, [GisStationsStaticDistrict, GisSystemStatic]);
|
}, [GisStationsStaticDistrict, GisSystemStatic]);
|
||||||
@@ -117,13 +139,38 @@ function MapLayersControlPanel() {
|
|||||||
const handleCheckboxChange = (key, event) => {
|
const handleCheckboxChange = (key, event) => {
|
||||||
if (editMode) return;
|
if (editMode) return;
|
||||||
const { checked } = event.target;
|
const { checked } = event.target;
|
||||||
|
// Debug-Ausgabe
|
||||||
dispatch(setLayerVisibility({ layer: key, visibility: checked }));
|
const params = new URLSearchParams(window.location.search);
|
||||||
localStorage.setItem(
|
const mapId = params.get("m");
|
||||||
"mapLayersVisibility",
|
const userId = params.get("u");
|
||||||
JSON.stringify({ ...mapLayersVisibility, [key]: checked })
|
const polylineKey =
|
||||||
|
mapId && userId ? `polylineVisible_m${mapId}_u${userId}` : "polylineVisible";
|
||||||
|
console.log(
|
||||||
|
"[UI/handleCheckboxChange] key:",
|
||||||
|
key,
|
||||||
|
"checked:",
|
||||||
|
checked,
|
||||||
|
"Redux:",
|
||||||
|
kabelstreckenVisible,
|
||||||
|
"localStorage:",
|
||||||
|
localStorage.getItem(polylineKey)
|
||||||
);
|
);
|
||||||
|
dispatch(setLayerVisibility({ layer: key, visibility: checked }));
|
||||||
|
const mapId2 = localStorage.getItem("currentMapId");
|
||||||
|
const userId2 = localStorage.getItem("currentUserId");
|
||||||
|
const mapStorageKey =
|
||||||
|
mapId2 && userId2 ? `mapLayersVisibility_m${mapId2}_u${userId2}` : "mapLayersVisibility";
|
||||||
|
localStorage.setItem(mapStorageKey, JSON.stringify({ ...mapLayersVisibility, [key]: checked }));
|
||||||
|
// Wenn TALAS (system-1) deaktiviert wird, Kabelstrecken deaktivieren
|
||||||
|
if (key === "system-1" && !checked) {
|
||||||
|
localStorage.setItem("kabelstreckenVisible", "false");
|
||||||
|
localStorage.setItem("polylineVisible", "false");
|
||||||
|
dispatch(setPolylineVisible(false));
|
||||||
|
setTimeout(() => {
|
||||||
|
const polylineEvent = new Event("polylineVisibilityChanged");
|
||||||
|
window.dispatchEvent(polylineEvent);
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const event = new Event("visibilityChanged");
|
const event = new Event("visibilityChanged");
|
||||||
window.dispatchEvent(event);
|
window.dispatchEvent(event);
|
||||||
@@ -133,7 +180,7 @@ function MapLayersControlPanel() {
|
|||||||
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.toString()); // Store POI visibility in localStorage
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleIconClick = () => {
|
const handleIconClick = () => {
|
||||||
@@ -143,7 +190,7 @@ function MapLayersControlPanel() {
|
|||||||
|
|
||||||
//------------------------------
|
//------------------------------
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
window.__debug = window.__debug || {};
|
window.__debug = window.__debug || {};
|
||||||
window.__debug.gisStations = GisStationsStaticDistrict;
|
window.__debug.gisStations = GisStationsStaticDistrict;
|
||||||
}
|
}
|
||||||
@@ -175,7 +222,7 @@ function MapLayersControlPanel() {
|
|||||||
}
|
}
|
||||||
return isUnique;
|
return isUnique;
|
||||||
});
|
});
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("📌 stationListing aktualisiert:", filteredAreas);
|
console.log("📌 stationListing aktualisiert:", filteredAreas);
|
||||||
}
|
}
|
||||||
}, [GisStationsStaticDistrict, GisSystemStatic]);
|
}, [GisStationsStaticDistrict, GisSystemStatic]);
|
||||||
@@ -209,18 +256,26 @@ function MapLayersControlPanel() {
|
|||||||
style={{ minWidth: "150px", maxWidth: "200px" }}
|
style={{ minWidth: "150px", maxWidth: "200px" }}
|
||||||
>
|
>
|
||||||
<option value="Station wählen">Station wählen</option>
|
<option value="Station wählen">Station wählen</option>
|
||||||
|
{/*
|
||||||
|
...new Map(
|
||||||
|
(GisStationsStaticDistrict.Points || [])
|
||||||
|
.filter(p => !!p.Area_Name)
|
||||||
|
.map(p => [p.Area_Name, p])
|
||||||
|
).values(),
|
||||||
|
*/}
|
||||||
{[
|
{[
|
||||||
...new Map(
|
...new Map(
|
||||||
(GisStationsStaticDistrict.Points || [])
|
(GisStationsStaticDistrict.Points || [])
|
||||||
.filter(p => !!p.Area_Name)
|
.filter(p => !!p.Area_Name)
|
||||||
.map(p => [p.Area_Name, p])
|
.map(p => [p.Area_Name, p])
|
||||||
).values(),
|
).values(),
|
||||||
].map((item, index) => (
|
].map(item => (
|
||||||
<option key={item.Area_Name} value={item.IdLD}>
|
<option key={item.Area_Name} value={item.IdLD}>
|
||||||
{item.Area_Name}
|
{item.Area_Name}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
{/*
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<EditModeToggle />
|
<EditModeToggle />
|
||||||
<img
|
<img
|
||||||
@@ -230,6 +285,7 @@ function MapLayersControlPanel() {
|
|||||||
onClick={handleIconClick}
|
onClick={handleIconClick}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
*/}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Checkboxen mit Untermenüs */}
|
{/* Checkboxen mit Untermenüs */}
|
||||||
@@ -255,9 +311,10 @@ function MapLayersControlPanel() {
|
|||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={polylineVisible} // Zustand für Kabelstrecken
|
checked={kabelstreckenVisible}
|
||||||
onChange={handlePolylineCheckboxChange}
|
onChange={e => handlePolylineCheckboxChange(e.target.checked)}
|
||||||
id="polyline-checkbox"
|
id="polyline-checkbox"
|
||||||
|
disabled={!isTalasAllowed || editMode}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="polyline-checkbox" className="text-sm ml-2">
|
<label htmlFor="polyline-checkbox" className="text-sm ml-2">
|
||||||
Kabelstrecken
|
Kabelstrecken
|
||||||
@@ -279,25 +336,6 @@ function MapLayersControlPanel() {
|
|||||||
POIs
|
POIs
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Areas
|
|
||||||
<div className="flex items-center">
|
|
||||||
<input type="checkbox" checked={areaVisible} onChange={handleAreaCheckboxChange} id="area-checkbox" />
|
|
||||||
<label htmlFor="area-checkbox" className="text-sm ml-2">
|
|
||||||
Bereiche
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
*/}
|
|
||||||
|
|
||||||
{/* Standorte
|
|
||||||
<div className="flex items-center">
|
|
||||||
<input type="checkbox" checked={standordVisible} onChange={handleStandorteCheckboxChange} id="area-checkbox" />
|
|
||||||
<label htmlFor="area-checkbox" className="text-sm ml-2">
|
|
||||||
Standorte
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
*/}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
// config/paths.js
|
// config/paths.js
|
||||||
const basePathRaw = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
let __configCache;
|
||||||
const BASE_PATH = basePathRaw.replace(/^\/|\/$/g, "");
|
export async function getBaseUrl() {
|
||||||
export const BASE_URL = BASE_PATH ? `/${BASE_PATH}` : "";
|
if (__configCache) return __configCache;
|
||||||
|
const res = await fetch("/config.json");
|
||||||
|
if (!res.ok) throw new Error("config.json konnte nicht geladen werden");
|
||||||
|
const config = await res.json();
|
||||||
|
const basePath = (config.basePath || "").replace(/^\/|\/$/g, "");
|
||||||
|
__configCache = basePath ? `/${basePath}` : "";
|
||||||
|
return __configCache;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
const { defineConfig } = require("cypress");
|
|
||||||
|
|
||||||
module.exports = defineConfig({
|
|
||||||
e2e: {
|
|
||||||
setupNodeEvents(on, config) {
|
|
||||||
// Node-Event-Listeners hier konfigurieren
|
|
||||||
},
|
|
||||||
experimentalStudio: true, // Studio aktivieren
|
|
||||||
},
|
|
||||||
|
|
||||||
component: {
|
|
||||||
devServer: {
|
|
||||||
framework: "next",
|
|
||||||
bundler: "webpack",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
describe("contextmenuTest", () => {
|
|
||||||
it("contetmenu Station öffnen (Tab)", () => {
|
|
||||||
cy.log("Test startet jetzt");
|
|
||||||
|
|
||||||
// 1. Viewport einstellen
|
|
||||||
cy.viewport(1920, 1080);
|
|
||||||
cy.log("Viewport eingestellt auf 1920x1080");
|
|
||||||
|
|
||||||
// 2. Seite besuchen
|
|
||||||
cy.visit("http://10.10.0.13:3000/?m=12&u=484");
|
|
||||||
cy.wait(5000); // Wartezeit nach dem Laden
|
|
||||||
cy.log("Seite geöffnet");
|
|
||||||
|
|
||||||
// 3. Sicherstellen, dass die Karte geladen ist
|
|
||||||
cy.get("#map", { timeout: 15000 }).should("be.visible");
|
|
||||||
cy.log("Karte geladen");
|
|
||||||
|
|
||||||
// 4. Wartezeit zum Stabilisieren der Karte
|
|
||||||
cy.wait(2000);
|
|
||||||
|
|
||||||
// 5. Marker suchen und Rechtsklick simulieren
|
|
||||||
cy.get('img[src*="img/icons/marker-icon-20.svg"]') // Marker suchen
|
|
||||||
.filter(":visible") // Nur sichtbare Marker
|
|
||||||
.first() // Ersten Marker auswählen
|
|
||||||
.scrollIntoView() // Marker in den sichtbaren Area scrollen
|
|
||||||
.should("be.visible") // Sicherstellen, dass Marker sichtbar ist
|
|
||||||
.trigger("mouseover") // Mouseover simulieren
|
|
||||||
.wait(500) // Wartezeit nach Mouseover
|
|
||||||
.rightclick({ force: true }); // Rechtsklick auf den Marker
|
|
||||||
cy.log("Rechtsklick auf Marker ausgeführt");
|
|
||||||
|
|
||||||
// Screenshot nach Rechtsklick zum Debugging
|
|
||||||
// cy.screenshot("nach-rechtsklick");
|
|
||||||
|
|
||||||
// 6. Kontextmenü prüfen mit explizitem Selektor
|
|
||||||
cy.get(".leaflet-contextmenu-item") // Suche alle Menüeinträge mit der Klasse
|
|
||||||
.contains("Station öffnen (Tab)", { timeout: 5000 }) // Prüfe Text innerhalb des Eintrags
|
|
||||||
.should("be.visible"); // Sichtbarkeit sicherstellen
|
|
||||||
cy.log("Menüeintrag gefunden");
|
|
||||||
|
|
||||||
// 7. URL abfangen und testen, bevor der Tab geöffnet wird
|
|
||||||
const targetUrl = "http://10.10.0.13/talas5/devices/cpl.aspx?ver=35&kue=24&id=50922";
|
|
||||||
|
|
||||||
// HTTP-Anfrage zur Überprüfung des Status
|
|
||||||
cy.request(targetUrl).then((response) => {
|
|
||||||
expect(response.status).to.eq(200); // Erwartet HTTP 200 OK
|
|
||||||
cy.log("URL ist erreichbar, Status 200");
|
|
||||||
});
|
|
||||||
|
|
||||||
// 8. Menüeintrag auswählen (öffnet den neuen Tab)
|
|
||||||
cy.get(".leaflet-contextmenu-item") // Explizit Menüeintrag mit Klasse auswählen
|
|
||||||
.contains("Station öffnen (Tab)")
|
|
||||||
.click(); // Menüeintrag anklicken
|
|
||||||
cy.log("Menüeintrag ausgewählt");
|
|
||||||
|
|
||||||
// 9. Klick auf die Karte, um Kontextmenü zu schließen
|
|
||||||
cy.get("#map").click(100, 100); // Klick auf eine leere Stelle
|
|
||||||
cy.log("Kontextmenü geschlossen");
|
|
||||||
|
|
||||||
// 10. Optionaler Screenshot nach Abschluss
|
|
||||||
// cy.screenshot("test-abgeschlossen");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
describe("GMA Markers Layer", () => {
|
|
||||||
before(() => {});
|
|
||||||
|
|
||||||
it("Der Test stellt sicher, dass das GMA Tooltip-Element für 'Rastede' angezeigt ist und die erwarteten Werte wie LT:, FBT:, GT: und RLF: enthält.", () => {
|
|
||||||
// Testbeschreibung: Dieser Test überprüft, ob der Tooltip selbst korrekt dargestellt wird und den erwarteten Inhalt anzeigt.
|
|
||||||
|
|
||||||
// Besuche die Map-Seite
|
|
||||||
//cy.visit("http://10.10.0.13:3000/?m=12&u=484"); // Passe die URL an
|
|
||||||
cy.visit("http://127.0.0.1:3000/?m=12&u=484");
|
|
||||||
|
|
||||||
cy.contains(".leaflet-tooltip", "Rastede")
|
|
||||||
// Wählt das Tooltip-Element mit der Klasse `leaflet-tooltip`, das den Text "Rastede" enthält.
|
|
||||||
.first();
|
|
||||||
|
|
||||||
cy.get(".leaflet-tooltip")
|
|
||||||
// Wählt das Tooltip-Element erneut aus, um weitere Überprüfungen durchzuführen.
|
|
||||||
.should("be.visible")
|
|
||||||
// Überprüft, ob das Tooltip sichtbar ist.
|
|
||||||
.and("contain", "LT:")
|
|
||||||
// Stellt sicher, dass der Tooltip den Text "LT :" enthält.
|
|
||||||
.and("contain", "FBT:")
|
|
||||||
// Stellt sicher, dass der Tooltip auch den Text "FBT:" enthält.
|
|
||||||
.and("contain", "GT:")
|
|
||||||
// Stellt sicher, dass der Tooltip auch den Text "GT:" enthält.
|
|
||||||
.and("contain", "RLF:");
|
|
||||||
// Stellt sicher, dass der Tooltip auch den Text "RLF:" enthält.
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should open context menu on right-click on tooltip", () => {
|
|
||||||
// Testbeschreibung: Dieser Test überprüft, ob ein Rechtsklick auf den Tooltip das Kontextmenü öffnet.
|
|
||||||
|
|
||||||
// Besuche die Map-Seite
|
|
||||||
cy.visit("http://127.0.0.1:3000/?m=12&u=484"); // Passe die URL an
|
|
||||||
//warte 2 Sekunden
|
|
||||||
cy.wait(2000);
|
|
||||||
|
|
||||||
cy.contains(".leaflet-tooltip", "Rastede")
|
|
||||||
// Wählt das Tooltip-Element mit der Klasse `leaflet-tooltip`, das den Text "Rastede" enthält.
|
|
||||||
.first()
|
|
||||||
.should("be.visible") // Überprüft, ob das Tooltip sichtbar ist.
|
|
||||||
.trigger("contextmenu"); // Führt einen Rechtsklick (Kontextmenü-Ereignis) auf das Tooltip aus.
|
|
||||||
|
|
||||||
cy.get(".custom-context-menu")
|
|
||||||
// Wählt das Element aus, das das Kontextmenü darstellt.
|
|
||||||
.should("be.visible") // Überprüft, ob das Kontextmenü sichtbar ist.
|
|
||||||
.and("contain", "Station öffnen (Tab)") // Überprüft, ob der Eintrag "Station öffnen (Tab)" vorhanden ist.
|
|
||||||
.and("contain", "Koordinaten anzeigen") // Überprüft, ob der Eintrag "Koordinaten anzeigen" vorhanden ist.
|
|
||||||
.and("contain", "Reinzoomen") // Überprüft, ob der Eintrag "Reinzoomen" vorhanden ist.
|
|
||||||
.and("contain", "Rauszoomen") // Überprüft, ob der Eintrag "Rauszoomen" vorhanden ist.
|
|
||||||
.and("contain", "Hier zentrieren"); // Überprüft, ob der Eintrag "Hier zentrieren" vorhanden ist.
|
|
||||||
});
|
|
||||||
|
|
||||||
//-----------------------------------------------
|
|
||||||
});
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
//TDD Test Driven Development
|
|
||||||
// Dieser Test überprüft die Karteninteraktion: Eingabe von Koordinaten und Zentrieren der Karte
|
|
||||||
// Schritte:
|
|
||||||
// 1. Öffnen der Karte auf einer bestimmten Seite.
|
|
||||||
// 2. Eingeben von Koordinaten in ein Eingabefeld.
|
|
||||||
// 3. Klicken auf einen Button, um die Karte zu den Koordinaten zu zoomen.
|
|
||||||
// 4. Überprüfen, ob die Karte korrekt zentriert wurde.
|
|
||||||
//--------------------------------------------------------------Test4 git config --global user.email "ismailali1553@gmail.com" in Terminal eingegeben und
|
|
||||||
// benutzer email adresse eingegeben git config --global user.name "ismailali1553"
|
|
||||||
describe("Karteninteraktion", () => {
|
|
||||||
it("zoomt zu den eingegebenen Koordinaten", () => {
|
|
||||||
// Öffne die Seite mit der Karte
|
|
||||||
cy.visit("http://127.0.0.1:3000/?m=12&u=484"); // Passe den Pfad an deine Karte an
|
|
||||||
|
|
||||||
// Gebe Koordinaten in das Eingabefeld ein
|
|
||||||
cy.get('input[placeholder="Koordinaten eingeben (lat,lng)"]').type("52.52,13.405");
|
|
||||||
|
|
||||||
// Klicke auf den Button "Zu Marker zoomen"
|
|
||||||
cy.get("button").contains("Zu Marker zoomen").click();
|
|
||||||
|
|
||||||
// Überprüfe, ob die Karte die eingegebenen Koordinaten korrekt zentriert hat
|
|
||||||
cy.window().then((win) => {
|
|
||||||
const map = win.map; // Zugriff auf die Leaflet-Instanz
|
|
||||||
const center = map.getCenter(); // Aktuelles Zentrum der Karte abrufen
|
|
||||||
expect(center.lat).to.be.closeTo(52.52, 0.01); // Latitude überprüfen
|
|
||||||
expect(center.lng).to.be.closeTo(13.405, 0.01); // Longitude überprüfen
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
//cypress/e2e/poiUpdateModal.cy.js
|
|
||||||
describe("POI bearbeiten – Typ-Auswahl prüfen", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.visit("http://localhost:3000/?m=12&u=484");
|
|
||||||
cy.get(".leaflet-container", { timeout: 10000 }).should("be.visible");
|
|
||||||
cy.get(".leaflet-marker-icon", { timeout: 10000 }).should("have.length.greaterThan", 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sollte beim Öffnen des Modals den richtigen POI-Typ anzeigen", () => {
|
|
||||||
cy.get('svg[aria-label="Bearbeitungsmodus aktivieren"]').click({ force: true });
|
|
||||||
cy.wait(5000);
|
|
||||||
|
|
||||||
cy.get('img[src="/img/icons/pois/poi-marker-icon-4.png"]', { timeout: 10000 }).should("be.visible");
|
|
||||||
cy.get('img[src="/img/icons/pois/poi-marker-icon-4.png"]').first().rightclick({ force: true });
|
|
||||||
|
|
||||||
cy.contains("POI Bearbeiten").click({ force: true });
|
|
||||||
|
|
||||||
cy.get("#idPoiTyp", { timeout: 10000 }).should("exist").find("[class*='singleValue']").should("not.contain.text", "Typ auswählen");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
describe("TK-Komponenten", () => {
|
|
||||||
before(() => {
|
|
||||||
// Lade die Seite nur einmal vor allen Tests
|
|
||||||
cy.visit("http://10.10.0.13:3000/?m=12&u=484");
|
|
||||||
//cy.wait(5000); // Wartezeit, bis die Seite vollständig geladen ist, cypress macht automatisch , alsobrauchen wir im moment kein wait() wenn cy. schafft
|
|
||||||
});
|
|
||||||
|
|
||||||
it("soll alle Tests in Reihenfolge ausführen", () => {
|
|
||||||
// Test 1: Sicherstellen, dass die Checkbox vorhanden und sichtbar ist
|
|
||||||
cy.get("input[type='checkbox'][id='system-10']")
|
|
||||||
.should("exist")
|
|
||||||
.and("be.visible")
|
|
||||||
.then(() => {
|
|
||||||
cy.log("Die Checkbox mit ID 'system-10' ist vorhanden und sichtbar.");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 2: Sicherstellen, dass die Checkbox aktiviert ist
|
|
||||||
cy.get("input[type='checkbox'][id='system-10']").then(($checkbox) => {
|
|
||||||
if (!$checkbox.prop("checked")) {
|
|
||||||
// Falls die Checkbox nicht aktiviert ist, aktiviere sie
|
|
||||||
cy.wrap($checkbox).check({ force: true });
|
|
||||||
cy.log("Die Checkbox war deaktiviert und wurde jetzt aktiviert.");
|
|
||||||
} else {
|
|
||||||
cy.log("Die Checkbox ist bereits aktiviert.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test 3: Checkbox deaktivieren und Marker verschwinden lassen
|
|
||||||
cy.get("input[type='checkbox'][id='system-10']")
|
|
||||||
.uncheck({ force: true })
|
|
||||||
.then(() => {
|
|
||||||
cy.log("Die Checkbox wurde deaktiviert.");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Using fixtures to represent data",
|
|
||||||
"email": "hello@cypress.io",
|
|
||||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
describe("Map Initial Load Test", () => {
|
|
||||||
it("should load the map with the correct center and zoom", () => {
|
|
||||||
// Besuche die Seite, auf der die Karte angezeigt wird
|
|
||||||
cy.visit("http://192.168.10.167:3000/?m=12&u=485");
|
|
||||||
|
|
||||||
// Überprüfe, ob das Kartenelement existiert
|
|
||||||
cy.get("#map").should("be.visible");
|
|
||||||
|
|
||||||
// Überprüfe, ob die Karte das korrekte Zentrum und den korrekten Zoom hat
|
|
||||||
cy.window().then((win) => {
|
|
||||||
const map = win.L.map;
|
|
||||||
const center = map.getCenter();
|
|
||||||
const zoom = map.getZoom();
|
|
||||||
|
|
||||||
expect(center.lat).to.be.closeTo(53.111111, 0.0001);
|
|
||||||
expect(center.lng).to.be.closeTo(8.4625, 0.0001);
|
|
||||||
expect(zoom).to.eq(12);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 618 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 587 KiB |
@@ -1,25 +0,0 @@
|
|||||||
// ***********************************************
|
|
||||||
// This example commands.js shows you how to
|
|
||||||
// create various custom commands and overwrite
|
|
||||||
// existing commands.
|
|
||||||
//
|
|
||||||
// For more comprehensive examples of custom
|
|
||||||
// commands please read more here:
|
|
||||||
// https://on.cypress.io/custom-commands
|
|
||||||
// ***********************************************
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a parent command --
|
|
||||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a child command --
|
|
||||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a dual command --
|
|
||||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This will overwrite an existing command --
|
|
||||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
||||||
<title>Components App</title>
|
|
||||||
<!-- Used by Next.js to inject CSS. -->
|
|
||||||
<div id="__next_css__DO_NOT_USE__"></div>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div data-cy-root></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// ***********************************************************
|
|
||||||
// This example support/component.js is processed and
|
|
||||||
// loaded automatically before your test files.
|
|
||||||
//
|
|
||||||
// This is a great place to put global configuration and
|
|
||||||
// behavior that modifies Cypress.
|
|
||||||
//
|
|
||||||
// You can change the location of this file or turn off
|
|
||||||
// automatically serving support files with the
|
|
||||||
// 'supportFile' configuration option.
|
|
||||||
//
|
|
||||||
// You can read more here:
|
|
||||||
// https://on.cypress.io/configuration
|
|
||||||
// ***********************************************************
|
|
||||||
|
|
||||||
// Import commands.js using ES2015 syntax:
|
|
||||||
import './commands'
|
|
||||||
|
|
||||||
// Alternatively you can use CommonJS syntax:
|
|
||||||
// require('./commands')
|
|
||||||
|
|
||||||
import { mount } from 'cypress/react18'
|
|
||||||
|
|
||||||
Cypress.Commands.add('mount', mount)
|
|
||||||
|
|
||||||
// Example use:
|
|
||||||
// cy.mount(<MyComponent />)
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
// ***********************************************************
|
|
||||||
// Diese Datei wird automatisch geladen, bevor Tests ausgeführt werden.
|
|
||||||
//
|
|
||||||
// Dies ist ein guter Platz für globale Konfigurationen
|
|
||||||
// und Änderungen, die Cypress beeinflussen.
|
|
||||||
//
|
|
||||||
// Weitere Infos: https://on.cypress.io/configuration
|
|
||||||
// ***********************************************************
|
|
||||||
|
|
||||||
// Importiere zusätzliche Befehle
|
|
||||||
import "./commands";
|
|
||||||
|
|
||||||
// Alternativ: CommonJS-Syntax verwenden
|
|
||||||
// require('./commands')
|
|
||||||
|
|
||||||
// Verstecke unnötige Logs für XHR- und Fetch-Anfragen
|
|
||||||
const app = window.top;
|
|
||||||
if (!app.document.head.querySelector("[data-hide-command-log-request]")) {
|
|
||||||
const style = app.document.createElement("style");
|
|
||||||
style.innerHTML = `
|
|
||||||
.command-name-request, .command-name-xhr {
|
|
||||||
display: none; /* Verstecke Fetch- und XHR-Logs */
|
|
||||||
}
|
|
||||||
.runnable-pass .collapsible-header {
|
|
||||||
display: none; /* Verstecke den Header erfolgreicher Tests */
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
style.setAttribute("data-hide-command-log-request", "");
|
|
||||||
app.document.head.appendChild(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Globale Ereignisse für Cypress konfigurieren
|
|
||||||
Cypress.on("test:after:run", (test, runnable) => {
|
|
||||||
// Minimiert die Logs für erfolgreiche Tests
|
|
||||||
if (test.state === "passed") {
|
|
||||||
runnable._testConfigBody = null; // Löscht den Test Body
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Schließe automatisch erfolgreiche Tests in der GUI
|
|
||||||
Cypress.on("log:added", (log) => {
|
|
||||||
if (log.state === "passed") {
|
|
||||||
log.displayName = ""; // Versteckt Log-Details für erfolgreiche Schritte
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Screenshot nach jedem fehlgeschlagenen Test
|
|
||||||
Cypress.on("fail", (error, runnable) => {
|
|
||||||
cy.screenshot(`error-${runnable.title}`); // Screenshot für Fehler erstellen
|
|
||||||
throw error; // Fehler weitergeben
|
|
||||||
});
|
|
||||||
|
|
||||||
// Logge die Dauer jedes Tests
|
|
||||||
Cypress.on("test:after:run", (test) => {
|
|
||||||
cy.log(`Test "${test.title}" abgeschlossen in ${test.duration} ms`);
|
|
||||||
});
|
|
||||||
@@ -6,10 +6,6 @@ DB_PASSWORD="root#$"
|
|||||||
DB_NAME=talas_v5
|
DB_NAME=talas_v5
|
||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
|
|
||||||
# Public Settings (Client braucht IP/Domain) , Variablen mit dem Präfix "NEXT_PUBLIC" ist in Browser sichtbar
|
|
||||||
NEXT_PUBLIC_DEBUG_LOG=false
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#auf dem Entwicklungsrechner dev läuft auf Port 3000 und auf dem Server prod auf Port 80, aber der WebService ist immer auf PORT 80
|
#auf dem Entwicklungsrechner dev läuft auf Port 3000 und auf dem Server prod auf Port 80, aber der WebService ist immer auf PORT 80
|
||||||
NEXT_PUBLIC_API_PORT_MODE=prod
|
NEXT_PUBLIC_API_PORT_MODE=prod
|
||||||
|
|||||||
@@ -25,6 +25,31 @@ Entwicklung, Architekturverständnis und Erweiterung.
|
|||||||
### ⚙️ Konfiguration
|
### ⚙️ Konfiguration
|
||||||
|
|
||||||
- [Allgemeine Übersicht](config/README.md)
|
- [Allgemeine Übersicht](config/README.md)
|
||||||
|
- [Kartenquellen-Konfiguration (public/config.json)](#kartenquellen-konfiguration-publicconfigjson)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Kartenquellen-Konfiguration (public/config.json)
|
||||||
|
|
||||||
|
Die Datei `public/config.json` steuert, welche Kartenquelle (z.B. OSM oder lokale Tiles) für die
|
||||||
|
Leaflet-Karte verwendet wird.
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"//info": "tileSources: 'local' für offline, 'osm' für online",
|
||||||
|
"tileSources": {
|
||||||
|
"local": "http://localhost/talas5/TileMap/mapTiles/{z}/{x}/{y}.png",
|
||||||
|
"osm": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
},
|
||||||
|
"active": "osm"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Mit `active` kann zwischen Online- und Offline-Karten umgeschaltet werden.
|
||||||
|
- Die Datei wird beim Start der App automatisch geladen.
|
||||||
|
- Für Offline-Betrieb muss das lokale Kartenmaterial vorhanden sein (siehe Installationsanleitung).
|
||||||
|
|
||||||
### 🧩 Hauptkomponenten
|
### 🧩 Hauptkomponenten
|
||||||
|
|
||||||
|
|||||||
34
docs/TODO.md
@@ -69,3 +69,37 @@ die Daten von DB auch mit WebSocket gelöst werden
|
|||||||
|
|
||||||
- [ ] Redundante Kontextmenülogik auflösen
|
- [ ] Redundante Kontextmenülogik auflösen
|
||||||
- [ ] Bessere Trennung zwischen Mock- und Live-API in Service-Funktionen
|
- [ ] Bessere Trennung zwischen Mock- und Live-API in Service-Funktionen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
28.07.2025 IdSystem 11 GMA Glätemeldeanlagen, werden neu neu laden das Browser nich mehr geladen in
|
||||||
|
DB maps idsystem ändern und testen
|
||||||
|
|
||||||
|
# 12.09.2025
|
||||||
|
|
||||||
|
Die aktuelle Ansicht ist bei kleineren Auflösungen unübersichtlich bzw. es wird zuviel von der
|
||||||
|
eigentlichen Karte verdeckt. Unquittierter Alarm, critical
|
||||||
|
|
||||||
|
Zu Marker zoomen
|
||||||
|
|
||||||
|
Station suchen
|
||||||
|
|
||||||
|
- [ ] TODO: Unquittierter Alarm, critical 🚨 Alarm
|
||||||
|
- [ ] TODO: Zu Marker zoomen: Dropdown-Menu Station auswählen und hinein zoomen bis zu ausgewählte
|
||||||
|
in einem betimmten Zoom-Stufe der Leaflet (OSM) Station mit flyto in Leaflet 📍 POI
|
||||||
|
- [ ] TODO: Station suchen: CoordinateInput.js Modal soll über einem Icon 'Suche / Lupe' oben rechts
|
||||||
|
eingeblendet und ausgeblendet um mehr von der Karte zu sehen 🔍 Suche
|
||||||
|
- [ ] TODO: Editiermodus: EditMode Stift Icon aktivieren und deaktivieren um POI Position ändern zu
|
||||||
|
können wenn der User Berechtigung hat ✏️ Edit
|
||||||
|
- [ ] TODO: Vergrössern: Maximieren Icon Button um rauszoomen zu einem bestimmten Bereich, z.B.
|
||||||
|
Deutschland Karte im Fenster sichtbar ⬜ Fenster maximieren
|
||||||
|
|
||||||
|
- [ ] TODO: Ebenen (Openstreetmap): Stack/Stapel/Ebenen Icon um den Ansicht der Karten zu ändern 🗂️
|
||||||
|
Stapel
|
||||||
|
|
||||||
|
- [ ] TODO: Menü öffenen mit Kiste der Systeme: MapLayerControlPanel.js Modal soll über einem Icon
|
||||||
|
'Hamburger menu' oben rechts eingeblendet und ausgeblendet um mehr von der Karte zu sehen ☰
|
||||||
|
Hamburger-Menü
|
||||||
|
- [ ] TODO: Info Karte: VersionInfoModal.js Modal soll über einem Icon 'Info' oben rechts
|
||||||
|
eingeblendet und ausgeblendet um mehr von der Karte zu sehen ℹ️ Info
|
||||||
|
https://www.openstreetmap.org/#map=13/51.80097/9.33495&layers=P
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ sequenceDiagram
|
|||||||
|
|
||||||
- **Konfigurierbarer basePath:**
|
- **Konfigurierbarer basePath:**
|
||||||
- **Konfigurierbarer basePath:**
|
- **Konfigurierbarer basePath:**
|
||||||
Pfad wie `/talas5` ist optional und kann per Umgebungsvariable `NEXT_PUBLIC_BASE_PATH` gesetzt
|
Pfad wie `/talas5` ist optional und wird jetzt in `public/config.json` als `basePath` gepflegt
|
||||||
werden.
|
werden.
|
||||||
Die Konfiguration erfolgt je nach Umgebung über:
|
Die Konfiguration erfolgt je nach Umgebung über:
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
# 📁 paths.js
|
# 📁 paths.js
|
||||||
|
|
||||||
Berechnet den sauberen `BASE_URL`-Pfad basierend auf `.env.production` oder
|
Berechnet den sauberen `BASE_URL`-Pfad basierend auf `.env.production` oder
|
||||||
`.env.development → NEXT_PUBLIC_BASE_PATH`.
|
`public/config.json → basePath`.
|
||||||
Entfernt führende und abschließende Slashes.
|
Entfernt führende und abschließende Slashes.
|
||||||
|
|
||||||
## Beispiel
|
## Beispiel
|
||||||
|
|
||||||
Wenn `NEXT_PUBLIC_BASE_PATH = "/talas5/"`, wird `BASE_URL = "/talas5"` gesetzt.
|
Wenn `basePath = "/talas5/"` in config.json gesetzt ist, wird `BASE_URL = "/talas5"` verwendet.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const BASE_PATH = basePathRaw.replace(/^\/|\/$/g, "");
|
const BASE_PATH = basePathRaw.replace(/^\/|\/$/g, "");
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ NodeMap verwendet Umgebungsvariablen zur Steuerung von API-Verhalten, Serverpfad
|
|||||||
|
|
||||||
## 🔧 Wichtige Variablen
|
## 🔧 Wichtige Variablen
|
||||||
|
|
||||||
| Variable | Beispielwert | Beschreibung |
|
| Variable | Beispielwert | Beschreibung |
|
||||||
| --------------------------- | ------------------- | --------------------------------------------------------------------- |
|
| --------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------- |
|
||||||
| `DB_HOST` | `localhost` | Adresse des Datenbankservers (MySQL) |
|
| `DB_HOST` | `localhost` | Adresse des Datenbankservers (MySQL) |
|
||||||
| `DB_PORT` | `3306` | Port für die Datenbankverbindung |
|
| `DB_PORT` | `3306` | Port für die Datenbankverbindung |
|
||||||
| `DB_NAME` | `talas` | Datenbankname |
|
| `DB_NAME` | `talas` | Datenbankname |
|
||||||
| `DB_USER` | `root` | Benutzername für MySQL |
|
| `DB_USER` | `root` | Benutzername für MySQL |
|
||||||
| `DB_PASSWORD` | `geheim` | Passwort für MySQL |
|
| `DB_PASSWORD` | `geheim` | Passwort für MySQL |
|
||||||
| `NEXT_PUBLIC_API_PORT_MODE` | `prod` oder `dev` | Steuert API-Routing bei Services (z. B. Portwechsel für lokal) |
|
| `NEXT_PUBLIC_API_PORT_MODE` | `prod` oder `dev` | Steuert API-Routing bei Services (z. B. Portwechsel für lokal) |
|
||||||
| `NEXT_PUBLIC_USE_MOCKS` | `true` oder `false` | Aktiviert den Mockdaten-Modus über `/api/mocks/...` |
|
| `NEXT_PUBLIC_USE_MOCKS` | `true` oder `false` | Aktiviert den Mockdaten-Modus über `/api/mocks/...` |
|
||||||
| `NEXT_PUBLIC_BASE_PATH` | `/talas5` oder leer | Optionaler Pfad, falls App unter Subpfad läuft (z. B. IIS) |
|
| `basePath` (in config.json) | `/talas5` oder leer | Optionaler Pfad, falls App unter Subpfad läuft (z. B. IIS). Wird jetzt in `public/config.json` gepflegt. |
|
||||||
| `NEXT_PUBLIC_DEBUG` | `true` oder `false` | Aktiviert zusätzliche `console.log` Ausgaben für Debugging im Browser |
|
| `NEXT_PUBLIC_DEBUG` | `true` oder `false` | Aktiviert zusätzliche `console.log` Ausgaben für Debugging im Browser |
|
||||||
|
|
||||||
## 📦 Beispiel `.env.production`
|
## 📦 Beispiel `.env.production`
|
||||||
|
|
||||||
@@ -34,7 +34,11 @@ DB_USER=root
|
|||||||
DB_PASSWORD=geheim
|
DB_PASSWORD=geheim
|
||||||
NEXT_PUBLIC_API_PORT_MODE=prod
|
NEXT_PUBLIC_API_PORT_MODE=prod
|
||||||
NEXT_PUBLIC_USE_MOCKS=false
|
NEXT_PUBLIC_USE_MOCKS=false
|
||||||
NEXT_PUBLIC_BASE_PATH=/talas5
|
// public/config.json
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"basePath": "/talas5"
|
||||||
|
}
|
||||||
NEXT_PUBLIC_DEBUG=false
|
NEXT_PUBLIC_DEBUG=false
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -48,7 +52,11 @@ DB_USER=root
|
|||||||
DB_PASSWORD=geheim
|
DB_PASSWORD=geheim
|
||||||
NEXT_PUBLIC_API_PORT_MODE=dev
|
NEXT_PUBLIC_API_PORT_MODE=dev
|
||||||
NEXT_PUBLIC_USE_MOCKS=true
|
NEXT_PUBLIC_USE_MOCKS=true
|
||||||
NEXT_PUBLIC_BASE_PATH=/talas5
|
// public/config.json
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"basePath": "/talas5"
|
||||||
|
}
|
||||||
NEXT_PUBLIC_DEBUG=true
|
NEXT_PUBLIC_DEBUG=true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import L from "leaflet";
|
import L from "leaflet";
|
||||||
|
import { getDebugLog } from "../utils/configUtils";
|
||||||
|
|
||||||
export class OverlappingMarkerSpiderfier {
|
export class OverlappingMarkerSpiderfier {
|
||||||
constructor(map, options = {}) {
|
constructor(map, options = {}) {
|
||||||
@@ -118,7 +119,7 @@ export class OverlappingMarkerSpiderfier {
|
|||||||
// 🔥 Künstliches Click-Event auslösen, um die UI zu aktualisieren
|
// 🔥 Künstliches Click-Event auslösen, um die UI zu aktualisieren
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.map.fire("click");
|
this.map.fire("click");
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("Click-Event ausgelöst in OverlappingMarkerspiderfier.js in unspiderfy ");
|
console.log("Click-Event ausgelöst in OverlappingMarkerspiderfier.js in unspiderfy ");
|
||||||
}
|
}
|
||||||
}, 10); // Kurze Verzögerung, um sicherzustellen, dass die UI neu gerendert wird
|
}, 10); // Kurze Verzögerung, um sicherzustellen, dass die UI neu gerendert wird
|
||||||
|
|||||||
48
nssm.exe Installation.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
```markdown
|
||||||
|
- Als Administrator Eingabeaufforderung oder PowerShell öffnen
|
||||||
|
|
||||||
|
- Navigiere zu dem NodeMap Projekt Verzeichnis:
|
||||||
|
```shell
|
||||||
|
C:\Users\Administrator>cd C:\inetpub\wwwroot\talas5\nodeMap
|
||||||
|
```
|
||||||
|
|
||||||
|
- Befehl zum Erstellen eines Dienstes:
|
||||||
|
Führen Sie den folgenden Befehl aus, um einen neuen Dienst zu erstellen:
|
||||||
|
```shell
|
||||||
|
nssm.exe install NodeMapService
|
||||||
|
```
|
||||||
|
Nachdem Sie diesen Befehl ausgeführt haben, öffnet sich ein NSSM-Dialogfenster.
|
||||||
|
|
||||||
|
**Dienstkonfiguration:**
|
||||||
|
In dem geöffneten NSSM-Dialogfenster müssen Sie einige Parameter angeben:
|
||||||
|
|
||||||
|
- **Path:** Der Pfad zur ausführbaren Datei, die der Dienst ausführen soll.
|
||||||
|
```shell
|
||||||
|
C:\inetpub\wwwroot\talas5\nodeMap\StartNodeApp.bat
|
||||||
|
```
|
||||||
|
- **Startup directory:** Das Verzeichnis, in dem die Anwendung gestartet werden soll.
|
||||||
|
```shell
|
||||||
|
C:\inetpub\wwwroot\talas5\nodeMap
|
||||||
|
```
|
||||||
|
- **Arguments:** kann leer gelassen werden.
|
||||||
|
|
||||||
|
- Dienst starten:
|
||||||
|
Sobald der Dienst erstellt wurde, können Sie ihn starten.
|
||||||
|
Das können Sie entweder über die Eingabeaufforderung oder über die Diensteverwaltung von Windows tun.
|
||||||
|
Um den Dienst über die Eingabeaufforderung zu starten, verwenden Sie den folgenden Befehl:
|
||||||
|
```shell
|
||||||
|
nssm.exe start DienstName
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **Dienst bearbeiten:**
|
||||||
|
```shell
|
||||||
|
nssm.exe edit NodeMapService
|
||||||
|
```
|
||||||
|
- **Dienst entfernen:**
|
||||||
|
```shell
|
||||||
|
nssm.exe remove NodeMapService confirm
|
||||||
|
```
|
||||||
|
dauert bis 1 Minute
|
||||||
|
```
|
||||||
2164
package-lock.json
generated
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "nodemap",
|
"name": "nodemap",
|
||||||
"version": "1.1.300",
|
"version": "1.1.361",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.13.3",
|
"@emotion/react": "^11.13.3",
|
||||||
"@emotion/styled": "^11.13.0",
|
"@emotion/styled": "^11.13.0",
|
||||||
"@heroicons/react": "^2.1.5",
|
"@heroicons/react": "^2.1.5",
|
||||||
|
"@iconify/react": "^6.0.1",
|
||||||
"@mui/icons-material": "^6.0.2",
|
"@mui/icons-material": "^6.0.2",
|
||||||
"@reduxjs/toolkit": "^2.5.1",
|
"@reduxjs/toolkit": "^2.5.1",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
@@ -39,17 +40,17 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node server.js",
|
"dev": "node server.js",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
|
"build:deploy": "next build && npm run create-zip",
|
||||||
|
"create-zip": "powershell -ExecutionPolicy Bypass -File ./scripts/create-deployment-zip.ps1",
|
||||||
"start": "cross-env NODE_ENV=production node server.js",
|
"start": "cross-env NODE_ENV=production node server.js",
|
||||||
"export": "next export",
|
"export": "next export",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"cypress": "cypress open",
|
|
||||||
"cypress:run": "cypress run",
|
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"bump-version": "node ./scripts/bumpVersion.js"
|
"bump-version": "node ./scripts/bumpVersion.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.54.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cypress": "^13.17.0",
|
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
|||||||
@@ -1,133 +0,0 @@
|
|||||||
// /pages/api/health.js
|
|
||||||
export default async function handler(req, res) {
|
|
||||||
const basePath = "talas5";
|
|
||||||
const protocol = "http";
|
|
||||||
const hostname = "10.10.0.70";
|
|
||||||
const port = "80";
|
|
||||||
const idMap = "12";
|
|
||||||
const idUser = "484";
|
|
||||||
const idLD = "50922";
|
|
||||||
|
|
||||||
const buildUrl = method =>
|
|
||||||
`${protocol}://${hostname}:${port}/${basePath}/ClientData/WebServiceMap.asmx/${method}?idMap=${idMap}&idUser=${idUser}`;
|
|
||||||
|
|
||||||
const externalUrls = {
|
|
||||||
GisStationsStaticDistrict: buildUrl("GisStationsStaticDistrict"),
|
|
||||||
GisLinesStatus: `${protocol}://${hostname}:${port}/${basePath}/ClientData/WebServiceMap.asmx/GisLinesStatus?idMap=${idMap}`,
|
|
||||||
GisStationsMeasurements: buildUrl("GisStationsMeasurements"),
|
|
||||||
GisStationsStatusDistrict: buildUrl("GisStationsStatusDistrict"),
|
|
||||||
GisSystemStatic: buildUrl("GisSystemStatic"),
|
|
||||||
};
|
|
||||||
|
|
||||||
const internalApiBase = "http://localhost:3000";
|
|
||||||
|
|
||||||
const internalApis = {
|
|
||||||
//area
|
|
||||||
readArea: `${internalApiBase}/api/talas_v5_DB/area/readArea?m=${idMap}`,
|
|
||||||
//device
|
|
||||||
getAllStationsNames: `${internalApiBase}/api/talas_v5_DB/device/getAllStationsNames`,
|
|
||||||
getDevices: `${internalApiBase}/api/talas_v5_DB/device/getDevices`,
|
|
||||||
//gisLines
|
|
||||||
readGisLines: `${internalApiBase}/api/talas_v5_DB/gisLines/readGisLines`,
|
|
||||||
//locationDevice
|
|
||||||
getDeviceId: `${internalApiBase}/api/talas_v5_DB/locationDevice/getDeviceId`,
|
|
||||||
locationDeviceNameById: `${internalApiBase}/api/talas_v5_DB/locationDevice/locationDeviceNameById?idLD=${idLD}`,
|
|
||||||
locationDevices: `${internalApiBase}/api/talas_v5_DB/locationDevice/locationDevices`,
|
|
||||||
//pois
|
|
||||||
addPoi: `${internalApiBase}/api/talas_v5_DB/pois/addPoi`,
|
|
||||||
deletePoi: `${internalApiBase}/api/talas_v5_DB/pois/deletePoi`,
|
|
||||||
getPoiById: `${internalApiBase}/api/talas_v5_DB/pois/getPoiById`,
|
|
||||||
poiIcons: `${internalApiBase}/api/talas_v5_DB/pois/poi-icons`,
|
|
||||||
readAllPOIs: `${internalApiBase}/api/talas_v5_DB/pois/readAllPOIs`,
|
|
||||||
updateLocation: `${internalApiBase}/api/talas_v5_DB/pois/updateLocation`,
|
|
||||||
updatePoi: `${internalApiBase}/api/talas_v5_DB/pois/updatePoi`,
|
|
||||||
//poiTyp
|
|
||||||
readPoiTyp: `${internalApiBase}/api/talas_v5_DB/poiTyp/readPoiTyp`,
|
|
||||||
//station
|
|
||||||
getAllStationsNames: `${internalApiBase}/api/talas_v5_DB/station/getAllStationsNames`,
|
|
||||||
getDevices: `${internalApiBase}/api/talas_v5_DB/station/getDevices`,
|
|
||||||
//
|
|
||||||
priorityConfig: `${internalApiBase}/api/talas_v5_DB/priorityConfig`,
|
|
||||||
};
|
|
||||||
|
|
||||||
const results = {};
|
|
||||||
|
|
||||||
// Prüfe externe Webservices
|
|
||||||
await Promise.all(
|
|
||||||
Object.entries(externalUrls).map(async ([name, url]) => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(url);
|
|
||||||
results[name] = {
|
|
||||||
ok: response.ok,
|
|
||||||
status: response.status,
|
|
||||||
url,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (name === "GisSystemStatic" && response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
results["UserRights"] = {
|
|
||||||
ok: Array.isArray(data.Rights),
|
|
||||||
length: data.Rights?.length || 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
results[name] = {
|
|
||||||
ok: false,
|
|
||||||
error: error.message,
|
|
||||||
url,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Prüfe interne API-Routen
|
|
||||||
await Promise.all(
|
|
||||||
Object.entries(internalApis).map(async ([name, url]) => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(url);
|
|
||||||
results[`API_${name}`] = {
|
|
||||||
ok: response.ok,
|
|
||||||
status: response.status,
|
|
||||||
url,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
results[`API_${name}`] = {
|
|
||||||
ok: false,
|
|
||||||
error: error.message,
|
|
||||||
url,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Prüfe Mock-Status
|
|
||||||
const useMocksEnv = process.env.NEXT_PUBLIC_USE_MOCKS;
|
|
||||||
results["MockMode"] = {
|
|
||||||
expected: "false",
|
|
||||||
actual: useMocksEnv,
|
|
||||||
ok: useMocksEnv === "false",
|
|
||||||
info:
|
|
||||||
useMocksEnv === "false"
|
|
||||||
? "✅ Mockdaten deaktiviert – Live-Daten aktiv."
|
|
||||||
: "⚠️ Mockdaten aktiv – nicht für Produktion geeignet!",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prüfe Konfiguration der .env.production
|
|
||||||
results["envConfig"] = {
|
|
||||||
NODE_ENV: process.env.NODE_ENV || "undefined",
|
|
||||||
DB_HOST: process.env.DB_HOST || "❌ fehlt",
|
|
||||||
NEXT_PUBLIC_USE_MOCKS: useMocksEnv || "❌ fehlt",
|
|
||||||
status:
|
|
||||||
process.env.NODE_ENV === "production" && useMocksEnv === "false" && process.env.DB_HOST
|
|
||||||
? "✅ .env.production scheint korrekt."
|
|
||||||
: "⚠️ Bitte .env.production prüfen – möglicherweise fehlt etwas.",
|
|
||||||
};
|
|
||||||
|
|
||||||
const allOk = Object.values(results).every(r => r.ok);
|
|
||||||
|
|
||||||
res.status(allOk ? 200 : 207).json({
|
|
||||||
status: allOk ? "ok" : "partial",
|
|
||||||
version: "1.0.0",
|
|
||||||
services: results,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
// /pages/api/talas_v5_DB/area/updateArea.js
|
// /pages/api/talas_v5_DB/area/updateArea.js
|
||||||
import getPool from "../../../../utils/mysqlPool";
|
import getPool from "../../../../utils/mysqlPool";
|
||||||
|
import { getDebugLog } from "../../../../utils/configUtils";
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("Request erhalten:", req.method, req.body); // Debugging
|
console.log("Request erhalten:", req.method, req.body); // Debugging
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// /pages/api/talas_v5_DB/gisLines/updateLineCoordinates.js
|
// /pages/api/talas_v5_DB/gisLines/updateLineCoordinates.js
|
||||||
import getPool from "../../../../utils/mysqlPool"; // Singleton-Pool importieren
|
import getPool from "../../../../utils/mysqlPool"; // Singleton-Pool importieren
|
||||||
|
import { getDebugLog } from "../../../../utils/configUtils";
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
const pool = getPool(); // Singleton-Pool verwenden
|
const pool = getPool(); // Singleton-Pool verwenden
|
||||||
@@ -34,7 +35,7 @@ export default async function handler(req, res) {
|
|||||||
|
|
||||||
// Commit der Transaktion
|
// Commit der Transaktion
|
||||||
await connection.commit();
|
await connection.commit();
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("Transaction Complete.");
|
console.log("Transaction Complete.");
|
||||||
}
|
}
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
// pages/api/talas_v5_DB/pois/addPoi.js
|
// pages/api/talas_v5_DB/pois/addPoi.js
|
||||||
import getPool from "../../../../utils/mysqlPool"; // Singleton-Pool importieren
|
import getPool from "../../../../utils/mysqlPool"; // Singleton-Pool importieren
|
||||||
|
import { getDebugLog } from "../../../../utils/configUtils";
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
const pool = getPool(); // Singleton-Pool verwenden
|
const pool = getPool(); // Singleton-Pool verwenden
|
||||||
|
|
||||||
if (req.method === "POST") {
|
if (req.method === "POST") {
|
||||||
const { name, poiTypeId, latitude, longitude, idLD } = req.body;
|
const { name, poiTypeId, latitude, longitude, idLD } = req.body;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("Received data:", req.body); // Überprüfen der empfangenen Daten
|
console.log("Received data:", req.body); // Überprüfen der empfangenen Daten
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
pages/api/testDbConnection.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// pages/api/testDbConnection.js
|
||||||
|
import getPool from "../../utils/mysqlPool";
|
||||||
|
|
||||||
|
export default async function handler(req, res) {
|
||||||
|
const pool = getPool();
|
||||||
|
let connection;
|
||||||
|
try {
|
||||||
|
connection = await pool.getConnection();
|
||||||
|
const [rows] = await connection.query("SELECT 1 AS test");
|
||||||
|
console.log("DB-Verbindung erfolgreich! Ergebnis:", rows);
|
||||||
|
res.status(200).json({ success: true, result: rows });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("DB-Verbindungsfehler:", error);
|
||||||
|
res.status(500).json({ success: false, error: error.message });
|
||||||
|
} finally {
|
||||||
|
if (connection) connection.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
7
playwright/tests/example.spec.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// example.spec.js
|
||||||
|
const { test, expect } = require("@playwright/test");
|
||||||
|
|
||||||
|
test("simple test", async ({ page }) => {
|
||||||
|
await page.goto("https://playwright.dev");
|
||||||
|
await expect(page).toHaveTitle(/Playwright/);
|
||||||
|
});
|
||||||
34
public/config.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"_comment": [
|
||||||
|
"tileSources: 'local' für offline, 'osm' für online",
|
||||||
|
"center: Startmittelpunkt der Karte (lat, lng)",
|
||||||
|
"zoomOutCenter: Zielkoordinaten für Herauszoomen (lat, lng)",
|
||||||
|
"minZoom/maxZoom: erlaubte Zoomstufen pro Quelle"
|
||||||
|
],
|
||||||
|
"tileSources": {
|
||||||
|
"local": {
|
||||||
|
"url": "http://localhost/talas5/TileMap/mapTiles/{z}/{x}/{y}.png",
|
||||||
|
"_comment": "Offline-Kartenquelle (lokal)",
|
||||||
|
"minZoom": 5,
|
||||||
|
"maxZoom": 15
|
||||||
|
},
|
||||||
|
"osm": {
|
||||||
|
"url": "/tiles/{z}/{x}/{y}.png",
|
||||||
|
"_comment": "OpenStreetMap Online-Kartenquelle über Server-Proxy (relativ)",
|
||||||
|
"minZoom": 0,
|
||||||
|
"maxZoom": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": "osm",
|
||||||
|
"_comment_active": "Aktive Kartenquelle: 'local' oder 'osm'",
|
||||||
|
"center": [53.111111, 8.4625],
|
||||||
|
"_comment_center": "Startmittelpunkt der Karte (lat, lng)",
|
||||||
|
|
||||||
|
"zoomOutCenter": [51.41321407879154, 7.739617925303934],
|
||||||
|
"_comment_zoomOutCenter": "Zielkoordinaten für Herauszoomen (lat, lng)",
|
||||||
|
|
||||||
|
"basePath": "/talas5",
|
||||||
|
"_comment_basePath": "Basis-URL für API und Routing",
|
||||||
|
"debugLog": false,
|
||||||
|
"_comment_debugLog": "Debug-Logging für Client "
|
||||||
|
}
|
||||||
BIN
public/img/icons/critical-marker-icon-17.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
public/img/icons/critical-marker-icon-18.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
public/img/icons/major-marker-icon-25.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
public/img/icons/major-marker-icon-26.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
public/img/icons/major-marker-icon-4.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
public/img/icons/major-marker-icon-5.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/img/icons/marker-icon-storage-upright.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/img/icons/marker-icon-thermo.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
public/img/icons/minor-marker-icon-19.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
public/img/icons/minor-marker-icon-20.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
public/img/icons/system-marker-icon-15.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
public/img/icons/system-marker-icon-16.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
@@ -1,8 +1,39 @@
|
|||||||
// /redux/slices/database7polylines/polylineLayerVisibleSlice.js
|
// /redux/slices/database7polylines/polylineLayerVisibleSlice.js
|
||||||
|
|
||||||
import { createSlice } from "@reduxjs/toolkit";
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
// Thunk to initialize polyline visibility from localStorage using mapId/userId from URL
|
||||||
|
export const initializePolylineFromLocalStorageThunk = () => dispatch => {
|
||||||
|
try {
|
||||||
|
// Prüfe globales Nutzer-Flag
|
||||||
|
if (typeof window !== "undefined" && window.userToggledPolyline) {
|
||||||
|
console.log(
|
||||||
|
"[Redux] Initialisierung abgebrochen: Nutzer hat Polyline bereits manuell geändert."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const mapId = params.get("m");
|
||||||
|
const userId = params.get("u");
|
||||||
|
if (mapId && userId) {
|
||||||
|
const key = `polylineVisible_m${mapId}_u${userId}`;
|
||||||
|
const stored = localStorage.getItem(key);
|
||||||
|
const visible = stored === "true";
|
||||||
|
dispatch(initializePolylineFromLocalStorage(visible));
|
||||||
|
// Optional: log for debugging
|
||||||
|
console.log(
|
||||||
|
`Redux: Initialized polyline visibility from localStorage key '${key}':`,
|
||||||
|
visible
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error initializing polyline visibility from localStorage:", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
visible: false, // oder Standardwert
|
visible: false, // Standardwert - wird in der Komponente aus localStorage überschrieben
|
||||||
|
isInitialized: false, // Flag um zu verfolgen, ob der Wert aus localStorage geladen wurde
|
||||||
};
|
};
|
||||||
|
|
||||||
const polylineLayerVisibleSlice = createSlice({
|
const polylineLayerVisibleSlice = createSlice({
|
||||||
@@ -11,11 +42,51 @@ const polylineLayerVisibleSlice = createSlice({
|
|||||||
reducers: {
|
reducers: {
|
||||||
setPolylineVisible: (state, action) => {
|
setPolylineVisible: (state, action) => {
|
||||||
state.visible = action.payload;
|
state.visible = action.payload;
|
||||||
localStorage.setItem("polylineVisible", action.payload);
|
state.isInitialized = true;
|
||||||
|
// Save to localStorage using mapId/userId key
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const mapId = params.get("m");
|
||||||
|
const userId = params.get("u");
|
||||||
|
if (mapId && userId) {
|
||||||
|
const key = `polylineVisible_m${mapId}_u${userId}`;
|
||||||
|
localStorage.setItem(key, action.payload.toString());
|
||||||
|
console.log(
|
||||||
|
"[Redux/setPolylineVisible] payload:",
|
||||||
|
action.payload,
|
||||||
|
"key:",
|
||||||
|
key,
|
||||||
|
"localStorage:",
|
||||||
|
localStorage.getItem(key)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// fallback: do nothing
|
||||||
|
}
|
||||||
|
console.log("💾 Redux: setPolylineVisible called with:", action.payload);
|
||||||
|
},
|
||||||
|
initializePolylineFromLocalStorage: (state, action) => {
|
||||||
|
// Diese Action wird nur beim Initialisieren aus localStorage verwendet
|
||||||
|
if (typeof window !== "undefined" && window.userToggledPolyline) {
|
||||||
|
console.log(
|
||||||
|
"[Redux] Initialisierung im Reducer abgebrochen: Nutzer hat Polyline bereits manuell geändert."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.visible = action.payload;
|
||||||
|
state.isInitialized = true;
|
||||||
|
console.log(
|
||||||
|
"🔧 Redux: initializePolylineFromLocalStorage called with:",
|
||||||
|
action.payload,
|
||||||
|
"visible:",
|
||||||
|
state.visible
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { setPolylineVisible } = polylineLayerVisibleSlice.actions;
|
export const { setPolylineVisible, initializePolylineFromLocalStorage } =
|
||||||
export const selectPolylineVisible = (state) => state.polylineLayerVisible.visible;
|
polylineLayerVisibleSlice.actions;
|
||||||
|
export const selectPolylineVisible = state => state.polylineLayerVisible.visible;
|
||||||
|
export const selectPolylineInitialized = state => state.polylineLayerVisible.isInitialized;
|
||||||
export default polylineLayerVisibleSlice.reducer;
|
export default polylineLayerVisibleSlice.reducer;
|
||||||
|
|||||||
@@ -15,20 +15,68 @@ const mapLayersSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setLayerVisibility: (state, action) => {
|
setLayerVisibility: (state, action) => {
|
||||||
const { layer, visibility } = action.payload;
|
const { layer, visibility } = action.payload;
|
||||||
if (state[layer] !== undefined) {
|
state[layer] = visibility; // Sicher setzen
|
||||||
state[layer] = visibility;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setInitialLayers: (state, action) => {
|
setInitialLayers: (state, action) => {
|
||||||
const systems = action.payload; // Array of GisSystem
|
const systems = action.payload; // Array of GisSystem
|
||||||
systems.forEach((system) => {
|
|
||||||
|
const mapId =
|
||||||
|
typeof localStorage !== "undefined" ? localStorage.getItem("currentMapId") : null;
|
||||||
|
const userId =
|
||||||
|
typeof localStorage !== "undefined" ? localStorage.getItem("currentUserId") : null;
|
||||||
|
const mapStorageKey =
|
||||||
|
mapId && userId ? `mapLayersVisibility_m${mapId}_u${userId}` : "mapLayersVisibility";
|
||||||
|
|
||||||
|
let existingVisibility = {};
|
||||||
|
if (typeof localStorage !== "undefined") {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(mapStorageKey);
|
||||||
|
if (stored) {
|
||||||
|
existingVisibility = JSON.parse(stored) || {};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading stored visibility:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Baue ein Set der gültigen Keys (nur Systeme mit Map===1)
|
||||||
|
const validKeys = new Set();
|
||||||
|
|
||||||
|
systems.forEach(system => {
|
||||||
|
if (system.Map !== 1) return; // Komplett überspringen, wenn Map==0
|
||||||
const key = `system-${system.IdSystem}`;
|
const key = `system-${system.IdSystem}`;
|
||||||
state[key] = true; // oder false, je nach Default
|
validKeys.add(key);
|
||||||
|
|
||||||
|
if (Object.prototype.hasOwnProperty.call(existingVisibility, key)) {
|
||||||
|
state[key] = existingVisibility[key];
|
||||||
|
} else {
|
||||||
|
state[key] = system.Allow === 1 && system.Map === 1; // Allow + Map Bedingung
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Entferne aus dem State alle Keys, die nicht mehr gültig sind (Map wurde evtl. auf 0 gesetzt)
|
||||||
|
Object.keys(state).forEach(k => {
|
||||||
|
if (!validKeys.has(k)) {
|
||||||
|
delete state[k];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bereinige auch den gespeicherten localStorage-Eintrag
|
||||||
|
if (typeof localStorage !== "undefined") {
|
||||||
|
const cleaned = {};
|
||||||
|
Object.keys(state).forEach(k => {
|
||||||
|
cleaned[k] = state[k];
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
localStorage.setItem(mapStorageKey, JSON.stringify(cleaned));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Could not persist cleaned map layer visibility", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { toggleLayer, setLayerVisibility, setInitialLayers } = mapLayersSlice.actions;
|
export const { toggleLayer, setLayerVisibility, setInitialLayers } = mapLayersSlice.actions;
|
||||||
export const selectMapLayersState = (state) => state.mapLayers || initialState;
|
export const selectMapLayersState = state => state.mapLayers || initialState;
|
||||||
export default mapLayersSlice.reducer;
|
export default mapLayersSlice.reducer;
|
||||||
|
|||||||
105
scripts/create-deployment-zip.ps1
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# PowerShell Script to create deployment ZIP after successful build
|
||||||
|
param(
|
||||||
|
[string]$ProjectRoot = $PSScriptRoot + "\.."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Read package.json to get name and version
|
||||||
|
$packageJsonPath = Join-Path $ProjectRoot "package.json"
|
||||||
|
$packageJson = Get-Content $packageJsonPath | ConvertFrom-Json
|
||||||
|
$projectName = $packageJson.name
|
||||||
|
$version = $packageJson.version
|
||||||
|
|
||||||
|
# Create ZIP filename
|
||||||
|
$zipFileName = "${projectName}-${version}.zip"
|
||||||
|
$zipPath = Join-Path $ProjectRoot $zipFileName
|
||||||
|
|
||||||
|
# Remove existing ZIP if it exists
|
||||||
|
if (Test-Path $zipPath) {
|
||||||
|
Remove-Item $zipPath -Force
|
||||||
|
Write-Host "[DELETE] Removed existing ZIP: $zipFileName" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define files and directories to include
|
||||||
|
$itemsToInclude = @(
|
||||||
|
".next",
|
||||||
|
"public",
|
||||||
|
".env.production",
|
||||||
|
"server.js",
|
||||||
|
"nssm.exe",
|
||||||
|
"nssm.exe Installation.md",
|
||||||
|
"package.json",
|
||||||
|
"package-lock.json",
|
||||||
|
"Start-Dev.ps1",
|
||||||
|
"StartNodeApp.bat"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check which items exist
|
||||||
|
$existingItems = @()
|
||||||
|
foreach ($item in $itemsToInclude) {
|
||||||
|
$itemPath = Join-Path $ProjectRoot $item
|
||||||
|
if (Test-Path $itemPath) {
|
||||||
|
$existingItems += $item
|
||||||
|
Write-Host "[FOUND] $item" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "[MISSING] $item" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($existingItems.Count -eq 0) {
|
||||||
|
Write-Host "[ERROR] No items found to include in ZIP!" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create temporary directory for ZIP contents
|
||||||
|
$tempDir = Join-Path $ProjectRoot "temp-deployment"
|
||||||
|
if (Test-Path $tempDir) {
|
||||||
|
Remove-Item $tempDir -Recurse -Force
|
||||||
|
}
|
||||||
|
New-Item -ItemType Directory -Path $tempDir | Out-Null
|
||||||
|
|
||||||
|
# Copy items to temporary directory
|
||||||
|
foreach ($item in $existingItems) {
|
||||||
|
$sourcePath = Join-Path $ProjectRoot $item
|
||||||
|
$destPath = Join-Path $tempDir $item
|
||||||
|
|
||||||
|
if (Test-Path $sourcePath -PathType Container) {
|
||||||
|
# It's a directory
|
||||||
|
Copy-Item $sourcePath $destPath -Recurse -Force
|
||||||
|
Write-Host "[COPY DIR] $item" -ForegroundColor Cyan
|
||||||
|
} else {
|
||||||
|
# It's a file
|
||||||
|
Copy-Item $sourcePath $destPath -Force
|
||||||
|
Write-Host "[COPY FILE] $item" -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create ZIP file
|
||||||
|
try {
|
||||||
|
Write-Host "[CREATE] Creating ZIP: $zipFileName..." -ForegroundColor Blue
|
||||||
|
|
||||||
|
# Use .NET compression to create ZIP
|
||||||
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||||
|
[System.IO.Compression.ZipFile]::CreateFromDirectory($tempDir, $zipPath)
|
||||||
|
|
||||||
|
# Get ZIP file size
|
||||||
|
$zipSize = (Get-Item $zipPath).Length
|
||||||
|
$zipSizeMB = [math]::Round($zipSize / 1MB, 2)
|
||||||
|
|
||||||
|
Write-Host "[SUCCESS] Successfully created deployment ZIP!" -ForegroundColor Green
|
||||||
|
Write-Host "[FILE] $zipFileName" -ForegroundColor White
|
||||||
|
Write-Host "[SIZE] $zipSizeMB MB" -ForegroundColor White
|
||||||
|
Write-Host "[LOCATION] $zipPath" -ForegroundColor White
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
Write-Host "[ERROR] Failed to create ZIP: $($_.Exception.Message)" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
} finally {
|
||||||
|
# Clean up temporary directory
|
||||||
|
if (Test-Path $tempDir) {
|
||||||
|
Remove-Item $tempDir -Recurse -Force
|
||||||
|
Write-Host "[CLEANUP] Cleaned up temporary files" -ForegroundColor Gray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "[COMPLETE] Deployment ZIP ready for production deployment!" -ForegroundColor Green
|
||||||
64
server.js
@@ -27,16 +27,46 @@ const extractData = (json, name) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
app.prepare().then(() => {
|
app.prepare().then(() => {
|
||||||
const server = createServer((req, res) => {
|
const express = require("express");
|
||||||
|
const expressApp = express();
|
||||||
|
// Proxy-Route für Karten-Tiles
|
||||||
|
expressApp.get("/tiles/:z/:x/:y.png", async (req, res) => {
|
||||||
|
const { z, x, y } = req.params;
|
||||||
|
// OSM-Subdomain (a, b, c) zufällig wählen
|
||||||
|
const subdomains = ["a", "b", "c"];
|
||||||
|
const s = subdomains[Math.floor(Math.random() * subdomains.length)];
|
||||||
|
const tileUrl = `https://${s}.tile.openstreetmap.org/${z}/${x}/${y}.png`;
|
||||||
|
try {
|
||||||
|
const response = await fetch(tileUrl);
|
||||||
|
if (!response.ok) {
|
||||||
|
res.status(response.status).send("Tile not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.set("Content-Type", "image/png");
|
||||||
|
response.body.pipe(res);
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).send("Error fetching tile");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Alle anderen Routen an Next.js
|
||||||
|
expressApp.all("*", (req, res) => {
|
||||||
handle(req, res);
|
handle(req, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const server = createServer(expressApp);
|
||||||
|
|
||||||
const io = new Server(server);
|
const io = new Server(server);
|
||||||
|
|
||||||
|
// ✅ Globaler Cache für alle aktuellen Daten
|
||||||
|
const globalDataCache = new Map();
|
||||||
|
|
||||||
io.on("connection", socket => {
|
io.on("connection", socket => {
|
||||||
const { m: idMap, u: idUser } = socket.handshake.query;
|
const { m: idMap, u: idUser } = socket.handshake.query;
|
||||||
console.log(`🔌 WebSocket verbunden (idMap=${idMap}, idUser=${idUser})`);
|
console.log(`🔌 WebSocket verbunden (idMap=${idMap}, idUser=${idUser})`);
|
||||||
|
|
||||||
|
const cacheKey = `${idMap}_${idUser}`;
|
||||||
|
|
||||||
const endpoints = [
|
const endpoints = [
|
||||||
{
|
{
|
||||||
name: "GisLinesStatus",
|
name: "GisLinesStatus",
|
||||||
@@ -69,6 +99,22 @@ app.prepare().then(() => {
|
|||||||
|
|
||||||
const lastDataMap = {};
|
const lastDataMap = {};
|
||||||
|
|
||||||
|
// ✅ Funktion um sofort alle verfügbaren Daten zu senden (für Browser-Reload)
|
||||||
|
const sendAllCurrentData = () => {
|
||||||
|
const cachedData = globalDataCache.get(cacheKey);
|
||||||
|
if (cachedData && Object.keys(cachedData).length > 0) {
|
||||||
|
console.log(
|
||||||
|
`📦 Sending all current data to client (${Object.keys(cachedData).length} endpoints)`
|
||||||
|
);
|
||||||
|
Object.entries(cachedData).forEach(([name, data]) => {
|
||||||
|
socket.emit(`${name}Updated`, data);
|
||||||
|
console.log(`🔄 Browser-Reload: ${name} data sent`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(`📭 No cached data available for ${cacheKey}, will fetch fresh data`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
for (const { name, getUrl, mock } of endpoints) {
|
for (const { name, getUrl, mock } of endpoints) {
|
||||||
try {
|
try {
|
||||||
@@ -99,6 +145,14 @@ app.prepare().then(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newDataStr = JSON.stringify(statis);
|
const newDataStr = JSON.stringify(statis);
|
||||||
|
|
||||||
|
// ✅ Cache aktualisieren
|
||||||
|
if (!globalDataCache.has(cacheKey)) {
|
||||||
|
globalDataCache.set(cacheKey, {});
|
||||||
|
}
|
||||||
|
globalDataCache.get(cacheKey)[name] = statis;
|
||||||
|
|
||||||
|
// ✅ Nur bei Änderungen senden (setInterval-Logik)
|
||||||
if (newDataStr !== lastDataMap[name]) {
|
if (newDataStr !== lastDataMap[name]) {
|
||||||
lastDataMap[name] = newDataStr;
|
lastDataMap[name] = newDataStr;
|
||||||
socket.emit(`${name}Updated`, statis);
|
socket.emit(`${name}Updated`, statis);
|
||||||
@@ -113,9 +167,15 @@ app.prepare().then(() => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// fetchData immer ausführen – unabhängig vom Modus
|
// ✅ Beim Connect: Sofort alle aktuellen Daten senden (für Browser-Reload)
|
||||||
|
sendAllCurrentData();
|
||||||
|
|
||||||
|
// ✅ Dann erste Datenabfrage durchführen
|
||||||
fetchData();
|
fetchData();
|
||||||
|
|
||||||
|
// ✅ setInterval für regelmäßige Updates (nur bei Änderungen)
|
||||||
const interval = setInterval(fetchData, 5000); // 5 Sekunden ,TALAS.web nutzt 12 Sekunden
|
const interval = setInterval(fetchData, 5000); // 5 Sekunden ,TALAS.web nutzt 12 Sekunden
|
||||||
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
console.log("❌ WebSocket getrennt");
|
console.log("❌ WebSocket getrennt");
|
||||||
|
|||||||
0
services/localStorage/stationCacheService.js
Normal file
@@ -1,12 +1,23 @@
|
|||||||
|
import { getDebugLog } from "../../utils/configUtils";
|
||||||
// /services/webservice/fetchGisLinesStatusService.js
|
// /services/webservice/fetchGisLinesStatusService.js
|
||||||
|
let __configCache;
|
||||||
|
async function getConfig() {
|
||||||
|
if (__configCache) return __configCache;
|
||||||
|
const res = await fetch("/config.json");
|
||||||
|
if (!res.ok) throw new Error("config.json konnte nicht geladen werden");
|
||||||
|
__configCache = await res.json();
|
||||||
|
return __configCache;
|
||||||
|
}
|
||||||
|
|
||||||
export const fetchGisLinesStatusService = async () => {
|
export const fetchGisLinesStatusService = async () => {
|
||||||
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
const config = await getConfig();
|
||||||
|
const basePath = config.basePath || "";
|
||||||
|
|
||||||
if (useMocks) {
|
if (useMocks) {
|
||||||
const mockBasePath = "/api/mocks/webservice/gisLinesStatus";
|
const mockBasePath = "/api/mocks/webservice/gisLinesStatus";
|
||||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("🧪 Mock-Modus aktiviert: fetchGisLinesStatusService ", mockURL);
|
console.log("🧪 Mock-Modus aktiviert: fetchGisLinesStatusService ", mockURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,11 +27,9 @@ export const fetchGisLinesStatusService = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mockData = await response.json();
|
const mockData = await response.json();
|
||||||
|
|
||||||
if (!Array.isArray(mockData.Statis)) {
|
if (!Array.isArray(mockData.Statis)) {
|
||||||
throw new Error("Ungültige Struktur: 'Status' fehlt im Mock");
|
throw new Error("Ungültige Struktur: 'Status' fehlt im Mock");
|
||||||
}
|
}
|
||||||
|
|
||||||
return mockData.Statis;
|
return mockData.Statis;
|
||||||
} else {
|
} else {
|
||||||
const baseUrl = `${window.location.protocol}//${window.location.hostname}:80${basePath}/ClientData/WebServiceMap.asmx`;
|
const baseUrl = `${window.location.protocol}//${window.location.hostname}:80${basePath}/ClientData/WebServiceMap.asmx`;
|
||||||
@@ -29,7 +38,7 @@ export const fetchGisLinesStatusService = async () => {
|
|||||||
const idMap = params.get("m");
|
const idMap = params.get("m");
|
||||||
|
|
||||||
const url = `${baseUrl}/GisLinesStatus?idMap=${idMap}`;
|
const url = `${baseUrl}/GisLinesStatus?idMap=${idMap}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("📡 fetchGisLinesStatusService URL:", url);
|
console.log("📡 fetchGisLinesStatusService URL:", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
|
import { getDebugLog } from "../../utils/configUtils";
|
||||||
|
let __configCache;
|
||||||
|
async function getConfig() {
|
||||||
|
if (__configCache) return __configCache;
|
||||||
|
const res = await fetch("/config.json");
|
||||||
|
if (!res.ok) throw new Error("config.json konnte nicht geladen werden");
|
||||||
|
__configCache = await res.json();
|
||||||
|
return __configCache;
|
||||||
|
}
|
||||||
|
|
||||||
export const fetchGisStationsMeasurementsService = async () => {
|
export const fetchGisStationsMeasurementsService = async () => {
|
||||||
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
const config = await getConfig();
|
||||||
|
const basePath = config.basePath || "";
|
||||||
|
|
||||||
if (useMocks) {
|
if (useMocks) {
|
||||||
const mockBasePath = "/api/mocks/webservice/gisStationsMeasurements";
|
const mockBasePath = "/api/mocks/webservice/gisStationsMeasurements";
|
||||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("🧪 Mock-Modus aktiviert: fetchGisStationsMeasurementsService ", mockURL);
|
console.log("🧪 Mock-Modus aktiviert: fetchGisStationsMeasurementsService ", mockURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +39,7 @@ export const fetchGisStationsMeasurementsService = async () => {
|
|||||||
const idUser = params.get("u");
|
const idUser = params.get("u");
|
||||||
|
|
||||||
const url = `${baseUrl}/GisStationsMeasurements?idMap=${idMap}&idUser=${idUser}`;
|
const url = `${baseUrl}/GisStationsMeasurements?idMap=${idMap}&idUser=${idUser}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("📡 fetchGisStationsMeasurementsService URL:", url);
|
console.log("📡 fetchGisStationsMeasurementsService URL:", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,18 @@
|
|||||||
* @returns {Promise<Array>} Liste mit Points[]
|
* @returns {Promise<Array>} Liste mit Points[]
|
||||||
* @throws {Error} bei Fehler oder ungültiger Antwortstruktur
|
* @throws {Error} bei Fehler oder ungültiger Antwortstruktur
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { getDebugLog, getConfig } from "../../utils/configUtils";
|
||||||
|
|
||||||
export const fetchGisStationsStaticDistrictService = async () => {
|
export const fetchGisStationsStaticDistrictService = async () => {
|
||||||
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
const config = await getConfig();
|
||||||
|
const basePath = config.basePath || "";
|
||||||
|
|
||||||
if (useMocks) {
|
if (useMocks) {
|
||||||
const mockBasePath = "/api/mocks/webservice/gisStationsStaticDistrict";
|
const mockBasePath = "/api/mocks/webservice/gisStationsStaticDistrict";
|
||||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("🧪 Mock-Modus aktiviert: fetchGisStationsStaticDistrictService ", mockURL);
|
console.log("🧪 Mock-Modus aktiviert: fetchGisStationsStaticDistrictService ", mockURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +39,7 @@ export const fetchGisStationsStaticDistrictService = async () => {
|
|||||||
const idUser = params.get("u");
|
const idUser = params.get("u");
|
||||||
|
|
||||||
const url = `${baseUrl}/GisStationsStaticDistrict?idMap=${idMap}&idUser=${idUser}`;
|
const url = `${baseUrl}/GisStationsStaticDistrict?idMap=${idMap}&idUser=${idUser}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("📡 fetchGisStationsStaticDistrictService URL:", url);
|
console.log("📡 fetchGisStationsStaticDistrictService URL:", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,18 @@
|
|||||||
* @returns {Promise<Array>} Liste mit Statis[]
|
* @returns {Promise<Array>} Liste mit Statis[]
|
||||||
* @throws {Error} bei Fehler oder ungültiger Antwortstruktur
|
* @throws {Error} bei Fehler oder ungültiger Antwortstruktur
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { getDebugLog, getConfig } from "../../utils/configUtils";
|
||||||
|
|
||||||
export const fetchGisStationsStatusDistrictService = async () => {
|
export const fetchGisStationsStatusDistrictService = async () => {
|
||||||
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
const config = await getConfig();
|
||||||
|
const basePath = config.basePath || "";
|
||||||
|
|
||||||
if (useMocks) {
|
if (useMocks) {
|
||||||
const mockBasePath = "/api/mocks/webservice/gisStationsStatusDistrict";
|
const mockBasePath = "/api/mocks/webservice/gisStationsStatusDistrict";
|
||||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("🧪 Mock-Modus aktiviert: fetchGisStationsStatusDistrictService ", mockURL);
|
console.log("🧪 Mock-Modus aktiviert: fetchGisStationsStatusDistrictService ", mockURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +39,7 @@ export const fetchGisStationsStatusDistrictService = async () => {
|
|||||||
const idUser = params.get("u");
|
const idUser = params.get("u");
|
||||||
|
|
||||||
const url = `${baseUrl}/GisStationsStatusDistrict?idMap=${idMap}&idUser=${idUser}`;
|
const url = `${baseUrl}/GisStationsStatusDistrict?idMap=${idMap}&idUser=${idUser}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("📡 fetchGisStationsStatusDistrictService URL:", url);
|
console.log("📡 fetchGisStationsStatusDistrictService URL:", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,18 @@
|
|||||||
* @returns {Promise<Array>} Liste mit Systems[]
|
* @returns {Promise<Array>} Liste mit Systems[]
|
||||||
* @throws {Error} bei Fehler oder ungültiger Antwortstruktur
|
* @throws {Error} bei Fehler oder ungültiger Antwortstruktur
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { getDebugLog, getConfig } from "../../utils/configUtils";
|
||||||
|
|
||||||
export const fetchGisSystemStaticService = async () => {
|
export const fetchGisSystemStaticService = async () => {
|
||||||
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
const config = await getConfig();
|
||||||
|
const basePath = config.basePath || "";
|
||||||
|
|
||||||
if (useMocks) {
|
if (useMocks) {
|
||||||
const mockBasePath = "/api/mocks/webservice/gisSystemStatic";
|
const mockBasePath = "/api/mocks/webservice/gisSystemStatic";
|
||||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("🧪 Mock-Modus aktiviert: fetchGisSystemStaticService ", mockURL);
|
console.log("🧪 Mock-Modus aktiviert: fetchGisSystemStaticService ", mockURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +38,7 @@ export const fetchGisSystemStaticService = async () => {
|
|||||||
const idUser = params.get("u");
|
const idUser = params.get("u");
|
||||||
|
|
||||||
const url = `${baseUrl}/GisSystemStatic?idMap=${idMap}&idUser=${idUser}`;
|
const url = `${baseUrl}/GisSystemStatic?idMap=${idMap}&idUser=${idUser}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("📡 fetchGisSystemStaticService von service URL:", url);
|
console.log("📡 fetchGisSystemStaticService von service URL:", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,18 @@
|
|||||||
* @returns {Promise<Array>} Rechte-Array
|
* @returns {Promise<Array>} Rechte-Array
|
||||||
* @throws {Error} bei Lade- oder Strukturfehler
|
* @throws {Error} bei Lade- oder Strukturfehler
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { getDebugLog, getConfig } from "../../utils/configUtils";
|
||||||
|
|
||||||
export const fetchUserRightsService = async () => {
|
export const fetchUserRightsService = async () => {
|
||||||
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
const useMocks = process.env.NEXT_PUBLIC_USE_MOCKS === "true";
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
const config = await getConfig();
|
||||||
|
const basePath = config.basePath || "";
|
||||||
|
|
||||||
if (useMocks) {
|
if (useMocks) {
|
||||||
const mockBasePath = "/api/mocks/webservice/gisSystemStatic";
|
const mockBasePath = "/api/mocks/webservice/gisSystemStatic";
|
||||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("🧪 Mock-Modus aktiviert: fetchUserRightsService ", mockURL);
|
console.log("🧪 Mock-Modus aktiviert: fetchUserRightsService ", mockURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +34,7 @@ export const fetchUserRightsService = async () => {
|
|||||||
const idUser = params.get("u");
|
const idUser = params.get("u");
|
||||||
|
|
||||||
const url = `${baseUrl}/GisSystemStatic?idMap=${idMap}&idUser=${idUser}`;
|
const url = `${baseUrl}/GisSystemStatic?idMap=${idMap}&idUser=${idUser}`;
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("🔍 Rechte-Fetch URL:", url);
|
console.log("🔍 Rechte-Fetch URL:", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +50,7 @@ export const fetchUserRightsService = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("👤 Rechte-Response JSON:", json);
|
console.log("👤 Rechte-Response JSON:", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
0
services/webservice/idsystemTypFilterService.js
Normal file
4
test-results/.last-run.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"status": "passed",
|
||||||
|
"failedTests": []
|
||||||
|
}
|
||||||
26
utils/configUtils.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// utils/configUtils.js
|
||||||
|
let __configCache;
|
||||||
|
export async function getConfig() {
|
||||||
|
if (__configCache) return __configCache;
|
||||||
|
const res = await fetch("/config.json");
|
||||||
|
if (!res.ok) throw new Error("config.json konnte nicht geladen werden");
|
||||||
|
__configCache = await res.json();
|
||||||
|
return __configCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync helper for debugLog (for use in event handlers etc.)
|
||||||
|
let debugLogValue;
|
||||||
|
export function getDebugLog() {
|
||||||
|
if (debugLogValue !== undefined) return debugLogValue;
|
||||||
|
// Try to read from window.__appConfig if available (set at app start)
|
||||||
|
if (
|
||||||
|
typeof window !== "undefined" &&
|
||||||
|
window.__appConfig &&
|
||||||
|
typeof window.__appConfig.debugLog !== "undefined"
|
||||||
|
) {
|
||||||
|
debugLogValue = !!window.__appConfig.debugLog;
|
||||||
|
return debugLogValue;
|
||||||
|
}
|
||||||
|
// Fallback: default false
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -26,19 +26,38 @@ export const createAndSetDevices = async (
|
|||||||
measurements,
|
measurements,
|
||||||
oms // 🔁 OMS für Spiderfy hinzugefügt
|
oms // 🔁 OMS für Spiderfy hinzugefügt
|
||||||
) => {
|
) => {
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
// Prüfe, ob das System erlaubt ist (Allow === 1)
|
||||||
|
const systemConfig = Array.isArray(GisSystemStatic)
|
||||||
|
? GisSystemStatic.find(sys => sys.IdSystem === systemId)
|
||||||
|
: null;
|
||||||
|
if (!systemConfig) {
|
||||||
|
console.warn(`🚫 Kein Marker für System ${systemId}, Allow = ${systemConfig?.Allow}`);
|
||||||
|
setMarkersFunction([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let basePath = "";
|
||||||
|
try {
|
||||||
|
const res = await fetch("/config.json");
|
||||||
|
if (res.ok) {
|
||||||
|
const config = await res.json();
|
||||||
|
basePath = config.basePath || "";
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const staticDistrictData = selectGisStationsStaticDistrict(state);
|
const staticDistrictData = selectGisStationsStaticDistrict(state);
|
||||||
const statusDistrictData = selectGisStationsStatusDistrict(state);
|
const statusDistrictData = selectGisStationsStatusDistrict(state);
|
||||||
const measurementData = selectGisStationsMeasurements(state);
|
const measurementData = selectGisStationsMeasurements(state);
|
||||||
|
// Sicherstellen, dass measurementData immer ein Array ist
|
||||||
|
const safeMeasurementData = Array.isArray(measurementData) ? measurementData : [];
|
||||||
|
|
||||||
if (!staticDistrictData?.Points?.length || !statusDistrictData?.length) return;
|
if (!staticDistrictData?.Points?.length) return;
|
||||||
|
const hasStatus = Array.isArray(statusDistrictData) && statusDistrictData.length > 0;
|
||||||
|
|
||||||
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 => {
|
safeMeasurementData.forEach(m => {
|
||||||
if (!measurementsMap.has(m.IdLD)) {
|
if (!measurementsMap.has(m.IdLD)) {
|
||||||
measurementsMap.set(m.IdLD, m);
|
measurementsMap.set(m.IdLD, m);
|
||||||
}
|
}
|
||||||
@@ -101,10 +120,10 @@ export const createAndSetDevices = async (
|
|||||||
marker.bindPopup(popupContent);
|
marker.bindPopup(popupContent);
|
||||||
|
|
||||||
// ✅ Tooltip (nur für System 11)
|
// ✅ Tooltip (nur für System 11)
|
||||||
|
|
||||||
if (station.System === 11 && messung) {
|
if (station.System === 11 && messung) {
|
||||||
const gmaMap = new Map();
|
const gmaMap = new Map();
|
||||||
|
safeMeasurementData.forEach(m => {
|
||||||
measurementData?.forEach(m => {
|
|
||||||
if (!gmaMap.has(m.IdLD)) {
|
if (!gmaMap.has(m.IdLD)) {
|
||||||
gmaMap.set(m.IdLD, {});
|
gmaMap.set(m.IdLD, {});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,50 +6,147 @@ import "leaflet-contextmenu/dist/leaflet.contextmenu.css";
|
|||||||
import "overlapping-marker-spiderfier-leaflet";
|
import "overlapping-marker-spiderfier-leaflet";
|
||||||
|
|
||||||
export const initializeMap = (
|
export const initializeMap = (
|
||||||
mapRef,
|
mapContainer,
|
||||||
setMap,
|
|
||||||
setOms,
|
|
||||||
setMenuItemAdded,
|
setMenuItemAdded,
|
||||||
addItemsToMapContextMenu,
|
addItemsToMapContextMenu,
|
||||||
hasRights,
|
hasRights,
|
||||||
setPolylineEventsDisabled
|
setPolylineEventsDisabled,
|
||||||
|
logError = false
|
||||||
) => {
|
) => {
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH;
|
// basePath wird aus config.json geladen (siehe unten)
|
||||||
|
let basePath = undefined;
|
||||||
|
|
||||||
if (!mapRef.current) {
|
if (
|
||||||
console.error("❌ Fehler: mapRef.current ist nicht definiert.");
|
!mapContainer ||
|
||||||
return;
|
!(mapContainer instanceof HTMLElement) ||
|
||||||
}
|
!document.body.contains(mapContainer)
|
||||||
|
) {
|
||||||
if (mapRef.current._leaflet_id) {
|
if (logError) {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
console.error("❌ Fehler: map container ist nicht definiert oder nicht im DOM.");
|
||||||
console.log("⚠️ Karte bereits initialisiert");
|
|
||||||
}
|
}
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(window.location.origin);
|
// Robuste Entfernung einer evtl. alten Leaflet-Instanz und Reset des DOM-Elements
|
||||||
const originWithoutPort = `${url.protocol}//${url.hostname}`;
|
if (mapContainer) {
|
||||||
const tileLayerUrl = `${originWithoutPort}${basePath}/TileMap/mapTiles/{z}/{x}/{y}.png`;
|
if (mapContainer._leaflet_id) {
|
||||||
|
try {
|
||||||
|
// Leaflet-Instanz entfernen
|
||||||
|
if (mapContainer._leaflet_map && typeof mapContainer._leaflet_map.remove === "function") {
|
||||||
|
mapContainer._leaflet_map.remove();
|
||||||
|
}
|
||||||
|
// Leaflet 1.7+ speichert die Map-Instanz in L.Map._instances
|
||||||
|
if (L && L.Map && L.Map._instances && mapContainer._leaflet_id) {
|
||||||
|
delete L.Map._instances[mapContainer._leaflet_id];
|
||||||
|
}
|
||||||
|
// Auch in L.DomUtil._store ggf. entfernen
|
||||||
|
if (L && L.DomUtil && L.DomUtil._store && mapContainer._leaflet_id) {
|
||||||
|
delete L.DomUtil._store[mapContainer._leaflet_id];
|
||||||
|
}
|
||||||
|
// _leaflet_id vom DOM-Element entfernen
|
||||||
|
delete mapContainer._leaflet_id;
|
||||||
|
// Alle weiteren _leaflet-Properties entfernen
|
||||||
|
for (const key in mapContainer) {
|
||||||
|
if (key.startsWith("_leaflet")) {
|
||||||
|
try {
|
||||||
|
delete mapContainer[key];
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Fehler beim Entfernen der alten Leaflet-Instanz:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Container leeren (immer, auch wenn keine Map-Instanz)
|
||||||
|
mapContainer.innerHTML = "";
|
||||||
|
}
|
||||||
|
|
||||||
const initMap = L.map(mapRef.current, {
|
// --- CONFIG LOADING ---
|
||||||
center: [53.111111, 8.4625],
|
let config = null;
|
||||||
zoom: 12,
|
let tileLayerUrl = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
|
||||||
minZoom: 5,
|
let mapCenter = [53.111111, 8.4625];
|
||||||
maxZoom: 15,
|
let mapZoom = 12;
|
||||||
zoomControl: false,
|
let minZoom = 5;
|
||||||
dragging: true,
|
let maxZoom = 15;
|
||||||
contextmenu: true,
|
try {
|
||||||
layers: [],
|
if (window && window.__leafletConfig) {
|
||||||
});
|
config = window.__leafletConfig;
|
||||||
|
} else {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/config.json", false); // false = synchronous
|
||||||
|
xhr.send(null);
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
config = JSON.parse(xhr.responseText);
|
||||||
|
window.__leafletConfig = config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config) {
|
||||||
|
// basePath aus config.json
|
||||||
|
if (typeof config.basePath === "string") {
|
||||||
|
basePath = config.basePath;
|
||||||
|
}
|
||||||
|
// Tile source
|
||||||
|
if (config.tileSources && config.active && config.tileSources[config.active]) {
|
||||||
|
const tileSource = config.tileSources[config.active];
|
||||||
|
tileLayerUrl = tileSource.url || tileLayerUrl;
|
||||||
|
// Dynamische URL für Server-Tiles
|
||||||
|
if (tileLayerUrl.startsWith("/tiles") || tileLayerUrl.startsWith("tiles")) {
|
||||||
|
tileLayerUrl = `${window.location.origin.replace(/\/$/, "")}${
|
||||||
|
tileLayerUrl.startsWith("/") ? tileLayerUrl : "/" + tileLayerUrl
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
minZoom = tileSource.minZoom ?? minZoom;
|
||||||
|
maxZoom = tileSource.maxZoom ?? maxZoom;
|
||||||
|
}
|
||||||
|
// Center
|
||||||
|
if (Array.isArray(config.center)) {
|
||||||
|
mapCenter = config.center;
|
||||||
|
}
|
||||||
|
// Zoom (optional, fallback to 12)
|
||||||
|
if (typeof config.zoom === "number") {
|
||||||
|
mapZoom = config.zoom;
|
||||||
|
}
|
||||||
|
// minZoom/maxZoom global fallback
|
||||||
|
if (typeof config.minZoom === "number") {
|
||||||
|
minZoom = config.minZoom;
|
||||||
|
}
|
||||||
|
if (typeof config.maxZoom === "number") {
|
||||||
|
maxZoom = config.maxZoom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Fallback bleibt OSM und Defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
let initMap;
|
||||||
|
try {
|
||||||
|
initMap = L.map(mapContainer, {
|
||||||
|
center: mapCenter,
|
||||||
|
zoom: mapZoom,
|
||||||
|
minZoom: minZoom,
|
||||||
|
maxZoom: maxZoom,
|
||||||
|
zoomControl: false,
|
||||||
|
dragging: true,
|
||||||
|
contextmenu: true,
|
||||||
|
layers: [],
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
if (e.message && e.message.includes("Map container is already initialized")) {
|
||||||
|
console.warn(
|
||||||
|
"Leaflet: Map container is already initialized. Map wird nicht erneut initialisiert."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
initMap.dragging.enable();
|
initMap.dragging.enable();
|
||||||
|
|
||||||
L.tileLayer(tileLayerUrl, {
|
L.tileLayer(tileLayerUrl, {
|
||||||
attribution: "© Eigene Kartenquelle (offline)",
|
attribution: "© OpenStreetMap contributors",
|
||||||
tileSize: 256,
|
tileSize: 256,
|
||||||
minZoom: 5,
|
minZoom: minZoom,
|
||||||
maxZoom: 15,
|
maxZoom: maxZoom,
|
||||||
noWrap: true,
|
noWrap: true,
|
||||||
errorTileUrl: "/img/empty-tile.png", // Optional
|
errorTileUrl: "/img/empty-tile.png", // Optional
|
||||||
}).addTo(initMap);
|
}).addTo(initMap);
|
||||||
@@ -58,10 +155,8 @@ export const initializeMap = (
|
|||||||
nearbyDistance: 20,
|
nearbyDistance: 20,
|
||||||
});
|
});
|
||||||
|
|
||||||
setMap(initMap);
|
|
||||||
setOms(overlappingMarkerSpiderfier);
|
|
||||||
|
|
||||||
if (typeof addItemsToMapContextMenu === "function") {
|
if (typeof addItemsToMapContextMenu === "function") {
|
||||||
addItemsToMapContextMenu(initMap, setMenuItemAdded, setPolylineEventsDisabled);
|
addItemsToMapContextMenu(initMap, setMenuItemAdded, setPolylineEventsDisabled);
|
||||||
}
|
}
|
||||||
|
return { map: initMap, oms: overlappingMarkerSpiderfier };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export const checkOverlappingMarkers = (map, markers, plusIcon, oms) => {
|
|||||||
|
|
||||||
export const handlePlusIconClick = (map, markers, oms, clickedLatLng) => {
|
export const handlePlusIconClick = (map, markers, oms, clickedLatLng) => {
|
||||||
// Debugging-Ausgabe
|
// Debugging-Ausgabe
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("Plus-Icon Position:", clickedLatLng);
|
console.log("Plus-Icon Position:", clickedLatLng);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ export const handlePlusIconClick = (map, markers, oms, clickedLatLng) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Debugging-Ausgabe
|
// Debugging-Ausgabe
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("Gefundene Marker in der Nähe:", nearbyMarkers);
|
console.log("Gefundene Marker in der Nähe:", nearbyMarkers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ export const handlePlusIconClick = (map, markers, oms, clickedLatLng) => {
|
|||||||
// Spiderfy die gefundenen Marker
|
// Spiderfy die gefundenen Marker
|
||||||
oms.spiderfy(nearbyMarkers);
|
oms.spiderfy(nearbyMarkers);
|
||||||
} else {
|
} else {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("Keine überlappenden Marker gefunden.");
|
console.log("Keine überlappenden Marker gefunden.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import mysql from "mysql2/promise";
|
import mysql from "mysql2/promise";
|
||||||
// Variablen für den Singleton-Pool
|
|
||||||
let cachedPool;
|
let cachedPool;
|
||||||
let connectionCount = 0; // Zähler für die aktiven Verbindungen
|
let connectionCount = 0;
|
||||||
|
|
||||||
// Funktion zum Abrufen des Pools
|
|
||||||
function getPool() {
|
function getPool() {
|
||||||
if (!cachedPool) {
|
if (!cachedPool) {
|
||||||
cachedPool = mysql.createPool({
|
cachedPool = mysql.createPool({
|
||||||
@@ -12,12 +10,11 @@ function getPool() {
|
|||||||
password: process.env.DB_PASSWORD,
|
password: process.env.DB_PASSWORD,
|
||||||
database: process.env.DB_NAME,
|
database: process.env.DB_NAME,
|
||||||
port: process.env.DB_PORT,
|
port: process.env.DB_PORT,
|
||||||
connectionLimit: 20, // Setze ein Limit für gleichzeitige Verbindungen
|
connectionLimit: 20,
|
||||||
waitForConnections: true,
|
waitForConnections: true,
|
||||||
queueLimit: 10, // Warteschlangenlimit für Verbindungen
|
queueLimit: 10,
|
||||||
connectTimeout: 5000, // Timeout für Verbindungsversuche (5 Sekunden)
|
connectTimeout: 5000,
|
||||||
//acquireTimeout: 10000, // Timeout für Verbindungsanforderungen aus dem Pool (10 Sekunden)
|
idleTimeout: 60000,
|
||||||
idleTimeout: 60000, // 1 Minute
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ereignisse für das Protokollieren der Verbindungsstatistiken
|
// Ereignisse für das Protokollieren der Verbindungsstatistiken
|
||||||
@@ -25,46 +22,27 @@ function getPool() {
|
|||||||
|
|
||||||
cachedPool.on("acquire", () => {
|
cachedPool.on("acquire", () => {
|
||||||
connectionCount++;
|
connectionCount++;
|
||||||
if (process.env.NODE_ENV === "development") {
|
// Debug-Logging entfernt
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (connectionCount > maxUsed) {
|
||||||
console.log("\x1b[36m%s\x1b[0m", `➕ Connection acquired (${connectionCount} total)`);
|
maxUsed = connectionCount;
|
||||||
}
|
|
||||||
if (connectionCount > maxUsed) {
|
|
||||||
maxUsed = connectionCount;
|
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
|
||||||
console.log(`📈 Neue Höchstzahl aktiver gleichzeitiger Verbindungen: ${maxUsed}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cachedPool.on("release", () => {
|
cachedPool.on("release", () => {
|
||||||
connectionCount--;
|
connectionCount--;
|
||||||
if (process.env.NODE_ENV === "development") {
|
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
|
||||||
console.log("\x1b[32m%s\x1b[0m", `➖ Connection released (${connectionCount} total)`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cachedPool.on("enqueue", () => {
|
cachedPool.on("enqueue", () => {
|
||||||
if (process.env.NODE_ENV === "development") {
|
// Debug-Logging entfernt
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
|
||||||
console.warn("\x1b[33m%s\x1b[0m", "⏳ Pool voll – Anfrage in Warteschlange");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return cachedPool;
|
return cachedPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optionale Funktion zum Schließen aller Verbindungen im Pool (manuell aufzurufen)
|
|
||||||
export function closePool() {
|
export function closePool() {
|
||||||
if (cachedPool) {
|
if (cachedPool) {
|
||||||
cachedPool.end(() => {
|
cachedPool.end(() => {
|
||||||
console.log("All pool connections closed.");
|
console.log("All pool connections closed.");
|
||||||
});
|
});
|
||||||
cachedPool = null; // Setze den Pool auf null, um sicherzustellen, dass er neu erstellt wird, falls benötigt.
|
cachedPool = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
// utils/openInNewTab.js
|
// utils/openInNewTab.js
|
||||||
|
|
||||||
export function openInNewTab(e, target) {
|
export async function openInNewTab(e, target) {
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
const res = await fetch("/config.json");
|
||||||
|
const config = await res.json();
|
||||||
|
const basePath = config.basePath || "";
|
||||||
const url = new URL(window.location.origin);
|
const url = new URL(window.location.origin);
|
||||||
const originWithoutPort = `${url.protocol}//${url.hostname}`; // ohne Port!
|
const originWithoutPort = `${url.protocol}//${url.hostname}`; // ohne Port!
|
||||||
|
|
||||||
@@ -28,9 +30,7 @@ export function openInNewTab(e, target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (link) {
|
if (link) {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
console.log("🟢 Öffne Link:", link);
|
||||||
console.log("🟢 Öffne Link:", link);
|
|
||||||
}
|
|
||||||
window.open(link, "_blank");
|
window.open(link, "_blank");
|
||||||
} else {
|
} else {
|
||||||
console.error("❌ Kein gültiger Link gefunden.");
|
console.error("❌ Kein gültiger Link gefunden.");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export function subscribeToPolylineContextMenu() {
|
|||||||
store.subscribe(() => {
|
store.subscribe(() => {
|
||||||
const state = store.getState(); // Redux-Toolkit empfohlene Methode
|
const state = store.getState(); // Redux-Toolkit empfohlene Methode
|
||||||
if (state.polylineContextMenu.forceClose) {
|
if (state.polylineContextMenu.forceClose) {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("🚀 Redux-Event erkannt - Kontextmenü wird geschlossen.");
|
console.log("🚀 Redux-Event erkannt - Kontextmenü wird geschlossen.");
|
||||||
}
|
}
|
||||||
store.dispatch(closePolylineContextMenu());
|
store.dispatch(closePolylineContextMenu());
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { updatePolylineCoordinatesThunk } from "../../redux/thunks/database/poly
|
|||||||
import { openInNewTab } from "../../utils/openInNewTab";
|
import { openInNewTab } from "../../utils/openInNewTab";
|
||||||
|
|
||||||
//--------------------------------------------
|
//--------------------------------------------
|
||||||
export const setupPolylines = (
|
export const setupPolylines = async (
|
||||||
map,
|
map,
|
||||||
linePositions,
|
linePositions,
|
||||||
lineColors,
|
lineColors,
|
||||||
@@ -29,20 +29,23 @@ export const setupPolylines = (
|
|||||||
polylineVisible
|
polylineVisible
|
||||||
) => {
|
) => {
|
||||||
const mode = process.env.NEXT_PUBLIC_API_PORT_MODE;
|
const mode = process.env.NEXT_PUBLIC_API_PORT_MODE;
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
let basePath = "";
|
||||||
if (!polylineVisible) {
|
try {
|
||||||
//console.warn("Polylines deaktiviert - keine Zeichnung");
|
const res = await fetch("/config.json");
|
||||||
return { markers: [], polylines: [] };
|
if (res.ok) {
|
||||||
}
|
const config = await res.json();
|
||||||
|
basePath = config.basePath || "";
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
if (!polylineVisible) {
|
if (!polylineVisible) {
|
||||||
// Entferne alle Polylinien, wenn sie ausgeblendet werden sollen
|
// Entferne alle Polylinien, wenn sie ausgeblendet werden sollen
|
||||||
if (window.polylines) {
|
if (window.polylines) {
|
||||||
window.polylines.forEach(polyline => {
|
window.polylines.forEach(polyline => {
|
||||||
if (map.hasLayer(polyline)) {
|
if (map && map.hasLayer(polyline)) {
|
||||||
map.removeLayer(polyline);
|
map.removeLayer(polyline);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
window.polylines = [];
|
||||||
}
|
}
|
||||||
return { markers: [], polylines: [] };
|
return { markers: [], polylines: [] };
|
||||||
}
|
}
|
||||||
@@ -110,7 +113,7 @@ export const setupPolylines = (
|
|||||||
.dispatch(updatePolylineCoordinatesThunk(requestData))
|
.dispatch(updatePolylineCoordinatesThunk(requestData))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("Koordinaten erfolgreich aktualisiert:", data);
|
console.log("Koordinaten erfolgreich aktualisiert:", data);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
// utils/setupDevices.js
|
// utils/setupDevices.js
|
||||||
|
|
||||||
import { setSelectedDevice, clearSelectedDevice } from "../redux/slices/selectedDeviceSlice";
|
import { setSelectedDevice, clearSelectedDevice } from "../redux/slices/selectedDeviceSlice";
|
||||||
|
import { getDebugLog } from "./configUtils";
|
||||||
|
|
||||||
export const setupDevices = async (map, deviceMarkers, dispatch) => {
|
export const setupDevices = async (map, deviceMarkers, dispatch) => {
|
||||||
for (const marker of deviceMarkers) {
|
for (const marker of deviceMarkers) {
|
||||||
marker.on("mouseover", function () {
|
marker.on("mouseover", function () {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("✅ Gerät ausgewählt:", marker);
|
console.log("✅ Gerät ausgewählt:", marker);
|
||||||
}
|
}
|
||||||
dispatch(setSelectedDevice(marker.options)); // Gerät in Redux speichern
|
dispatch(setSelectedDevice(marker.options)); // Gerät in Redux speichern
|
||||||
});
|
});
|
||||||
|
|
||||||
marker.on("mouseout", function () {
|
marker.on("mouseout", function () {
|
||||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
if (getDebugLog()) {
|
||||||
console.log("❌ Gerät abgewählt");
|
console.log("❌ Gerät abgewählt");
|
||||||
}
|
}
|
||||||
dispatch(clearSelectedDevice()); // Gerät aus Redux entfernen
|
dispatch(clearSelectedDevice()); // Gerät aus Redux entfernen
|
||||||
|
|||||||
@@ -15,29 +15,71 @@ export const zoomIn = (e, map) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentZoom = map.getZoom();
|
let maxZoom = 19;
|
||||||
|
try {
|
||||||
|
if (window && window.__tileSourceMaxZoom !== undefined) {
|
||||||
|
maxZoom = window.__tileSourceMaxZoom;
|
||||||
|
} else {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/config.json", false);
|
||||||
|
xhr.send(null);
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
const config = JSON.parse(xhr.responseText);
|
||||||
|
if (config.tileSources && config.active && config.tileSources[config.active]) {
|
||||||
|
maxZoom = config.tileSources[config.active].maxZoom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
if (currentZoom < 14) {
|
const currentZoom = map.getZoom();
|
||||||
map.flyTo(e.latlng, 14);
|
if (currentZoom < maxZoom) {
|
||||||
localStorage.setItem("mapZoom", 16);
|
map.flyTo(e.latlng, currentZoom + 1);
|
||||||
|
localStorage.setItem("mapZoom", currentZoom + 1);
|
||||||
localStorage.setItem("mapCenter", JSON.stringify(map.getCenter()));
|
localStorage.setItem("mapCenter", JSON.stringify(map.getCenter()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const zoomOut = (map) => {
|
export const zoomOut = map => {
|
||||||
if (!map) {
|
if (!map) {
|
||||||
console.error("map is not defined in zoomOut");
|
console.error("map is not defined in zoomOut");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let minZoom = 5;
|
||||||
|
try {
|
||||||
|
if (window && window.__tileSourceMinZoom !== undefined) {
|
||||||
|
minZoom = window.__tileSourceMinZoom;
|
||||||
|
} else {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/config.json", false);
|
||||||
|
xhr.send(null);
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
const config = JSON.parse(xhr.responseText);
|
||||||
|
if (config.tileSources && config.active && config.tileSources[config.active]) {
|
||||||
|
minZoom = config.tileSources[config.active].minZoom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
const currentZoom = map.getZoom();
|
const currentZoom = map.getZoom();
|
||||||
|
if (currentZoom > minZoom) {
|
||||||
|
let zoomOutCenter = [51.41321407879154, 7.739617925303934];
|
||||||
|
try {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/config.json", false);
|
||||||
|
xhr.send(null);
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
const config = JSON.parse(xhr.responseText);
|
||||||
|
if (Array.isArray(config.zoomOutCenter) && config.zoomOutCenter.length === 2) {
|
||||||
|
zoomOutCenter = config.zoomOutCenter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
const zoom = minZoom;
|
||||||
|
|
||||||
if (currentZoom > 7) {
|
map.flyTo(zoomOutCenter, zoom);
|
||||||
const x = 51.41321407879154;
|
|
||||||
const y = 7.739617925303934;
|
|
||||||
const zoom = 7;
|
|
||||||
|
|
||||||
map.flyTo([x, y], zoom);
|
|
||||||
localStorage.setItem("mapZoom", zoom);
|
localStorage.setItem("mapZoom", zoom);
|
||||||
localStorage.setItem("mapCenter", JSON.stringify(map.getCenter()));
|
localStorage.setItem("mapCenter", JSON.stringify(map.getCenter()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,153 +1,85 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"LD_Name": "CPL Ismail4",
|
"LD_Name": "CPL Kai Schmidt",
|
||||||
"IdLD": 50922,
|
"IdLD": 50977,
|
||||||
"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=50977",
|
||||||
"Location_Name": "Littwin",
|
"Location_Name": "Littwin",
|
||||||
"Location_Short": "LTW",
|
"Location_Short": "LTW",
|
||||||
"IdLocation": 24101,
|
"IdLocation": 24101,
|
||||||
"Area_Name": "Rastede",
|
"Area_Name": "Rastede 2",
|
||||||
"Area_Short": "",
|
"Area_Short": "",
|
||||||
"IdArea": 20998,
|
"IdArea": 20998,
|
||||||
"X": 53.242157,
|
"X": 53.245957,
|
||||||
"Y": 8.160353,
|
"Y": 8.164172,
|
||||||
"Icon": 20,
|
"Icon": 20,
|
||||||
"System": 1,
|
"System": 1,
|
||||||
"Active": 1
|
"Active": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"LD_Name": "LR 77 ISA",
|
"LD_Name": "GMA-isa-test",
|
||||||
"IdLD": 50935,
|
"IdLD": 50981,
|
||||||
"Device": "LTE Modem LR77",
|
"Device": "Glättemeldeanlage",
|
||||||
"Link": "lr77.aspx?ver=1&id=50935",
|
"Link": "gma.aspx?ver=1&id=50981",
|
||||||
"Location_Name": "Littwin",
|
"Location_Name": "Littwin",
|
||||||
"Location_Short": "LTW",
|
"Location_Short": "LTW",
|
||||||
"IdLocation": 24101,
|
"IdLocation": 24101,
|
||||||
"Area_Name": "Rastede",
|
"Area_Name": "Rastede 2",
|
||||||
"Area_Short": "",
|
"Area_Short": "",
|
||||||
"IdArea": 20998,
|
"IdArea": 20998,
|
||||||
"X": 53.242157,
|
"X": 53.245957,
|
||||||
"Y": 8.160353,
|
"Y": 8.164172,
|
||||||
"Icon": 12,
|
|
||||||
"System": 5,
|
|
||||||
"Active": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"LD_Name": "Cisco Router 1841",
|
|
||||||
"IdLD": 50936,
|
|
||||||
"Device": "Cisco 1841",
|
|
||||||
"Link": "cisco1841.aspx?ver=1&id=50936",
|
|
||||||
"Location_Name": "Littwin",
|
|
||||||
"Location_Short": "LTW",
|
|
||||||
"IdLocation": 24101,
|
|
||||||
"Area_Name": "Rastede",
|
|
||||||
"Area_Short": "",
|
|
||||||
"IdArea": 20998,
|
|
||||||
"X": 53.242157,
|
|
||||||
"Y": 8.160353,
|
|
||||||
"Icon": 21,
|
|
||||||
"System": 6,
|
|
||||||
"Active": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"LD_Name": "GMA Testgerät ISA",
|
|
||||||
"IdLD": 50937,
|
|
||||||
"Device": "Glättemeldeanlage",
|
|
||||||
"Link": "gma.aspx?ver=1&id=50937",
|
|
||||||
"Location_Name": "Littwin",
|
|
||||||
"Location_Short": "LTW",
|
|
||||||
"IdLocation": 24101,
|
|
||||||
"Area_Name": "Rastede",
|
|
||||||
"Area_Short": "",
|
|
||||||
"IdArea": 20998,
|
|
||||||
"X": 53.242157,
|
|
||||||
"Y": 8.160353,
|
|
||||||
"Icon": 1,
|
"Icon": 1,
|
||||||
"System": 11,
|
"System": 11,
|
||||||
"Active": 1
|
"Active": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"LD_Name": "SMS-Funkmodem",
|
"LD_Name": "Cisco-isa",
|
||||||
"IdLD": 50938,
|
"IdLD": 50982,
|
||||||
"Device": "SMS Funkmodem",
|
"Device": "Cisco 1841",
|
||||||
"Link": "sms_modem.aspx?ver=1&id=50938",
|
"Link": "cisco1841.aspx?ver=1&id=50982",
|
||||||
"Location_Name": "Littwin",
|
"Location_Name": "Littwin",
|
||||||
"Location_Short": "LTW",
|
"Location_Short": "LTW",
|
||||||
"IdLocation": 24101,
|
"IdLocation": 24101,
|
||||||
"Area_Name": "Rastede",
|
"Area_Name": "Rastede 2",
|
||||||
"Area_Short": "",
|
"Area_Short": "",
|
||||||
"IdArea": 20998,
|
"IdArea": 20998,
|
||||||
"X": 53.242157,
|
"X": 53.245957,
|
||||||
"Y": 8.160353,
|
"Y": 8.164172,
|
||||||
"Icon": 12,
|
"Icon": 21,
|
||||||
"System": 111,
|
"System": 6,
|
||||||
"Active": 1
|
"Active": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"LD_Name": "TALAS Meldestationen ISA",
|
"LD_Name": "CPL Ganz neu",
|
||||||
"IdLD": 50939,
|
"IdLD": 50984,
|
||||||
"Device": "CPL V3.5 mit 16 Kü",
|
"Device": "CPL V3.5 mit 24 Kü",
|
||||||
"Link": "cpl.aspx?ver=35&kue=16&id=50939",
|
"Link": "cpl.aspx?ver=35&kue=24&id=50984",
|
||||||
"Location_Name": "Littwin",
|
"Location_Name": "Littwin",
|
||||||
"Location_Short": "LTW",
|
"Location_Short": "LTW",
|
||||||
"IdLocation": 24101,
|
"IdLocation": 24101,
|
||||||
"Area_Name": "Rastede",
|
"Area_Name": "Rastede 2",
|
||||||
"Area_Short": "",
|
"Area_Short": "",
|
||||||
"IdArea": 20998,
|
"IdArea": 20998,
|
||||||
"X": 53.242157,
|
"X": 53.245957,
|
||||||
"Y": 8.160353,
|
"Y": 8.164172,
|
||||||
"Icon": 20,
|
"Icon": 20,
|
||||||
"System": 1,
|
"System": 1,
|
||||||
"Active": 1
|
"Active": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"LD_Name": "WAGO Klemmen ISA",
|
"LD_Name": "Entwicklung KAS Cisco 8200 für LVZ 2025",
|
||||||
"IdLD": 50941,
|
"IdLD": 50986,
|
||||||
"Device": "WAGO 16 DE",
|
|
||||||
"Link": "wago.aspx?ver=1&DE=16&id=50941",
|
|
||||||
"Location_Name": "Littwin",
|
|
||||||
"Location_Short": "LTW",
|
|
||||||
"IdLocation": 24101,
|
|
||||||
"Area_Name": "Rastede",
|
|
||||||
"Area_Short": "",
|
|
||||||
"IdArea": 20998,
|
|
||||||
"X": 53.242157,
|
|
||||||
"Y": 8.160353,
|
|
||||||
"Icon": 9,
|
|
||||||
"System": 7,
|
|
||||||
"Active": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"LD_Name": "Cisco 1921",
|
|
||||||
"IdLD": 50942,
|
|
||||||
"Device": "Cisco 1921",
|
|
||||||
"Link": "cisco1921.aspx?ver=1&id=50942",
|
|
||||||
"Location_Name": "Littwin",
|
|
||||||
"Location_Short": "LTW",
|
|
||||||
"IdLocation": 24101,
|
|
||||||
"Area_Name": "Rastede",
|
|
||||||
"Area_Short": "",
|
|
||||||
"IdArea": 20998,
|
|
||||||
"X": 53.242157,
|
|
||||||
"Y": 8.160353,
|
|
||||||
"Icon": 21,
|
|
||||||
"System": 6,
|
|
||||||
"Active": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"LD_Name": "Cisco 8200",
|
|
||||||
"IdLD": 50943,
|
|
||||||
"Device": "Cisco 8200",
|
"Device": "Cisco 8200",
|
||||||
"Link": "cisco8200.aspx?ver=1&id=50943",
|
"Link": "cisco8200.aspx?ver=1&id=50986",
|
||||||
"Location_Name": "Littwin",
|
"Location_Name": "SW-Entwicklung 1.01",
|
||||||
"Location_Short": "LTW",
|
"Location_Short": "SW101",
|
||||||
"IdLocation": 24101,
|
"IdLocation": 24102,
|
||||||
"Area_Name": "Rastede",
|
"Area_Name": "Rastede",
|
||||||
"Area_Short": "",
|
"Area_Short": "RAST-007",
|
||||||
"IdArea": 20998,
|
"IdArea": 18192,
|
||||||
"X": 53.242157,
|
"X": 53.24615,
|
||||||
"Y": 8.160353,
|
"Y": 8.16237,
|
||||||
"Icon": 21,
|
"Icon": 21,
|
||||||
"System": 6,
|
"System": 6,
|
||||||
"Active": 1
|
"Active": 1
|
||||||
|
|||||||
@@ -4,25 +4,7 @@
|
|||||||
"Na": "system",
|
"Na": "system",
|
||||||
"Le": 4,
|
"Le": 4,
|
||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 01 kommend test2",
|
"Me": "Eingang DE 01 kommend",
|
||||||
"Feld": 4,
|
|
||||||
"Icon": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"IdLD": 50922,
|
|
||||||
"Na": "system",
|
|
||||||
"Le": 4,
|
|
||||||
"Co": "#FF00FF",
|
|
||||||
"Me": "Eingang DE 05 kommend",
|
|
||||||
"Feld": 4,
|
|
||||||
"Icon": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"IdLD": 50922,
|
|
||||||
"Na": "system",
|
|
||||||
"Le": 4,
|
|
||||||
"Co": "#FF00FF",
|
|
||||||
"Me": "Eingang DE 17 kommend",
|
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0
|
||||||
},
|
},
|
||||||
@@ -40,10 +22,127 @@
|
|||||||
"Na": "system",
|
"Na": "system",
|
||||||
"Le": 4,
|
"Le": 4,
|
||||||
"Co": "#FF00FF",
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Eingang DE 17 kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50922,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Eingang DE 05 kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50984,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Eingang DE 20 kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50975,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
"Me": "Eingang DE 32 kommend",
|
"Me": "Eingang DE 32 kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50984,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Eingang DE 01 kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50977,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Station offline",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50977,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Eingang DE 01 kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50975,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Station offline",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50066,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "CPL offline",
|
||||||
|
"Feld": 5,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50011,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "CPL offline",
|
||||||
|
"Feld": 16,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50011,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Wasserdruck aus",
|
||||||
|
"Feld": 16,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50011,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Ein",
|
||||||
|
"Feld": 16,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50011,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Digitaleingang 1 ON",
|
||||||
|
"Feld": 16,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50000,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Ein",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
"Na": "system",
|
"Na": "system",
|
||||||
@@ -53,6 +152,51 @@
|
|||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50922,
|
||||||
|
"Na": "system",
|
||||||
|
"Le": 4,
|
||||||
|
"Co": "#FF00FF",
|
||||||
|
"Me": "Eingang DE 32 kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50975,
|
||||||
|
"Na": "minor",
|
||||||
|
"Le": 3,
|
||||||
|
"Co": "#FFFF00",
|
||||||
|
"Me": "KÜG 07: Überspannung kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50975,
|
||||||
|
"Na": "minor",
|
||||||
|
"Le": 3,
|
||||||
|
"Co": "#FFFF00",
|
||||||
|
"Me": "KÜG 08: Überspannung gehend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50977,
|
||||||
|
"Na": "minor",
|
||||||
|
"Le": 3,
|
||||||
|
"Co": "#FFFF00",
|
||||||
|
"Me": "KÜG 08: Überspannung gehend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50984,
|
||||||
|
"Na": "minor",
|
||||||
|
"Le": 3,
|
||||||
|
"Co": "#FFFF00",
|
||||||
|
"Me": "KÜG 08: Überspannung gehend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"IdLD": 50922,
|
"IdLD": 50922,
|
||||||
"Na": "minor",
|
"Na": "minor",
|
||||||
@@ -76,7 +220,16 @@
|
|||||||
"Na": "major",
|
"Na": "major",
|
||||||
"Le": 2,
|
"Le": 2,
|
||||||
"Co": "#FF9900",
|
"Co": "#FF9900",
|
||||||
"Me": "Eingang DE 03 kommend",
|
"Me": "Eingang DE 03 Test Karte kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50922,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 01: Aderbruch kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0
|
||||||
},
|
},
|
||||||
@@ -97,5 +250,167 @@
|
|||||||
"Me": "KÜG 03: Aderbruch kommend",
|
"Me": "KÜG 03: Aderbruch kommend",
|
||||||
"Feld": 4,
|
"Feld": 4,
|
||||||
"Icon": 0
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50000,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "über 8V kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50984,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 05: Aderbruch kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50984,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 02: Isolationsminderung kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50984,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 06: Aderbruch kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50977,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 01: Isolationsminderung kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50977,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 06: Aderbruch kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50977,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 05: Aderbruch kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50976,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "CPL offline",
|
||||||
|
"Feld": 3,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50976,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 03: Isolationsminderung kommend",
|
||||||
|
"Feld": 3,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50975,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 04: Isolationsminderung kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50975,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 02: Isolationsminderung kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50975,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 01: Isolationsminderung kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50001,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "Sammelstörung kommend",
|
||||||
|
"Feld": 5,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50975,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 06: Aderbruch kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50975,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "KÜG 05: Aderbruch kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50963,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "CPL offline",
|
||||||
|
"Feld": 3,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50063,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "Digitaleingang 1 EIN",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdLD": 50000,
|
||||||
|
"Na": "critical",
|
||||||
|
"Le": 1,
|
||||||
|
"Co": "#FF0000",
|
||||||
|
"Me": "über 10V kommend",
|
||||||
|
"Feld": 4,
|
||||||
|
"Icon": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -4,111 +4,303 @@
|
|||||||
"Name": "TALAS",
|
"Name": "TALAS",
|
||||||
"Longname": "Talas Meldestationen",
|
"Longname": "Talas Meldestationen",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 1
|
"Icon": 20,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 1,
|
||||||
|
"Name": "TALAS",
|
||||||
|
"Longname": "Talas Meldestationen",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 20,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 2,
|
"IdSystem": 2,
|
||||||
"Name": "ECI",
|
"Name": "ECI",
|
||||||
"Longname": "ECI Geräte",
|
"Longname": "ECI Geräte",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 17,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 3,
|
||||||
|
"Name": "ULAF",
|
||||||
|
"Longname": "ULAF Geräte",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 2
|
"Icon": 14,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 3,
|
"IdSystem": 3,
|
||||||
"Name": "ULAF",
|
"Name": "ULAF",
|
||||||
"Longname": "ULAF Geräte",
|
"Longname": "ULAF Geräte",
|
||||||
"Allow": 0,
|
"Allow": 0,
|
||||||
"Icon": 3
|
"Icon": 14,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 5,
|
"IdSystem": 5,
|
||||||
"Name": "GSM Modem",
|
"Name": "GSM Modem",
|
||||||
"Longname": "LR77 GSM Modems",
|
"Longname": "LR77 GSM Modems",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 5
|
"Icon": 12,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 5,
|
||||||
|
"Name": "GSM Modem",
|
||||||
|
"Longname": "LR77 GSM Modems",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 12,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 6,
|
||||||
|
"Name": "Cisco Router",
|
||||||
|
"Longname": "Cisco Router",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 21,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 6,
|
"IdSystem": 6,
|
||||||
"Name": "Cisco Router",
|
"Name": "Cisco Router",
|
||||||
"Longname": "Cisco Router",
|
"Longname": "Cisco Router",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 6
|
"Icon": 21,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 6,
|
||||||
|
"Name": "Cisco Router",
|
||||||
|
"Longname": "Cisco Router",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 21,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 7,
|
"IdSystem": 7,
|
||||||
"Name": "WAGO",
|
"Name": "WAGO",
|
||||||
"Longname": "WAGO I/O Systeme",
|
"Longname": "WAGO I/O Systeme",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 7
|
"Icon": 9,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 7,
|
||||||
|
"Name": "WAGO",
|
||||||
|
"Longname": "WAGO I/O Systeme",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 9,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 7,
|
||||||
|
"Name": "WAGO",
|
||||||
|
"Longname": "WAGO I/O Systeme",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 9,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 7,
|
||||||
|
"Name": "WAGO",
|
||||||
|
"Longname": "WAGO I/O Systeme",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 9,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 8,
|
"IdSystem": 8,
|
||||||
"Name": "Siemens",
|
"Name": "Siemens",
|
||||||
"Longname": "Siemens Notrufsysteme",
|
"Longname": "Siemens Notrufsysteme",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 8
|
"Icon": 19,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 8,
|
||||||
|
"Name": "Siemens",
|
||||||
|
"Longname": "Siemens Notrufsysteme",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 19,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 9,
|
"IdSystem": 9,
|
||||||
"Name": "OTDR",
|
"Name": "OTDR",
|
||||||
"Longname": "Glasfaserüberwachung OTU",
|
"Longname": "Glasfaserüberwachung OTU",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 9
|
"Icon": 24,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 9,
|
||||||
|
"Name": "OTDR",
|
||||||
|
"Longname": "Glasfaserüberwachung OTU",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 24,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 9,
|
||||||
|
"Name": "OTDR",
|
||||||
|
"Longname": "Glasfaserüberwachung OTU",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 24,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 9,
|
||||||
|
"Name": "OTDR",
|
||||||
|
"Longname": "Glasfaserüberwachung OTU",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 24,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 10,
|
"IdSystem": 10,
|
||||||
"Name": "WDM",
|
"Name": "WDM",
|
||||||
"Longname": " Wavelength Division Multiplexing",
|
"Longname": " Wavelength Division Multiplexing",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 10
|
"Icon": 24,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 10,
|
||||||
|
"Name": "WDM",
|
||||||
|
"Longname": " Wavelength Division Multiplexing",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 24,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 10,
|
||||||
|
"Name": "WDM",
|
||||||
|
"Longname": " Wavelength Division Multiplexing",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 24,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 11,
|
"IdSystem": 11,
|
||||||
"Name": "GMA",
|
"Name": "GMA",
|
||||||
"Longname": "Glättemeldeanlagen",
|
"Longname": "Glättemeldeanlagen",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 11
|
"Icon": 1,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 11,
|
||||||
|
"Name": "GMA",
|
||||||
|
"Longname": "Glättemeldeanlagen",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 1,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 11,
|
||||||
|
"Name": "GMA",
|
||||||
|
"Longname": "Glättemeldeanlagen",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 1,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 13,
|
"IdSystem": 13,
|
||||||
"Name": "Messstellen",
|
"Name": "Messstellen",
|
||||||
"Longname": "Messstellen",
|
"Longname": "Messstellen",
|
||||||
"Allow": 1,
|
"Allow": 0,
|
||||||
"Icon": 13
|
"Icon": 23,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 30,
|
"IdSystem": 30,
|
||||||
"Name": "TK-Komponenten",
|
"Name": "TK-Komponenten",
|
||||||
"Longname": "TK-Komponenten",
|
"Longname": "TK-Komponenten",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 30
|
"Icon": 14,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 30,
|
||||||
|
"Name": "TK-Komponenten",
|
||||||
|
"Longname": "TK-Komponenten",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 14,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 100,
|
"IdSystem": 100,
|
||||||
"Name": "TALAS ICL",
|
"Name": "TALAS ICL",
|
||||||
"Longname": "Talas ICL Unterstationen",
|
"Longname": "Talas ICL Unterstationen",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 100
|
"Icon": 23,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 100,
|
||||||
|
"Name": "TALAS ICL",
|
||||||
|
"Longname": "Talas ICL Unterstationen",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 23,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 110,
|
||||||
|
"Name": "DAUZ",
|
||||||
|
"Longname": "Dauerzählstellen",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 14,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 110,
|
"IdSystem": 110,
|
||||||
"Name": "DAUZ",
|
"Name": "DAUZ",
|
||||||
"Longname": "Dauerzählstellen",
|
"Longname": "Dauerzählstellen",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 110
|
"Icon": 14,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 110,
|
||||||
|
"Name": "DAUZ",
|
||||||
|
"Longname": "Dauerzählstellen",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 14,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 111,
|
"IdSystem": 111,
|
||||||
"Name": "SMS Modem",
|
"Name": "SMS Modem",
|
||||||
"Longname": "SMS Modem",
|
"Longname": "SMS Modem",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 111
|
"Icon": 12,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 111,
|
||||||
|
"Name": "SMS Modem",
|
||||||
|
"Longname": "SMS Modem",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 12,
|
||||||
|
"Map": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSystem": 200,
|
"IdSystem": 200,
|
||||||
"Name": "Sonstige",
|
"Name": "Sonstige",
|
||||||
"Longname": "Sonstige",
|
"Longname": "Sonstige",
|
||||||
"Allow": 1,
|
"Allow": 1,
|
||||||
"Icon": 200
|
"Icon": 31,
|
||||||
|
"Map": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSystem": 200,
|
||||||
|
"Name": "Sonstige",
|
||||||
|
"Longname": "Sonstige",
|
||||||
|
"Allow": 0,
|
||||||
|
"Icon": 31,
|
||||||
|
"Map": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||