Compare commits
56 Commits
feat/docs
...
4d2a94ffea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
|
||||
|
||||
# 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.)
|
||||
# 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.
|
||||
# z.B. http://10.10.0.13/talas5/index.aspx -> NEXT_PUBLIC_BASE_PATH=/talas5
|
||||
# z.B. http://10.10.0.13/xyz/index.aspx -> NEXT_PUBLIC_BASE_PATH=/xyz
|
||||
NEXT_PUBLIC_BASE_PATH=/talas5
|
||||
# Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH=
|
||||
# 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 -> basePath in config.json auf /xyz setzen
|
||||
# basePath wird jetzt in public/config.json gepflegt
|
||||
# App-Versionsnummer
|
||||
NEXT_PUBLIC_APP_VERSION=1.1.300
|
||||
NEXT_PUBLIC_APP_VERSION=1.1.355
|
||||
|
||||
@@ -7,7 +7,6 @@ DB_NAME=talas_v5
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -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.)
|
||||
# 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.
|
||||
# z.B. http://10.10.0.13/talas5/index.aspx -> NEXT_PUBLIC_BASE_PATH=/talas5
|
||||
# z.B. http://10.10.0.13/xyz/index.aspx -> NEXT_PUBLIC_BASE_PATH=/xyz
|
||||
NEXT_PUBLIC_BASE_PATH=/talas5
|
||||
# Oder leer lassen für direkten Zugriff -> NEXT_PUBLIC_BASE_PATH=
|
||||
# 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 -> basePath in config.json auf /xyz setzen
|
||||
# basePath wird jetzt in public/config.json gepflegt
|
||||
|
||||
# App-Versionsnummer
|
||||
NEXT_PUBLIC_APP_VERSION=1.1.300
|
||||
NEXT_PUBLIC_APP_VERSION=1.1.355
|
||||
|
||||
@@ -250,7 +250,7 @@ das Objekt selbst
|
||||
### ♻️ Refactor
|
||||
|
||||
- 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
|
||||
|
||||
### 🧠 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
|
||||
|
||||
### Voraussetzungen
|
||||
@@ -61,8 +85,8 @@ User-ID.
|
||||
(Server-IP mit Port 3000)
|
||||
- Browser: Chrome ab Version 125.0.6420.142 empfohlen
|
||||
- Karten Material vorhanden in: `C:\inetpub\wwwroot\talas5\TileMap\mapTiles`
|
||||

|
||||
Falls nicht vorhanden hier downloaden: http://10.10.0.28/produkte/TALAS.map/mapTiles.zip
|
||||
 Falls nicht vorhanden hier downloaden:
|
||||
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
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import L from "leaflet";
|
||||
@@ -22,7 +23,13 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => {
|
||||
const updateMarkersVisibility = () => {
|
||||
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);
|
||||
|
||||
if (areAllLayersInvisible === prevVisibility.current) return;
|
||||
@@ -42,7 +49,8 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => {
|
||||
updateMarkersVisibility();
|
||||
|
||||
const handleStorageChange = event => {
|
||||
if (event.key === "mapLayersVisibility") {
|
||||
// Überwache sowohl den alten als auch kartenspezifische Keys
|
||||
if (event.key === "mapLayersVisibility" || event.key?.startsWith("mapLayersVisibility_")) {
|
||||
updateMarkersVisibility();
|
||||
}
|
||||
};
|
||||
@@ -64,10 +72,11 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => {
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const editMode = localStorage.getItem("editMode") === "true";
|
||||
const markers = data.map(item => {
|
||||
const marker = L.marker([item.x, item.y], {
|
||||
icon: customIcon,
|
||||
draggable: true,
|
||||
draggable: editMode,
|
||||
customType: "areaMarker",
|
||||
});
|
||||
|
||||
@@ -81,24 +90,26 @@ const useAreaMarkersLayer = (map, oms, apiUrl, onUpdateSuccess) => {
|
||||
}
|
||||
);
|
||||
|
||||
marker.on("dragend", async e => {
|
||||
const { lat, lng } = e.target.getLatLng();
|
||||
try {
|
||||
await dispatch(
|
||||
updateAreaThunk({
|
||||
idLocation: item.idLocation,
|
||||
idMap: item.idMaps,
|
||||
newCoords: { x: lat, y: lng },
|
||||
})
|
||||
).unwrap();
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
console.log("✔️ Koordinaten erfolgreich aktualisiert:", { lat, lng });
|
||||
if (editMode) {
|
||||
marker.on("dragend", async e => {
|
||||
const { lat, lng } = e.target.getLatLng();
|
||||
try {
|
||||
await dispatch(
|
||||
updateAreaThunk({
|
||||
idLocation: item.idLocation,
|
||||
idMap: item.idMaps,
|
||||
newCoords: { x: lat, y: lng },
|
||||
})
|
||||
).unwrap();
|
||||
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;
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getDebugLog } from "@/utils/configUtils.js";
|
||||
// components/contextmenu/useMapContextMenu.js
|
||||
import { toast } from "react-toastify";
|
||||
import { zoomIn, zoomOut, centerHere } from "../../utils/zoomAndCenterUtils";
|
||||
@@ -71,7 +72,7 @@ const addItemsToMapContextMenu = (
|
||||
if (!menuItemAdded && map && map.contextmenu) {
|
||||
const editMode = localStorage.getItem("editMode") === "true";
|
||||
if (editMode) {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("editMode localStorage:", localStorage.getItem("editMode"));
|
||||
}
|
||||
|
||||
|
||||
@@ -30,30 +30,30 @@ const useDynamicDeviceLayers = (map, GisSystemStatic, mapLayersVisibility, prior
|
||||
if (!map || GisSystemStatic.length === 0) return;
|
||||
|
||||
GisSystemStatic.forEach(({ Name, IdSystem }) => {
|
||||
const key = `system-${IdSystem}`; // Einheitlicher Key
|
||||
|
||||
if (!layerRefs.current[key]) {
|
||||
layerRefs.current[key] = new L.LayerGroup().addTo(map);
|
||||
const key = `system-${IdSystem}`;
|
||||
// LayerGroup immer komplett neu erstellen, um doppelte Marker zu verhindern
|
||||
if (layerRefs.current[key]) {
|
||||
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(
|
||||
IdSystem,
|
||||
newMarkers => {
|
||||
const oldMarkers = markerStates[key];
|
||||
|
||||
// Entferne alte Marker aus Karte und OMS
|
||||
if (oldMarkers && Array.isArray(oldMarkers)) {
|
||||
oldMarkers.forEach(marker => {
|
||||
if (map.hasLayer(marker)) {
|
||||
map.removeLayer(marker);
|
||||
}
|
||||
if (oms) {
|
||||
oms.removeMarker(marker);
|
||||
}
|
||||
// Füge neue Marker der LayerGroup hinzu (nur Geräte-Marker)
|
||||
if (layerRefs.current[key]) {
|
||||
layerRefs.current[key].clearLayers();
|
||||
// Nur eindeutige Marker hinzufügen
|
||||
const uniqueMarkers = Array.isArray(newMarkers) ? Array.from(new Set(newMarkers)) : [];
|
||||
uniqueMarkers.forEach(marker => {
|
||||
marker.addTo(layerRefs.current[key]);
|
||||
});
|
||||
}
|
||||
|
||||
// Neue Marker setzen
|
||||
setMarkerStates(prev => ({ ...prev, [key]: newMarkers }));
|
||||
},
|
||||
GisSystemStatic,
|
||||
@@ -69,20 +69,21 @@ const useDynamicDeviceLayers = (map, GisSystemStatic, mapLayersVisibility, prior
|
||||
if (!map) return;
|
||||
const editMode = localStorage.getItem("editMode") === "true";
|
||||
|
||||
Object.entries(markerStates).forEach(([key, markers]) => {
|
||||
const isVisible = mapLayersVisibility[key];
|
||||
markers.forEach(marker => {
|
||||
const hasLayer = map.hasLayer(marker);
|
||||
if (editMode || !isVisible) {
|
||||
if (hasLayer) map.removeLayer(marker);
|
||||
} else {
|
||||
if (!hasLayer) marker.addTo(map);
|
||||
Object.entries(layerRefs.current).forEach(([key, layerGroup]) => {
|
||||
const isVisible = mapLayersVisibility[key] ?? true;
|
||||
if (editMode || isVisible === false) {
|
||||
if (map.hasLayer(layerGroup)) {
|
||||
map.removeLayer(layerGroup);
|
||||
}
|
||||
});
|
||||
} 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();
|
||||
|
||||
checkOverlappingMarkers(map, allMarkers, plusRoundIcon);
|
||||
}, [map, markerStates, mapLayersVisibility]);
|
||||
|
||||
|
||||
0
components/hooks/useStationCache.js
Normal file
@@ -10,6 +10,9 @@ import { InformationCircleIcon } from "@heroicons/react/20/solid";
|
||||
import PoiUpdateModal from "@/components/pois/poiUpdateModal/PoiUpdateModal.js";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
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 addItemsToMapContextMenu from "@/components/contextmenu/useMapContextMenu.js";
|
||||
@@ -35,7 +38,7 @@ import { useSelector, useDispatch } from "react-redux";
|
||||
import { setSelectedPoi } from "@/redux/slices/database/pois/selectedPoiSlice.js";
|
||||
import { setDisabled } from "@/redux/slices/database/polylines/polylineEventsDisabledSlice.js";
|
||||
import { setMapId, setUserId } from "@/redux/slices/urlParameterSlice";
|
||||
import { selectMapLayersState } from "@/redux/slices/mapLayersSlice";
|
||||
import { selectMapLayersState, setLayerVisibility } from "@/redux/slices/mapLayersSlice";
|
||||
import { setCurrentPoi } from "@/redux/slices/database/pois/currentPoiSlice.js";
|
||||
import { selectGisLines } from "@/redux/slices/database/polylines/gisLinesSlice";
|
||||
import { selectGisLinesStatus } from "@/redux/slices/webservice/gisLinesStatusSlice";
|
||||
@@ -54,6 +57,7 @@ import {
|
||||
import {
|
||||
selectPolylineVisible,
|
||||
setPolylineVisible,
|
||||
initializePolylineFromLocalStorageThunk,
|
||||
} from "@/redux/slices/database/polylines/polylineLayerVisibleSlice.js";
|
||||
import { selectGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice.js";
|
||||
import {
|
||||
@@ -84,6 +88,7 @@ import { monitorHeapWithRedux } from "@/utils/common/monitorMemory";
|
||||
import { io } from "socket.io-client";
|
||||
|
||||
import { setGisStationsStaticDistrict } from "@/redux/slices/webservice/gisStationsStaticDistrictSlice.js";
|
||||
import { getDebugLog } from "../../utils/configUtils";
|
||||
//-----------------------------------------------------------------------------------------------------
|
||||
const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
//-------------------------------
|
||||
@@ -96,6 +101,14 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
const countdownActive = useSelector(state => state.polylineContextMenu.countdownActive);
|
||||
const isPolylineContextMenuOpen = useSelector(state => state.polylineContextMenu.isOpen);
|
||||
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 statusMeasurements = useSelector(state => state.gisStationsMeasurements.status);
|
||||
@@ -107,7 +120,6 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
const zoomTrigger = useSelector(state => state.zoomTrigger.trigger);
|
||||
const poiReadTrigger = useSelector(state => state.poiReadFromDbTrigger.trigger);
|
||||
const GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict);
|
||||
const GisSystemStatic = useSelector(selectGisSystemStatic);
|
||||
const gisSystemStaticStatus = useSelector(state => state.gisSystemStatic.status);
|
||||
const polylineEventsDisabled = useSelector(state => state.polylineEventsDisabled.disabled);
|
||||
const mapLayersVisibility = useSelector(selectMapLayersState) || {};
|
||||
@@ -140,6 +152,12 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
const [map, setMap] = useState(null); // Zustand der Karteninstanz
|
||||
const [oms, setOms] = useState(null); // State für OMS-Instanz
|
||||
|
||||
// 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----------------
|
||||
const isRightsLoaded = useSelector(
|
||||
state => state.gisUserRightsFromWebservice.status === "succeeded"
|
||||
@@ -200,6 +218,16 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
}
|
||||
};
|
||||
//-----------------------------Map Initialisierung----------------
|
||||
// Default map options for Leaflet
|
||||
const mapOptions = {
|
||||
center: currentCenter,
|
||||
zoom: currentZoom,
|
||||
zoomControl: true,
|
||||
contextmenu: true,
|
||||
contextmenuWidth: 180,
|
||||
contextmenuItems: [],
|
||||
};
|
||||
|
||||
useInitializeMap(
|
||||
map,
|
||||
mapRef,
|
||||
@@ -208,10 +236,85 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
setMenuItemAdded,
|
||||
addItemsToMapContextMenu,
|
||||
hasRights,
|
||||
value => dispatch(setDisabled(value))
|
||||
value => dispatch(setDisabled(value)),
|
||||
mapOptions // pass mapOptions
|
||||
);
|
||||
|
||||
//-------------------------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(() => {
|
||||
if (linesData && Array.isArray(linesData)) {
|
||||
const transformed = linesData.map(item => ({
|
||||
@@ -222,6 +325,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
setLinePositions(transformed);
|
||||
}
|
||||
}, [linesData]);
|
||||
|
||||
//--------------------------------------------
|
||||
useEffect(() => {
|
||||
dispatch(fetchPoiIconsDataThunk());
|
||||
@@ -309,72 +413,106 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
//Tooltip an mouse position anzeigen für die Linien
|
||||
useEffect(() => {
|
||||
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
|
||||
|
||||
markers.forEach(marker => {
|
||||
(Array.isArray(markers) ? markers : []).forEach(marker => {
|
||||
marker.remove();
|
||||
});
|
||||
cleanupPolylinesForMemory(polylines, map);
|
||||
console.log("[MapComponent/useEffect] Nach cleanupPolylinesForMemory, polylines:", polylines);
|
||||
|
||||
// Setze neue Marker und Polylinien mit den aktuellen Daten
|
||||
const { markers: newMarkers, polylines: newPolylines } = setupPolylines(
|
||||
map,
|
||||
linePositions,
|
||||
lineColors,
|
||||
tooltipContents,
|
||||
setNewCoords,
|
||||
tempMarker,
|
||||
currentZoom,
|
||||
currentCenter,
|
||||
polylineVisible // kommt aus Redux
|
||||
);
|
||||
// Setze neue Marker und Polylinien mit den aktuellen Daten (asynchron!)
|
||||
const updatePolylines = async () => {
|
||||
if (polylineVisible) {
|
||||
const { markers: newMarkers, polylines: newPolylines } = await setupPolylines(
|
||||
map,
|
||||
linePositions,
|
||||
lineColors,
|
||||
tooltipContents,
|
||||
setNewCoords,
|
||||
tempMarker,
|
||||
currentZoom,
|
||||
currentCenter,
|
||||
polylineVisible
|
||||
);
|
||||
|
||||
newPolylines.forEach((polyline, index) => {
|
||||
const tooltipContent =
|
||||
tooltipContents[`${linePositions[index].idLD}-${linePositions[index].idModul}`] ||
|
||||
"Die Linie ist noch nicht in Webservice vorhanden oder bekommt keine Daten";
|
||||
(Array.isArray(newPolylines) ? newPolylines : []).forEach((polyline, index) => {
|
||||
const tooltipContent =
|
||||
tooltipContents[`${linePositions[index].idLD}-${linePositions[index].idModul}`] ||
|
||||
"Die Linie ist noch nicht in Webservice vorhanden oder bekommt keine Daten";
|
||||
|
||||
polyline.bindTooltip(tooltipContent, {
|
||||
permanent: false,
|
||||
direction: "auto",
|
||||
sticky: true,
|
||||
offset: [20, 0],
|
||||
pane: "tooltipPane",
|
||||
});
|
||||
polyline.bindTooltip(tooltipContent, {
|
||||
permanent: false,
|
||||
direction: "auto",
|
||||
sticky: true,
|
||||
offset: [20, 0],
|
||||
pane: "tooltipPane",
|
||||
});
|
||||
|
||||
polyline.on("mouseover", e => {
|
||||
const tooltip = polyline.getTooltip();
|
||||
if (tooltip) {
|
||||
const mousePos = e.containerPoint;
|
||||
const mapSize = map.getSize();
|
||||
polyline.on("mouseover", e => {
|
||||
const tooltip = polyline.getTooltip();
|
||||
if (tooltip) {
|
||||
const mousePos = e.containerPoint;
|
||||
const mapSize = map.getSize();
|
||||
|
||||
let direction = "right";
|
||||
let direction = "right";
|
||||
|
||||
if (mousePos.x > mapSize.x - 100) {
|
||||
direction = "left";
|
||||
} else if (mousePos.x < 100) {
|
||||
direction = "right";
|
||||
}
|
||||
if (mousePos.x > mapSize.x - 100) {
|
||||
direction = "left";
|
||||
} else if (mousePos.x < 100) {
|
||||
direction = "right";
|
||||
}
|
||||
|
||||
if (mousePos.y > mapSize.y - 100) {
|
||||
direction = "top";
|
||||
} else if (mousePos.y < 100) {
|
||||
direction = "bottom";
|
||||
}
|
||||
if (mousePos.y > mapSize.y - 100) {
|
||||
direction = "top";
|
||||
} else if (mousePos.y < 100) {
|
||||
direction = "bottom";
|
||||
}
|
||||
|
||||
tooltip.options.direction = direction;
|
||||
polyline.openTooltip(e.latlng);
|
||||
tooltip.options.direction = direction;
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
polyline.on("mouseout", () => {
|
||||
polyline.closeTooltip();
|
||||
});
|
||||
});
|
||||
cleanupMarkers(markers, oms);
|
||||
setMarkers(newMarkers);
|
||||
setPolylines(newPolylines);
|
||||
cleanupPolylinesForMemory(polylines, map);
|
||||
setPolylines([]);
|
||||
console.log("[MapComponent/useEffect] setPolylines ([]), alle Polylinien entfernt");
|
||||
}
|
||||
};
|
||||
updatePolylines();
|
||||
}, [
|
||||
map,
|
||||
linePositions,
|
||||
@@ -384,6 +522,8 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
newCoords,
|
||||
tempMarker,
|
||||
polylineVisible,
|
||||
isTalasAllowed,
|
||||
poiLayerVisible,
|
||||
]);
|
||||
|
||||
//--------------------------------------------
|
||||
@@ -391,7 +531,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
//Test in useEffect
|
||||
useEffect(() => {
|
||||
if (map) {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("🗺️ Map-Einstellungen werden wiederhergestellt...");
|
||||
}
|
||||
restoreMapSettings(map);
|
||||
@@ -400,7 +540,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
//--------------------------------------------
|
||||
useEffect(() => {
|
||||
if (map) {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("map in MapComponent: ", map);
|
||||
}
|
||||
const handleMapMoveEnd = event => {
|
||||
@@ -433,7 +573,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
const station = points.find(s => s.Area_Name === selectedArea);
|
||||
|
||||
if (station) {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("📌 Gefundene Station:", station);
|
||||
}
|
||||
map.flyTo([station.X, station.Y], 14);
|
||||
@@ -479,7 +619,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
//--------------------------------------------
|
||||
useEffect(() => {
|
||||
if (map) {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("6- Karteninstanz (map) wurde jetzt erfolgreich initialisiert");
|
||||
}
|
||||
}
|
||||
@@ -492,7 +632,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
map.whenReady(() => {
|
||||
timeoutId = setTimeout(() => {
|
||||
if (map.contextmenu) {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("Contextmenu ist vorhanden");
|
||||
}
|
||||
} else {
|
||||
@@ -523,7 +663,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
const handleLocationUpdate = async (idLocation, idMap, newCoords) => {
|
||||
try {
|
||||
await dispatch(updateAreaThunk({ idLocation, idMap, newCoords })).unwrap();
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("Koordinaten erfolgreich aktualisiert:", result);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -547,14 +687,14 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
// Entferne alle Marker aus der Karte
|
||||
if (!map) return; // Sicherstellen, dass map existiert
|
||||
|
||||
areaMarkers.forEach(marker => {
|
||||
(Array.isArray(areaMarkers) ? areaMarkers : []).forEach(marker => {
|
||||
if (map.hasLayer(marker)) {
|
||||
map.removeLayer(marker);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 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)) {
|
||||
marker.addTo(map); // Layer hinzufügen
|
||||
}
|
||||
@@ -615,11 +755,10 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
}, [dispatch]);
|
||||
|
||||
//--------------------------------------------
|
||||
// Beim ersten Client-Render den Wert aus localStorage laden
|
||||
useEffect(() => {
|
||||
const storedPolylineVisible = localStorage.getItem("polylineVisible") === "true";
|
||||
dispatch(setPolylineVisible(storedPolylineVisible));
|
||||
}, [dispatch]);
|
||||
|
||||
// (Initialisierung erfolgt in MapLayersControlPanel)
|
||||
//--------------------------------------------
|
||||
// MapComponent reagiert nicht mehr direkt auf localStorage-Events für polylineVisible
|
||||
//--------------------------------------------
|
||||
useEffect(() => {
|
||||
if (statusStaticDistrict === "idle") {
|
||||
@@ -693,7 +832,7 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
// console.log(`⏳ Redux Countdown: ${countdown} Sekunden`);
|
||||
|
||||
if (countdown <= 2) {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("🚀 Kontextmenü wird wegen Countdown < 2 geschlossen.");
|
||||
}
|
||||
dispatch(closePolylineContextMenu());
|
||||
@@ -738,7 +877,9 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
useEffect(() => {
|
||||
if (poiTypStatus === "succeeded" && Array.isArray(poiTypData)) {
|
||||
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);
|
||||
}
|
||||
}, [poiTypData, poiTypStatus]);
|
||||
@@ -749,30 +890,6 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
}
|
||||
}, [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(() => {
|
||||
@@ -848,6 +965,21 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
}, [GisStationsStaticDistrict]);
|
||||
const { Points = [] } = useSelector(selectGisStationsStaticDistrict);
|
||||
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]);
|
||||
|
||||
//---------------------------------------------
|
||||
//--------------------------------------------
|
||||
return (
|
||||
@@ -904,7 +1036,10 @@ const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
|
||||
</div>
|
||||
|
||||
{GisStationsStaticDistrict && GisStationsStaticDistrict.Points?.length > 0 && (
|
||||
<MapLayersControlPanel className="z-50" />
|
||||
<MapLayersControlPanel
|
||||
className="z-50"
|
||||
handlePolylineCheckboxChange={handlePolylineCheckboxChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CoordinateInput onCoordinatesSubmit={handleCoordinatesSubmit} />
|
||||
|
||||
@@ -2,12 +2,58 @@
|
||||
import { useEffect } from "react";
|
||||
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(() => {
|
||||
if (mapRef.current && !map) {
|
||||
initializeMap(mapRef, setMap, setOms, setMenuItemAdded, addItemsToMapContextMenu, hasRights, setPolylineEventsDisabled);
|
||||
let cancelled = false;
|
||||
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;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// /components/uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js
|
||||
import { getDebugLog } from "../../../utils/configUtils";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { setSelectedArea } from "@/redux/slices/selectedAreaSlice";
|
||||
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 { incrementZoomTrigger } from "@/redux/slices/zoomTriggerSlice";
|
||||
|
||||
function MapLayersControlPanel() {
|
||||
function MapLayersControlPanel({ handlePolylineCheckboxChange }) {
|
||||
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 setPoiVisible = value => dispatch(setVisible(value));
|
||||
const dispatch = useDispatch();
|
||||
@@ -24,46 +27,60 @@ function MapLayersControlPanel() {
|
||||
const GisStationsStaticDistrict = useSelector(selectGisStationsStaticDistrict) || [];
|
||||
const GisSystemStatic = useSelector(selectGisSystemStatic) || [];
|
||||
|
||||
const polylineVisible = useSelector(selectPolylineVisible);
|
||||
// Debug: Kabelstrecken state verfolgen
|
||||
useEffect(() => {
|
||||
console.log("🎯 kabelstreckenVisible state changed to:", kabelstreckenVisible);
|
||||
}, [kabelstreckenVisible]);
|
||||
|
||||
const handlePolylineCheckboxChange = event => {
|
||||
const checked = event.target.checked;
|
||||
dispatch(setPolylineVisible(checked));
|
||||
localStorage.setItem("polylineVisible", checked);
|
||||
// Prüfen, ob TALAS (IdSystem 1) erlaubt & sichtbar auf Karte (Allow + Map)
|
||||
const isTalasAllowed = Array.isArray(GisSystemStatic)
|
||||
? GisSystemStatic.some(
|
||||
system => system.IdSystem === 1 && system.Allow === 1 && system.Map === 1
|
||||
)
|
||||
: false;
|
||||
|
||||
if (checked) {
|
||||
dispatch(setLayerVisibility({ layer: "TALAS", visibility: true }));
|
||||
localStorage.setItem(
|
||||
"mapLayersVisibility",
|
||||
JSON.stringify({ ...mapLayersVisibility, TALAS: true })
|
||||
);
|
||||
}
|
||||
};
|
||||
// handlePolylineCheckboxChange kommt jetzt als Prop von MapComponent
|
||||
|
||||
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");
|
||||
if (storedPoiVisible !== null) {
|
||||
setPoiVisible(storedPoiVisible === "true");
|
||||
}
|
||||
const storedPolylineVisible = localStorage.getItem("polylineVisible");
|
||||
if (storedPolylineVisible !== null) {
|
||||
dispatch(setPolylineVisible(storedPolylineVisible === "true"));
|
||||
}
|
||||
|
||||
// Layer-Sichtbarkeiten aus localStorage laden
|
||||
const storedMapLayersVisibility = 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 storedMapLayersVisibility = localStorage.getItem(mapStorageKey);
|
||||
if (storedMapLayersVisibility) {
|
||||
const parsedVisibility = JSON.parse(storedMapLayersVisibility);
|
||||
Object.keys(parsedVisibility).forEach(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");
|
||||
setEditMode(storedEditMode === "true");
|
||||
}, [setPoiVisible, dispatch]); // ✅ `setMapLayersVisibility` entfernt
|
||||
|
||||
setLocalStorageLoaded(true);
|
||||
}, []); // Läuft nur beim Mount
|
||||
|
||||
const handleAreaChange = event => {
|
||||
const selectedIndex = event.target.options.selectedIndex;
|
||||
@@ -72,8 +89,13 @@ function MapLayersControlPanel() {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Allowed systems jetzt nach Allow && Map filtern
|
||||
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();
|
||||
|
||||
const seenNames = new Set();
|
||||
@@ -97,7 +119,7 @@ function MapLayersControlPanel() {
|
||||
const seenSystemNames = new Set();
|
||||
const filteredSystems = Array.isArray(GisSystemStatic)
|
||||
? 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) {
|
||||
seenSystemNames.add(item.Name);
|
||||
}
|
||||
@@ -108,8 +130,8 @@ function MapLayersControlPanel() {
|
||||
setSystemListing(
|
||||
filteredSystems.map((system, index) => ({
|
||||
id: index + 1,
|
||||
name: system.Name, // Verwende den Originalnamen für die Anzeige
|
||||
key: `system-${system.IdSystem}`, // Internen Schlüssel für die MapLayersVisibility-Logik
|
||||
name: system.Name, // Anzeige
|
||||
key: `system-${system.IdSystem}`, // interner Schlüssel
|
||||
}))
|
||||
);
|
||||
}, [GisStationsStaticDistrict, GisSystemStatic]);
|
||||
@@ -117,13 +139,38 @@ function MapLayersControlPanel() {
|
||||
const handleCheckboxChange = (key, event) => {
|
||||
if (editMode) return;
|
||||
const { checked } = event.target;
|
||||
|
||||
dispatch(setLayerVisibility({ layer: key, visibility: checked }));
|
||||
localStorage.setItem(
|
||||
"mapLayersVisibility",
|
||||
JSON.stringify({ ...mapLayersVisibility, [key]: checked })
|
||||
// Debug-Ausgabe
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const mapId = params.get("m");
|
||||
const userId = params.get("u");
|
||||
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(() => {
|
||||
const event = new Event("visibilityChanged");
|
||||
window.dispatchEvent(event);
|
||||
@@ -133,7 +180,7 @@ function MapLayersControlPanel() {
|
||||
const handlePoiCheckboxChange = event => {
|
||||
const { checked } = event.target;
|
||||
setPoiVisible(checked);
|
||||
localStorage.setItem("poiVisible", checked); // Store POI visibility in localStorage
|
||||
localStorage.setItem("poiVisible", checked.toString()); // Store POI visibility in localStorage
|
||||
};
|
||||
|
||||
const handleIconClick = () => {
|
||||
@@ -143,7 +190,7 @@ function MapLayersControlPanel() {
|
||||
|
||||
//------------------------------
|
||||
useEffect(() => {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
window.__debug = window.__debug || {};
|
||||
window.__debug.gisStations = GisStationsStaticDistrict;
|
||||
}
|
||||
@@ -175,7 +222,7 @@ function MapLayersControlPanel() {
|
||||
}
|
||||
return isUnique;
|
||||
});
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("📌 stationListing aktualisiert:", filteredAreas);
|
||||
}
|
||||
}, [GisStationsStaticDistrict, GisSystemStatic]);
|
||||
@@ -209,13 +256,20 @@ function MapLayersControlPanel() {
|
||||
style={{ minWidth: "150px", maxWidth: "200px" }}
|
||||
>
|
||||
<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(
|
||||
(GisStationsStaticDistrict.Points || [])
|
||||
.filter(p => !!p.Area_Name)
|
||||
.map(p => [p.Area_Name, p])
|
||||
).values(),
|
||||
].map((item, index) => (
|
||||
].map(item => (
|
||||
<option key={item.Area_Name} value={item.IdLD}>
|
||||
{item.Area_Name}
|
||||
</option>
|
||||
@@ -255,9 +309,10 @@ function MapLayersControlPanel() {
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={polylineVisible} // Zustand für Kabelstrecken
|
||||
onChange={handlePolylineCheckboxChange}
|
||||
checked={kabelstreckenVisible}
|
||||
onChange={e => handlePolylineCheckboxChange(e.target.checked)}
|
||||
id="polyline-checkbox"
|
||||
disabled={!isTalasAllowed || editMode}
|
||||
/>
|
||||
<label htmlFor="polyline-checkbox" className="text-sm ml-2">
|
||||
Kabelstrecken
|
||||
@@ -279,25 +334,6 @@ function MapLayersControlPanel() {
|
||||
POIs
|
||||
</label>
|
||||
</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>
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
// config/paths.js
|
||||
const basePathRaw = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
||||
const BASE_PATH = basePathRaw.replace(/^\/|\/$/g, "");
|
||||
export const BASE_URL = BASE_PATH ? `/${BASE_PATH}` : "";
|
||||
let __configCache;
|
||||
export async function getBaseUrl() {
|
||||
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_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
|
||||
NEXT_PUBLIC_API_PORT_MODE=prod
|
||||
|
||||
@@ -25,6 +25,31 @@ Entwicklung, Architekturverständnis und Erweiterung.
|
||||
### ⚙️ Konfiguration
|
||||
|
||||
- [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
|
||||
|
||||
|
||||
33
docs/TODO.md
@@ -69,3 +69,36 @@ die Daten von DB auch mit WebSocket gelöst werden
|
||||
|
||||
- [ ] Redundante Kontextmenülogik auflösen
|
||||
- [ ] 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
|
||||
|
||||
@@ -87,7 +87,7 @@ sequenceDiagram
|
||||
|
||||
- **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.
|
||||
Die Konfiguration erfolgt je nach Umgebung über:
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
# 📁 paths.js
|
||||
|
||||
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.
|
||||
|
||||
## 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
|
||||
const BASE_PATH = basePathRaw.replace(/^\/|\/$/g, "");
|
||||
|
||||
@@ -12,17 +12,17 @@ NodeMap verwendet Umgebungsvariablen zur Steuerung von API-Verhalten, Serverpfad
|
||||
|
||||
## 🔧 Wichtige Variablen
|
||||
|
||||
| Variable | Beispielwert | Beschreibung |
|
||||
| --------------------------- | ------------------- | --------------------------------------------------------------------- |
|
||||
| `DB_HOST` | `localhost` | Adresse des Datenbankservers (MySQL) |
|
||||
| `DB_PORT` | `3306` | Port für die Datenbankverbindung |
|
||||
| `DB_NAME` | `talas` | Datenbankname |
|
||||
| `DB_USER` | `root` | Benutzername 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_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) |
|
||||
| `NEXT_PUBLIC_DEBUG` | `true` oder `false` | Aktiviert zusätzliche `console.log` Ausgaben für Debugging im Browser |
|
||||
| Variable | Beispielwert | Beschreibung |
|
||||
| --------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| `DB_HOST` | `localhost` | Adresse des Datenbankservers (MySQL) |
|
||||
| `DB_PORT` | `3306` | Port für die Datenbankverbindung |
|
||||
| `DB_NAME` | `talas` | Datenbankname |
|
||||
| `DB_USER` | `root` | Benutzername 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_USE_MOCKS` | `true` oder `false` | Aktiviert den Mockdaten-Modus über `/api/mocks/...` |
|
||||
| `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 |
|
||||
|
||||
## 📦 Beispiel `.env.production`
|
||||
|
||||
@@ -34,7 +34,11 @@ DB_USER=root
|
||||
DB_PASSWORD=geheim
|
||||
NEXT_PUBLIC_API_PORT_MODE=prod
|
||||
NEXT_PUBLIC_USE_MOCKS=false
|
||||
NEXT_PUBLIC_BASE_PATH=/talas5
|
||||
// public/config.json
|
||||
{
|
||||
...
|
||||
"basePath": "/talas5"
|
||||
}
|
||||
NEXT_PUBLIC_DEBUG=false
|
||||
```
|
||||
|
||||
@@ -48,7 +52,11 @@ DB_USER=root
|
||||
DB_PASSWORD=geheim
|
||||
NEXT_PUBLIC_API_PORT_MODE=dev
|
||||
NEXT_PUBLIC_USE_MOCKS=true
|
||||
NEXT_PUBLIC_BASE_PATH=/talas5
|
||||
// public/config.json
|
||||
{
|
||||
...
|
||||
"basePath": "/talas5"
|
||||
}
|
||||
NEXT_PUBLIC_DEBUG=true
|
||||
```
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import L from "leaflet";
|
||||
import { getDebugLog } from "../utils/configUtils";
|
||||
|
||||
export class OverlappingMarkerSpiderfier {
|
||||
constructor(map, options = {}) {
|
||||
@@ -118,7 +119,7 @@ export class OverlappingMarkerSpiderfier {
|
||||
// 🔥 Künstliches Click-Event auslösen, um die UI zu aktualisieren
|
||||
setTimeout(() => {
|
||||
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 ");
|
||||
}
|
||||
}, 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
|
||||
```
|
||||
2142
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nodemap",
|
||||
"version": "1.1.300",
|
||||
"version": "1.1.355",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
@@ -39,17 +39,17 @@
|
||||
"scripts": {
|
||||
"dev": "node server.js",
|
||||
"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",
|
||||
"export": "next export",
|
||||
"test": "jest",
|
||||
"cypress": "cypress open",
|
||||
"cypress:run": "cypress run",
|
||||
"prepare": "husky",
|
||||
"bump-version": "node ./scripts/bumpVersion.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.54.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"cypress": "^13.17.0",
|
||||
"husky": "^9.1.7",
|
||||
"identity-obj-proxy": "^3.0.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
|
||||
import getPool from "../../../../utils/mysqlPool";
|
||||
import { getDebugLog } from "../../../../utils/configUtils";
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// /pages/api/talas_v5_DB/gisLines/updateLineCoordinates.js
|
||||
import getPool from "../../../../utils/mysqlPool"; // Singleton-Pool importieren
|
||||
import { getDebugLog } from "../../../../utils/configUtils";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const pool = getPool(); // Singleton-Pool verwenden
|
||||
@@ -34,7 +35,7 @@ export default async function handler(req, res) {
|
||||
|
||||
// Commit der Transaktion
|
||||
await connection.commit();
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("Transaction Complete.");
|
||||
}
|
||||
res.status(200).json({
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// pages/api/talas_v5_DB/pois/addPoi.js
|
||||
import getPool from "../../../../utils/mysqlPool"; // Singleton-Pool importieren
|
||||
import { getDebugLog } from "../../../../utils/configUtils";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const pool = getPool(); // Singleton-Pool verwenden
|
||||
|
||||
if (req.method === "POST") {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
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 = {
|
||||
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({
|
||||
@@ -11,11 +42,51 @@ const polylineLayerVisibleSlice = createSlice({
|
||||
reducers: {
|
||||
setPolylineVisible: (state, action) => {
|
||||
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 selectPolylineVisible = (state) => state.polylineLayerVisible.visible;
|
||||
export const { setPolylineVisible, initializePolylineFromLocalStorage } =
|
||||
polylineLayerVisibleSlice.actions;
|
||||
export const selectPolylineVisible = state => state.polylineLayerVisible.visible;
|
||||
export const selectPolylineInitialized = state => state.polylineLayerVisible.isInitialized;
|
||||
export default polylineLayerVisibleSlice.reducer;
|
||||
|
||||
@@ -15,20 +15,68 @@ const mapLayersSlice = createSlice({
|
||||
},
|
||||
setLayerVisibility: (state, action) => {
|
||||
const { layer, visibility } = action.payload;
|
||||
if (state[layer] !== undefined) {
|
||||
state[layer] = visibility;
|
||||
}
|
||||
state[layer] = visibility; // Sicher setzen
|
||||
},
|
||||
setInitialLayers: (state, action) => {
|
||||
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}`;
|
||||
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 selectMapLayersState = (state) => state.mapLayers || initialState;
|
||||
export const selectMapLayersState = state => state.mapLayers || initialState;
|
||||
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(() => {
|
||||
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);
|
||||
});
|
||||
|
||||
const server = createServer(expressApp);
|
||||
|
||||
const io = new Server(server);
|
||||
|
||||
// ✅ Globaler Cache für alle aktuellen Daten
|
||||
const globalDataCache = new Map();
|
||||
|
||||
io.on("connection", socket => {
|
||||
const { m: idMap, u: idUser } = socket.handshake.query;
|
||||
console.log(`🔌 WebSocket verbunden (idMap=${idMap}, idUser=${idUser})`);
|
||||
|
||||
const cacheKey = `${idMap}_${idUser}`;
|
||||
|
||||
const endpoints = [
|
||||
{
|
||||
name: "GisLinesStatus",
|
||||
@@ -69,6 +99,22 @@ app.prepare().then(() => {
|
||||
|
||||
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 () => {
|
||||
for (const { name, getUrl, mock } of endpoints) {
|
||||
try {
|
||||
@@ -99,6 +145,14 @@ app.prepare().then(() => {
|
||||
}
|
||||
|
||||
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]) {
|
||||
lastDataMap[name] = newDataStr;
|
||||
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();
|
||||
|
||||
// ✅ setInterval für regelmäßige Updates (nur bei Änderungen)
|
||||
const interval = setInterval(fetchData, 5000); // 5 Sekunden ,TALAS.web nutzt 12 Sekunden
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
clearInterval(interval);
|
||||
console.log("❌ WebSocket getrennt");
|
||||
|
||||
0
services/localStorage/stationCacheService.js
Normal file
@@ -1,12 +1,23 @@
|
||||
import { getDebugLog } from "../../utils/configUtils";
|
||||
// /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 () => {
|
||||
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) {
|
||||
const mockBasePath = "/api/mocks/webservice/gisLinesStatus";
|
||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("🧪 Mock-Modus aktiviert: fetchGisLinesStatusService ", mockURL);
|
||||
}
|
||||
|
||||
@@ -16,11 +27,9 @@ export const fetchGisLinesStatusService = async () => {
|
||||
}
|
||||
|
||||
const mockData = await response.json();
|
||||
|
||||
if (!Array.isArray(mockData.Statis)) {
|
||||
throw new Error("Ungültige Struktur: 'Status' fehlt im Mock");
|
||||
}
|
||||
|
||||
return mockData.Statis;
|
||||
} else {
|
||||
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 url = `${baseUrl}/GisLinesStatus?idMap=${idMap}`;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
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 () => {
|
||||
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) {
|
||||
const mockBasePath = "/api/mocks/webservice/gisStationsMeasurements";
|
||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("🧪 Mock-Modus aktiviert: fetchGisStationsMeasurementsService ", mockURL);
|
||||
}
|
||||
|
||||
@@ -28,7 +39,7 @@ export const fetchGisStationsMeasurementsService = async () => {
|
||||
const idUser = params.get("u");
|
||||
|
||||
const url = `${baseUrl}/GisStationsMeasurements?idMap=${idMap}&idUser=${idUser}`;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("📡 fetchGisStationsMeasurementsService URL:", url);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,18 @@
|
||||
* @returns {Promise<Array>} Liste mit Points[]
|
||||
* @throws {Error} bei Fehler oder ungültiger Antwortstruktur
|
||||
*/
|
||||
|
||||
import { getDebugLog, getConfig } from "../../utils/configUtils";
|
||||
|
||||
export const fetchGisStationsStaticDistrictService = async () => {
|
||||
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) {
|
||||
const mockBasePath = "/api/mocks/webservice/gisStationsStaticDistrict";
|
||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("🧪 Mock-Modus aktiviert: fetchGisStationsStaticDistrictService ", mockURL);
|
||||
}
|
||||
|
||||
@@ -35,7 +39,7 @@ export const fetchGisStationsStaticDistrictService = async () => {
|
||||
const idUser = params.get("u");
|
||||
|
||||
const url = `${baseUrl}/GisStationsStaticDistrict?idMap=${idMap}&idUser=${idUser}`;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("📡 fetchGisStationsStaticDistrictService URL:", url);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,18 @@
|
||||
* @returns {Promise<Array>} Liste mit Statis[]
|
||||
* @throws {Error} bei Fehler oder ungültiger Antwortstruktur
|
||||
*/
|
||||
|
||||
import { getDebugLog, getConfig } from "../../utils/configUtils";
|
||||
|
||||
export const fetchGisStationsStatusDistrictService = async () => {
|
||||
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) {
|
||||
const mockBasePath = "/api/mocks/webservice/gisStationsStatusDistrict";
|
||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("🧪 Mock-Modus aktiviert: fetchGisStationsStatusDistrictService ", mockURL);
|
||||
}
|
||||
|
||||
@@ -35,7 +39,7 @@ export const fetchGisStationsStatusDistrictService = async () => {
|
||||
const idUser = params.get("u");
|
||||
|
||||
const url = `${baseUrl}/GisStationsStatusDistrict?idMap=${idMap}&idUser=${idUser}`;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("📡 fetchGisStationsStatusDistrictService URL:", url);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,18 @@
|
||||
* @returns {Promise<Array>} Liste mit Systems[]
|
||||
* @throws {Error} bei Fehler oder ungültiger Antwortstruktur
|
||||
*/
|
||||
|
||||
import { getDebugLog, getConfig } from "../../utils/configUtils";
|
||||
|
||||
export const fetchGisSystemStaticService = async () => {
|
||||
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) {
|
||||
const mockBasePath = "/api/mocks/webservice/gisSystemStatic";
|
||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("🧪 Mock-Modus aktiviert: fetchGisSystemStaticService ", mockURL);
|
||||
}
|
||||
|
||||
@@ -34,7 +38,7 @@ export const fetchGisSystemStaticService = async () => {
|
||||
const idUser = params.get("u");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,18 @@
|
||||
* @returns {Promise<Array>} Rechte-Array
|
||||
* @throws {Error} bei Lade- oder Strukturfehler
|
||||
*/
|
||||
|
||||
import { getDebugLog, getConfig } from "../../utils/configUtils";
|
||||
|
||||
export const fetchUserRightsService = async () => {
|
||||
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) {
|
||||
const mockBasePath = "/api/mocks/webservice/gisSystemStatic";
|
||||
const mockURL = `${window.location.origin}${mockBasePath}`;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("🧪 Mock-Modus aktiviert: fetchUserRightsService ", mockURL);
|
||||
}
|
||||
|
||||
@@ -30,7 +34,7 @@ export const fetchUserRightsService = async () => {
|
||||
const idUser = params.get("u");
|
||||
|
||||
const url = `${baseUrl}/GisSystemStatic?idMap=${idMap}&idUser=${idUser}`;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("🔍 Rechte-Fetch URL:", url);
|
||||
}
|
||||
|
||||
@@ -46,7 +50,7 @@ export const fetchUserRightsService = async () => {
|
||||
}
|
||||
|
||||
const json = await response.json();
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
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,
|
||||
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 {
|
||||
const state = store.getState();
|
||||
const staticDistrictData = selectGisStationsStaticDistrict(state);
|
||||
const statusDistrictData = selectGisStationsStatusDistrict(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 measurementsMap = new Map();
|
||||
measurementData?.forEach(m => {
|
||||
safeMeasurementData.forEach(m => {
|
||||
if (!measurementsMap.has(m.IdLD)) {
|
||||
measurementsMap.set(m.IdLD, m);
|
||||
}
|
||||
@@ -101,10 +120,10 @@ export const createAndSetDevices = async (
|
||||
marker.bindPopup(popupContent);
|
||||
|
||||
// ✅ Tooltip (nur für System 11)
|
||||
|
||||
if (station.System === 11 && messung) {
|
||||
const gmaMap = new Map();
|
||||
|
||||
measurementData?.forEach(m => {
|
||||
safeMeasurementData.forEach(m => {
|
||||
if (!gmaMap.has(m.IdLD)) {
|
||||
gmaMap.set(m.IdLD, {});
|
||||
}
|
||||
|
||||
@@ -6,50 +6,147 @@ import "leaflet-contextmenu/dist/leaflet.contextmenu.css";
|
||||
import "overlapping-marker-spiderfier-leaflet";
|
||||
|
||||
export const initializeMap = (
|
||||
mapRef,
|
||||
setMap,
|
||||
setOms,
|
||||
mapContainer,
|
||||
setMenuItemAdded,
|
||||
addItemsToMapContextMenu,
|
||||
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) {
|
||||
console.error("❌ Fehler: mapRef.current ist nicht definiert.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mapRef.current._leaflet_id) {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
console.log("⚠️ Karte bereits initialisiert");
|
||||
if (
|
||||
!mapContainer ||
|
||||
!(mapContainer instanceof HTMLElement) ||
|
||||
!document.body.contains(mapContainer)
|
||||
) {
|
||||
if (logError) {
|
||||
console.error("❌ Fehler: map container ist nicht definiert oder nicht im DOM.");
|
||||
}
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
const url = new URL(window.location.origin);
|
||||
const originWithoutPort = `${url.protocol}//${url.hostname}`;
|
||||
const tileLayerUrl = `${originWithoutPort}${basePath}/TileMap/mapTiles/{z}/{x}/{y}.png`;
|
||||
// Robuste Entfernung einer evtl. alten Leaflet-Instanz und Reset des DOM-Elements
|
||||
if (mapContainer) {
|
||||
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, {
|
||||
center: [53.111111, 8.4625],
|
||||
zoom: 12,
|
||||
minZoom: 5,
|
||||
maxZoom: 15,
|
||||
zoomControl: false,
|
||||
dragging: true,
|
||||
contextmenu: true,
|
||||
layers: [],
|
||||
});
|
||||
// --- CONFIG LOADING ---
|
||||
let config = null;
|
||||
let tileLayerUrl = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
|
||||
let mapCenter = [53.111111, 8.4625];
|
||||
let mapZoom = 12;
|
||||
let minZoom = 5;
|
||||
let maxZoom = 15;
|
||||
try {
|
||||
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();
|
||||
|
||||
L.tileLayer(tileLayerUrl, {
|
||||
attribution: "© Eigene Kartenquelle (offline)",
|
||||
attribution: "© OpenStreetMap contributors",
|
||||
tileSize: 256,
|
||||
minZoom: 5,
|
||||
maxZoom: 15,
|
||||
minZoom: minZoom,
|
||||
maxZoom: maxZoom,
|
||||
noWrap: true,
|
||||
errorTileUrl: "/img/empty-tile.png", // Optional
|
||||
}).addTo(initMap);
|
||||
@@ -58,10 +155,8 @@ export const initializeMap = (
|
||||
nearbyDistance: 20,
|
||||
});
|
||||
|
||||
setMap(initMap);
|
||||
setOms(overlappingMarkerSpiderfier);
|
||||
|
||||
if (typeof addItemsToMapContextMenu === "function") {
|
||||
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) => {
|
||||
// Debugging-Ausgabe
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("Plus-Icon Position:", clickedLatLng);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ export const handlePlusIconClick = (map, markers, oms, clickedLatLng) => {
|
||||
);
|
||||
|
||||
// Debugging-Ausgabe
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("Gefundene Marker in der Nähe:", nearbyMarkers);
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ export const handlePlusIconClick = (map, markers, oms, clickedLatLng) => {
|
||||
// Spiderfy die gefundenen Marker
|
||||
oms.spiderfy(nearbyMarkers);
|
||||
} else {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("Keine überlappenden Marker gefunden.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import mysql from "mysql2/promise";
|
||||
// Variablen für den Singleton-Pool
|
||||
let cachedPool;
|
||||
let connectionCount = 0; // Zähler für die aktiven Verbindungen
|
||||
let connectionCount = 0;
|
||||
|
||||
// Funktion zum Abrufen des Pools
|
||||
function getPool() {
|
||||
if (!cachedPool) {
|
||||
cachedPool = mysql.createPool({
|
||||
@@ -12,12 +10,11 @@ function getPool() {
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
port: process.env.DB_PORT,
|
||||
connectionLimit: 20, // Setze ein Limit für gleichzeitige Verbindungen
|
||||
connectionLimit: 20,
|
||||
waitForConnections: true,
|
||||
queueLimit: 10, // Warteschlangenlimit für Verbindungen
|
||||
connectTimeout: 5000, // Timeout für Verbindungsversuche (5 Sekunden)
|
||||
//acquireTimeout: 10000, // Timeout für Verbindungsanforderungen aus dem Pool (10 Sekunden)
|
||||
idleTimeout: 60000, // 1 Minute
|
||||
queueLimit: 10,
|
||||
connectTimeout: 5000,
|
||||
idleTimeout: 60000,
|
||||
});
|
||||
|
||||
// Ereignisse für das Protokollieren der Verbindungsstatistiken
|
||||
@@ -25,46 +22,27 @@ function getPool() {
|
||||
|
||||
cachedPool.on("acquire", () => {
|
||||
connectionCount++;
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
console.log("\x1b[36m%s\x1b[0m", `➕ Connection acquired (${connectionCount} total)`);
|
||||
}
|
||||
if (connectionCount > maxUsed) {
|
||||
maxUsed = connectionCount;
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
console.log(`📈 Neue Höchstzahl aktiver gleichzeitiger Verbindungen: ${maxUsed}`);
|
||||
}
|
||||
}
|
||||
// Debug-Logging entfernt
|
||||
if (connectionCount > maxUsed) {
|
||||
maxUsed = connectionCount;
|
||||
}
|
||||
});
|
||||
cachedPool.on("release", () => {
|
||||
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", () => {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
console.warn("\x1b[33m%s\x1b[0m", "⏳ Pool voll – Anfrage in Warteschlange");
|
||||
}
|
||||
}
|
||||
// Debug-Logging entfernt
|
||||
});
|
||||
}
|
||||
|
||||
return cachedPool;
|
||||
}
|
||||
|
||||
// Optionale Funktion zum Schließen aller Verbindungen im Pool (manuell aufzurufen)
|
||||
export function closePool() {
|
||||
if (cachedPool) {
|
||||
cachedPool.end(() => {
|
||||
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
|
||||
|
||||
export function openInNewTab(e, target) {
|
||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
||||
export async function openInNewTab(e, target) {
|
||||
const res = await fetch("/config.json");
|
||||
const config = await res.json();
|
||||
const basePath = config.basePath || "";
|
||||
const url = new URL(window.location.origin);
|
||||
const originWithoutPort = `${url.protocol}//${url.hostname}`; // ohne Port!
|
||||
|
||||
@@ -28,9 +30,7 @@ export function openInNewTab(e, target) {
|
||||
}
|
||||
|
||||
if (link) {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
console.log("🟢 Öffne Link:", link);
|
||||
}
|
||||
console.log("🟢 Öffne Link:", link);
|
||||
window.open(link, "_blank");
|
||||
} else {
|
||||
console.error("❌ Kein gültiger Link gefunden.");
|
||||
|
||||
@@ -6,7 +6,7 @@ export function subscribeToPolylineContextMenu() {
|
||||
store.subscribe(() => {
|
||||
const state = store.getState(); // Redux-Toolkit empfohlene Methode
|
||||
if (state.polylineContextMenu.forceClose) {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("🚀 Redux-Event erkannt - Kontextmenü wird geschlossen.");
|
||||
}
|
||||
store.dispatch(closePolylineContextMenu());
|
||||
|
||||
@@ -17,7 +17,7 @@ import { updatePolylineCoordinatesThunk } from "../../redux/thunks/database/poly
|
||||
import { openInNewTab } from "../../utils/openInNewTab";
|
||||
|
||||
//--------------------------------------------
|
||||
export const setupPolylines = (
|
||||
export const setupPolylines = async (
|
||||
map,
|
||||
linePositions,
|
||||
lineColors,
|
||||
@@ -29,20 +29,23 @@ export const setupPolylines = (
|
||||
polylineVisible
|
||||
) => {
|
||||
const mode = process.env.NEXT_PUBLIC_API_PORT_MODE;
|
||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
||||
if (!polylineVisible) {
|
||||
//console.warn("Polylines deaktiviert - keine Zeichnung");
|
||||
return { markers: [], polylines: [] };
|
||||
}
|
||||
|
||||
let basePath = "";
|
||||
try {
|
||||
const res = await fetch("/config.json");
|
||||
if (res.ok) {
|
||||
const config = await res.json();
|
||||
basePath = config.basePath || "";
|
||||
}
|
||||
} catch (e) {}
|
||||
if (!polylineVisible) {
|
||||
// Entferne alle Polylinien, wenn sie ausgeblendet werden sollen
|
||||
if (window.polylines) {
|
||||
window.polylines.forEach(polyline => {
|
||||
if (map.hasLayer(polyline)) {
|
||||
if (map && map.hasLayer(polyline)) {
|
||||
map.removeLayer(polyline);
|
||||
}
|
||||
});
|
||||
window.polylines = [];
|
||||
}
|
||||
return { markers: [], polylines: [] };
|
||||
}
|
||||
@@ -110,7 +113,7 @@ export const setupPolylines = (
|
||||
.dispatch(updatePolylineCoordinatesThunk(requestData))
|
||||
.unwrap()
|
||||
.then(data => {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("Koordinaten erfolgreich aktualisiert:", data);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
// utils/setupDevices.js
|
||||
|
||||
import { setSelectedDevice, clearSelectedDevice } from "../redux/slices/selectedDeviceSlice";
|
||||
import { getDebugLog } from "./configUtils";
|
||||
|
||||
export const setupDevices = async (map, deviceMarkers, dispatch) => {
|
||||
for (const marker of deviceMarkers) {
|
||||
marker.on("mouseover", function () {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("✅ Gerät ausgewählt:", marker);
|
||||
}
|
||||
dispatch(setSelectedDevice(marker.options)); // Gerät in Redux speichern
|
||||
});
|
||||
|
||||
marker.on("mouseout", function () {
|
||||
if (process.env.NEXT_PUBLIC_DEBUG_LOG === "true") {
|
||||
if (getDebugLog()) {
|
||||
console.log("❌ Gerät abgewählt");
|
||||
}
|
||||
dispatch(clearSelectedDevice()); // Gerät aus Redux entfernen
|
||||
|
||||
@@ -15,29 +15,71 @@ export const zoomIn = (e, map) => {
|
||||
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) {
|
||||
map.flyTo(e.latlng, 14);
|
||||
localStorage.setItem("mapZoom", 16);
|
||||
const currentZoom = map.getZoom();
|
||||
if (currentZoom < maxZoom) {
|
||||
map.flyTo(e.latlng, currentZoom + 1);
|
||||
localStorage.setItem("mapZoom", currentZoom + 1);
|
||||
localStorage.setItem("mapCenter", JSON.stringify(map.getCenter()));
|
||||
}
|
||||
};
|
||||
|
||||
export const zoomOut = (map) => {
|
||||
export const zoomOut = map => {
|
||||
if (!map) {
|
||||
console.error("map is not defined in zoomOut");
|
||||
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();
|
||||
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) {
|
||||
const x = 51.41321407879154;
|
||||
const y = 7.739617925303934;
|
||||
const zoom = 7;
|
||||
|
||||
map.flyTo([x, y], zoom);
|
||||
map.flyTo(zoomOutCenter, zoom);
|
||||
localStorage.setItem("mapZoom", zoom);
|
||||
localStorage.setItem("mapCenter", JSON.stringify(map.getCenter()));
|
||||
}
|
||||
|
||||
@@ -1,153 +1,85 @@
|
||||
[
|
||||
{
|
||||
"LD_Name": "CPL Ismail4",
|
||||
"IdLD": 50922,
|
||||
"Device": "CPL V3.5 mit 24 Kü",
|
||||
"Link": "cpl.aspx?ver=35&kue=24&id=50922",
|
||||
"LD_Name": "CPL Kai Schmidt",
|
||||
"IdLD": 50977,
|
||||
"Device": "CPL V3.5 mit 24 Kü",
|
||||
"Link": "cpl.aspx?ver=35&kue=24&id=50977",
|
||||
"Location_Name": "Littwin",
|
||||
"Location_Short": "LTW",
|
||||
"IdLocation": 24101,
|
||||
"Area_Name": "Rastede",
|
||||
"Area_Name": "Rastede 2",
|
||||
"Area_Short": "",
|
||||
"IdArea": 20998,
|
||||
"X": 53.242157,
|
||||
"Y": 8.160353,
|
||||
"X": 53.245957,
|
||||
"Y": 8.164172,
|
||||
"Icon": 20,
|
||||
"System": 1,
|
||||
"Active": 1
|
||||
},
|
||||
{
|
||||
"LD_Name": "LR 77 ISA",
|
||||
"IdLD": 50935,
|
||||
"Device": "LTE Modem LR77",
|
||||
"Link": "lr77.aspx?ver=1&id=50935",
|
||||
"LD_Name": "GMA-isa-test",
|
||||
"IdLD": 50981,
|
||||
"Device": "Glättemeldeanlage",
|
||||
"Link": "gma.aspx?ver=1&id=50981",
|
||||
"Location_Name": "Littwin",
|
||||
"Location_Short": "LTW",
|
||||
"IdLocation": 24101,
|
||||
"Area_Name": "Rastede",
|
||||
"Area_Name": "Rastede 2",
|
||||
"Area_Short": "",
|
||||
"IdArea": 20998,
|
||||
"X": 53.242157,
|
||||
"Y": 8.160353,
|
||||
"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,
|
||||
"X": 53.245957,
|
||||
"Y": 8.164172,
|
||||
"Icon": 1,
|
||||
"System": 11,
|
||||
"Active": 1
|
||||
},
|
||||
{
|
||||
"LD_Name": "SMS-Funkmodem",
|
||||
"IdLD": 50938,
|
||||
"Device": "SMS Funkmodem",
|
||||
"Link": "sms_modem.aspx?ver=1&id=50938",
|
||||
"LD_Name": "Cisco-isa",
|
||||
"IdLD": 50982,
|
||||
"Device": "Cisco 1841",
|
||||
"Link": "cisco1841.aspx?ver=1&id=50982",
|
||||
"Location_Name": "Littwin",
|
||||
"Location_Short": "LTW",
|
||||
"IdLocation": 24101,
|
||||
"Area_Name": "Rastede",
|
||||
"Area_Name": "Rastede 2",
|
||||
"Area_Short": "",
|
||||
"IdArea": 20998,
|
||||
"X": 53.242157,
|
||||
"Y": 8.160353,
|
||||
"Icon": 12,
|
||||
"System": 111,
|
||||
"X": 53.245957,
|
||||
"Y": 8.164172,
|
||||
"Icon": 21,
|
||||
"System": 6,
|
||||
"Active": 1
|
||||
},
|
||||
{
|
||||
"LD_Name": "TALAS Meldestationen ISA",
|
||||
"IdLD": 50939,
|
||||
"Device": "CPL V3.5 mit 16 Kü",
|
||||
"Link": "cpl.aspx?ver=35&kue=16&id=50939",
|
||||
"LD_Name": "CPL Ganz neu",
|
||||
"IdLD": 50984,
|
||||
"Device": "CPL V3.5 mit 24 Kü",
|
||||
"Link": "cpl.aspx?ver=35&kue=24&id=50984",
|
||||
"Location_Name": "Littwin",
|
||||
"Location_Short": "LTW",
|
||||
"IdLocation": 24101,
|
||||
"Area_Name": "Rastede",
|
||||
"Area_Name": "Rastede 2",
|
||||
"Area_Short": "",
|
||||
"IdArea": 20998,
|
||||
"X": 53.242157,
|
||||
"Y": 8.160353,
|
||||
"X": 53.245957,
|
||||
"Y": 8.164172,
|
||||
"Icon": 20,
|
||||
"System": 1,
|
||||
"Active": 1
|
||||
},
|
||||
{
|
||||
"LD_Name": "WAGO Klemmen ISA",
|
||||
"IdLD": 50941,
|
||||
"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,
|
||||
"LD_Name": "Entwicklung KAS Cisco 8200 für LVZ 2025",
|
||||
"IdLD": 50986,
|
||||
"Device": "Cisco 8200",
|
||||
"Link": "cisco8200.aspx?ver=1&id=50943",
|
||||
"Location_Name": "Littwin",
|
||||
"Location_Short": "LTW",
|
||||
"IdLocation": 24101,
|
||||
"Link": "cisco8200.aspx?ver=1&id=50986",
|
||||
"Location_Name": "SW-Entwicklung 1.01",
|
||||
"Location_Short": "SW101",
|
||||
"IdLocation": 24102,
|
||||
"Area_Name": "Rastede",
|
||||
"Area_Short": "",
|
||||
"IdArea": 20998,
|
||||
"X": 53.242157,
|
||||
"Y": 8.160353,
|
||||
"Area_Short": "RAST-007",
|
||||
"IdArea": 18192,
|
||||
"X": 53.24615,
|
||||
"Y": 8.16237,
|
||||
"Icon": 21,
|
||||
"System": 6,
|
||||
"Active": 1
|
||||
|
||||
@@ -4,25 +4,7 @@
|
||||
"Na": "system",
|
||||
"Le": 4,
|
||||
"Co": "#FF00FF",
|
||||
"Me": "Eingang DE 01 kommend test2",
|
||||
"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",
|
||||
"Me": "Eingang DE 01 kommend",
|
||||
"Feld": 4,
|
||||
"Icon": 0
|
||||
},
|
||||
@@ -40,10 +22,127 @@
|
||||
"Na": "system",
|
||||
"Le": 4,
|
||||
"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",
|
||||
"Feld": 4,
|
||||
"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,
|
||||
"Na": "system",
|
||||
@@ -53,6 +152,51 @@
|
||||
"Feld": 4,
|
||||
"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,
|
||||
"Na": "minor",
|
||||
@@ -76,7 +220,16 @@
|
||||
"Na": "major",
|
||||
"Le": 2,
|
||||
"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,
|
||||
"Icon": 0
|
||||
},
|
||||
@@ -97,5 +250,167 @@
|
||||
"Me": "KÜG 03: Aderbruch kommend",
|
||||
"Feld": 4,
|
||||
"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",
|
||||
"Longname": "Talas Meldestationen",
|
||||
"Allow": 1,
|
||||
"Icon": 1
|
||||
"Icon": 20,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 1,
|
||||
"Name": "TALAS",
|
||||
"Longname": "Talas Meldestationen",
|
||||
"Allow": 0,
|
||||
"Icon": 20,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 2,
|
||||
"Name": "ECI",
|
||||
"Longname": "ECI Geräte",
|
||||
"Allow": 0,
|
||||
"Icon": 17,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 3,
|
||||
"Name": "ULAF",
|
||||
"Longname": "ULAF Geräte",
|
||||
"Allow": 1,
|
||||
"Icon": 2
|
||||
"Icon": 14,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 3,
|
||||
"Name": "ULAF",
|
||||
"Longname": "ULAF Geräte",
|
||||
"Allow": 0,
|
||||
"Icon": 3
|
||||
"Icon": 14,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 5,
|
||||
"Name": "GSM Modem",
|
||||
"Longname": "LR77 GSM Modems",
|
||||
"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,
|
||||
"Name": "Cisco Router",
|
||||
"Longname": "Cisco Router",
|
||||
"Allow": 1,
|
||||
"Icon": 6
|
||||
"Icon": 21,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 6,
|
||||
"Name": "Cisco Router",
|
||||
"Longname": "Cisco Router",
|
||||
"Allow": 0,
|
||||
"Icon": 21,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 7,
|
||||
"Name": "WAGO",
|
||||
"Longname": "WAGO I/O Systeme",
|
||||
"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,
|
||||
"Name": "Siemens",
|
||||
"Longname": "Siemens Notrufsysteme",
|
||||
"Allow": 1,
|
||||
"Icon": 8
|
||||
"Icon": 19,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 8,
|
||||
"Name": "Siemens",
|
||||
"Longname": "Siemens Notrufsysteme",
|
||||
"Allow": 0,
|
||||
"Icon": 19,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 9,
|
||||
"Name": "OTDR",
|
||||
"Longname": "Glasfaserüberwachung OTU",
|
||||
"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,
|
||||
"Name": "WDM",
|
||||
"Longname": " Wavelength Division Multiplexing",
|
||||
"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,
|
||||
"Name": "GMA",
|
||||
"Longname": "Glättemeldeanlagen",
|
||||
"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,
|
||||
"Name": "Messstellen",
|
||||
"Longname": "Messstellen",
|
||||
"Allow": 1,
|
||||
"Icon": 13
|
||||
"Allow": 0,
|
||||
"Icon": 23,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 30,
|
||||
"Name": "TK-Komponenten",
|
||||
"Longname": "TK-Komponenten",
|
||||
"Allow": 1,
|
||||
"Icon": 30
|
||||
"Icon": 14,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 30,
|
||||
"Name": "TK-Komponenten",
|
||||
"Longname": "TK-Komponenten",
|
||||
"Allow": 0,
|
||||
"Icon": 14,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 100,
|
||||
"Name": "TALAS ICL",
|
||||
"Longname": "Talas ICL Unterstationen",
|
||||
"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,
|
||||
"Name": "DAUZ",
|
||||
"Longname": "Dauerzählstellen",
|
||||
"Allow": 1,
|
||||
"Icon": 110
|
||||
"Icon": 14,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 110,
|
||||
"Name": "DAUZ",
|
||||
"Longname": "Dauerzählstellen",
|
||||
"Allow": 0,
|
||||
"Icon": 14,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 111,
|
||||
"Name": "SMS Modem",
|
||||
"Longname": "SMS Modem",
|
||||
"Allow": 1,
|
||||
"Icon": 111
|
||||
"Icon": 12,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 111,
|
||||
"Name": "SMS Modem",
|
||||
"Longname": "SMS Modem",
|
||||
"Allow": 0,
|
||||
"Icon": 12,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 200,
|
||||
"Name": "Sonstige",
|
||||
"Longname": "Sonstige",
|
||||
"Allow": 1,
|
||||
"Icon": 200
|
||||
"Icon": 31,
|
||||
"Map": 1
|
||||
},
|
||||
{
|
||||
"IdSystem": 200,
|
||||
"Name": "Sonstige",
|
||||
"Longname": "Sonstige",
|
||||
"Allow": 0,
|
||||
"Icon": 31,
|
||||
"Map": 1
|
||||
}
|
||||
]
|
||||