Files
nodeMap/components/mainComponent/MapComponent.js
ISA be2da8414c git commit -m "refactor: Gerätelayer dynamisch über IdSystem initialisiert
BREAKING CHANGE: Sichtbarkeit der Gerätegruppen basiert nun auf 'system-<IdSystem>' statt auf Namen wie 'SMSFunkmodem'. Statische Layer-Konfiguration im Redux-Slice entfernt."
2025-06-02 09:00:49 +02:00

749 lines
30 KiB
JavaScript

// components/mainComponent/MapComponent.js
import React, { useEffect, useRef, useState, useCallback } from "react";
import L from "leaflet";
import "leaflet/dist/leaflet.css";
import "leaflet-contextmenu/dist/leaflet.contextmenu.css";
import "leaflet-contextmenu";
import "leaflet.smooth_marker_bouncing";
import "react-toastify/dist/ReactToastify.css";
import { InformationCircleIcon } from "@heroicons/react/20/solid";
import PoiUpdateModal from "../pois/PoiUpdateModal.js";
import { ToastContainer, toast } from "react-toastify";
import plusRoundIcon from "../icons/devices/overlapping/PlusRoundIcon.js";
import { restoreMapSettings, checkOverlappingMarkers } from "../../utils/mapUtils.js";
import { APP_VERSION } from "../../config/appVersion.js";
import addItemsToMapContextMenu from "../contextmenu/useMapContextMenu.js";
import useAreaMarkersLayer from "../../hooks/layers/useAreaMarkersLayer.js";
import { setupPolylines } from "../../utils/polylines/setupPolylines.js";
import { setupPOIs } from "../../utils/setupPOIs.js";
import useLineData from "../../hooks/useLineData.js";
import { useMapComponentState } from "../../hooks/useMapComponentState.js";
import CoordinatePopup from "../contextmenu/CoordinatePopup.js";
//----------Ui Widgets----------------
import MapLayersControlPanel from "../uiWidgets/mapLayersControlPanel/MapLayersControlPanel.js";
import CoordinateInput from "../uiWidgets/CoordinateInput.js";
import VersionInfoModal from "../uiWidgets/VersionInfoModal.js";
//----------Daten aus API--------------------
import { fetchPoiDataService } from "../../services/database/pois/fetchPoiDataByIdService.js";
import AddPOIModal from "../pois/AddPOIModal.js";
import { enablePolylineEvents, disablePolylineEvents } from "../../utils/polylines/eventHandlers";
//----------MapComponent.js hooks--------------------
import useInitializeMap from "./hooks/useInitializeMap";
//-------------------Redux--------------------
import { useSelector, useDispatch } from "react-redux";
//-------------------Redux-Slices--------------------
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 { setCurrentPoi } from "../../redux/slices/database/pois/currentPoiSlice.js";
import { selectGisLines } from "../../redux/slices/database/polylines/gisLinesSlice";
import { selectGisLinesStatus } from "../../redux/slices/webservice/gisLinesStatusSlice";
import { selectPoiTypData, selectPoiTypStatus } from "../../redux/slices/database/pois/poiTypSlice";
import { selectPriorityConfig } from "../../redux/slices/database/priorityConfigSlice.js";
import { selectPoiIconsData, selectPoiIconsStatus } from "../../redux/slices/database/pois/poiIconsDataSlice";
import { selectGisLinesStatusFromWebservice } from "../../redux/slices/webservice/gisLinesStatusSlice";
import { selectGisUserRightsFromWebservice } from "../../redux/slices/webservice/userRightsSlice";
import { updateCountdown, closePolylineContextMenu } from "../../redux/slices/database/polylines/polylineContextMenuSlice.js";
import { selectPolylineVisible, setPolylineVisible } from "../../redux/slices/database/polylines/polylineLayerVisibleSlice.js";
import { selectGisStationsStaticDistrict } from "../../redux/slices/webservice/gisStationsStaticDistrictSlice.js";
import { selectGisSystemStatic, setGisSystemStatic } from "../../redux/slices/webservice/gisSystemStaticSlice.js";
//-----------Redux-Thunks-------------------
import { fetchGisStationsMeasurementsThunk } from "../../redux/thunks/webservice/fetchGisStationsMeasurementsThunk";
import { fetchGisSystemStaticThunk } from "../../redux/thunks/webservice/fetchGisSystemStaticThunk";
import { fetchGisStationsStaticDistrictThunk } from "../../redux/thunks/webservice/fetchGisStationsStaticDistrictThunk";
import { fetchGisStationsStatusDistrictThunk } from "../../redux/thunks/webservice/fetchGisStationsStatusDistrictThunk";
import { fetchLocationDevicesThunk } from "../../redux/thunks/database/fetchLocationDevicesThunk";
import { fetchPriorityConfigThunk } from "../../redux/thunks/database/fetchPriorityConfigThunk.js";
import { fetchGisLinesThunk } from "../../redux/thunks/database/polylines/fetchGisLinesThunk.js";
import { fetchGisLinesStatusThunk } from "../../redux/thunks/webservice/fetchGisLinesStatusThunk";
import { fetchUserRightsThunk } from "../../redux/thunks/webservice/fetchUserRightsThunk";
import { fetchPoiIconsDataThunk } from "../../redux/thunks/database/pois/fetchPoiIconsDataThunk.js";
import { fetchPoiTypThunk } from "../../redux/thunks/database/pois/fetchPoiTypThunk.js";
import { updateAreaThunk } from "../../redux/thunks/database/area/updateAreaThunk";
import useDynamicDeviceLayers from "../../hooks/layers/useDynamicDeviceLayers";
//-----------------------------------------------------------------------------------------------------
const MapComponent = ({ locations, onLocationUpdate, lineCoordinates }) => {
//-------------------------------
const dispatch = useDispatch();
const countdown = useSelector((state) => state.polylineContextMenu.countdown);
const countdownActive = useSelector((state) => state.polylineContextMenu.countdownActive);
const isPolylineContextMenuOpen = useSelector((state) => state.polylineContextMenu.isOpen);
const polylineVisible = useSelector(selectPolylineVisible);
const isPoiTypLoaded = useSelector((state) => state.poiTypes.status === "succeeded");
const statusMeasurements = useSelector((state) => state.gisStationsMeasurements.status);
const statusSystem = useSelector((state) => state.gisSystemStatic.status);
const statusStaticDistrict = useSelector((state) => state.gisStationsStaticDistrict.status);
const statusStatusDistrict = useSelector((state) => state.gisStationsStatusDistrict.status);
const priorityConfig = useSelector(selectPriorityConfig);
const poiLayerVisible = useSelector((state) => state.poiLayerVisible.visible);
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) || {};
const selectedArea = useSelector((state) => state.selectedArea.area);
const linesData = useSelector((state) => state.gisLinesFromDatabase.data);
const gisLinesStatus = useSelector((state) => state.gisLinesStatusFromWebservice.status);
const { data: gisLinesStatusData, status: statusGisLinesStatus } = useSelector(selectGisLinesStatusFromWebservice);
const poiIconsData = useSelector(selectPoiIconsData);
const poiIconsStatus = useSelector(selectPoiIconsStatus);
const poiTypData = useSelector(selectPoiTypData);
const poiTypStatus = useSelector((state) => state.poiTypes.status);
//const poiTypStatus = useSelector(selectPoiTypStatus);
//-------------------------------
const { deviceName, setDeviceName } = useMapComponentState();
const [locationDeviceData, setLocationDeviceData] = useState([]);
const [menuItemAdded, setMenuItemAdded] = useState(false);
const [isPopupOpen, setIsPopupOpen] = useState(false);
const closePopup = () => setIsPopupOpen(false);
const [currentCoordinates, setCurrentCoordinates] = useState("");
const [showPoiUpdateModal, setShowPoiUpdateModal] = useState(false);
const [currentPoiData, setCurrentPoiData] = useState(null);
const [showVersionInfoModal, setShowVersionInfoModal] = useState(false);
const [poiTypMap, setPoiTypMap] = useState(new Map());
const [showPopup, setShowPopup] = useState(false);
const poiLayerRef = useRef(null); // Referenz auf die Layer-Gruppe für Datenbank-Marker
const mapRef = useRef(null); // Referenz auf das DIV-Element der Karte
const [map, setMap] = useState(null); // Zustand der Karteninstanz
const [oms, setOms] = useState(null); // State für OMS-Instanz
//-----userRights----------------
const isRightsLoaded = useSelector((state) => state.gisUserRightsFromWebservice.status === "succeeded");
const userRights = useSelector(selectGisUserRightsFromWebservice);
const hasRights = userRights.includes(56);
//-----------------------------
const openPopupWithCoordinates = (e) => {
const coordinates = `${e.latlng.lat.toFixed(5)}, ${e.latlng.lng.toFixed(5)}`;
setCurrentCoordinates(coordinates);
setIsPopupOpen(true);
setPopupCoordinates(e.latlng);
setPopupVisible(true);
};
// Konstanten für die URLs
//console.log("priorityConfig in MapComponent1: ", priorityConfig);
//-----------------------------------------
const [linePositions, setLinePositions] = useState([]);
const { lineColors, tooltipContents } = useLineData();
const [polylines, setPolylines] = useState([]);
const [markers, setMarkers] = useState([]);
const [newPoint, setNewPoint] = useState(null);
const [newCoords, setNewCoords] = useState(null);
const [tempMarker, setTempMarker] = useState(null);
const [showPoiModal, setShowPoiModal] = useState(false);
const [showCoordinatesModal, setShowCoordinatesModal] = useState(false);
const [popupCoordinates, setPopupCoordinates] = useState(null);
const [popupVisible, setPopupVisible] = useState(false);
const [poiData, setPoiData] = useState([]);
const openVersionInfoModal = () => {
setShowVersionInfoModal(true);
};
const closeVersionInfoModal = () => {
setShowVersionInfoModal(false);
};
const [currentZoom, setCurrentZoom] = useState(() => {
const storedZoom = localStorage.getItem("mapZoom");
return storedZoom ? parseInt(storedZoom, 10) : 12;
});
const [currentCenter, setCurrentCenter] = useState(() => {
const storedCenter = localStorage.getItem("mapCenter");
try {
return storedCenter ? JSON.parse(storedCenter) : [53.111111, 8.4625];
} catch (e) {
console.error("Error parsing stored map center:", e);
return [53.111111, 8.4625];
}
});
//--------------------------------------------
const handleCoordinatesSubmit = (coords) => {
const [lat, lng] = coords.split(",").map(Number);
if (map && lat && lng) {
map.flyTo([lat, lng], 12); // Zentriere die Karte auf die Koordinaten
}
};
//-----------------------------Map Initialisierung----------------
useInitializeMap(map, mapRef, setMap, setOms, setMenuItemAdded, addItemsToMapContextMenu, hasRights, (value) => dispatch(setDisabled(value)));
//-------------------------React Hooks--------------------------------
useEffect(() => {
if (linesData && Array.isArray(linesData)) {
const transformed = linesData.map((item) => ({
coordinates: item.points.map((point) => [point.x, point.y]),
idModul: item.idModul,
idLD: item.idLD,
}));
setLinePositions(transformed);
}
}, [linesData]);
//--------------------------------------------
useEffect(() => {
dispatch(fetchPoiIconsDataThunk());
dispatch(fetchPoiTypThunk());
}, [dispatch]);
//--------------------------------------------
// POIs auf die Karte zeichnen
useEffect(() => {
if (map && !poiLayerRef.current) {
poiLayerRef.current = new L.LayerGroup().addTo(map);
}
return () => {
if (map && poiLayerRef.current) {
map.removeLayer(poiLayerRef.current);
poiLayerRef.current = new L.LayerGroup().addTo(map);
}
locations.forEach((location) => {});
};
//console.log("trigger in MapComponent.js:", poiReadTrigger);
}, [map, locations, poiReadTrigger]);
//--------------------------------------------
// POIs auf die Karte zeichnen
useEffect(() => {
if (poiData.length === 0) return;
setupPOIs(
map,
locations,
poiData,
poiTypMap,
userRights,
poiLayerRef,
setSelectedPoi,
setLocationDeviceData,
setDeviceName,
setCurrentPoi,
poiLayerVisible,
fetchPoiDataService,
toast,
setShowPoiUpdateModal,
setCurrentPoiData,
deviceName,
dispatch
);
}, [map, locations, onLocationUpdate, poiReadTrigger, isPoiTypLoaded, userRights, poiLayerVisible, poiData, poiTypMap, dispatch]);
//---------------------------------------------
//console.log("priorityConfig in MapComponent2: ", priorityConfig);
useEffect(() => {
if (map) {
}
}, [map, GisSystemStatic, priorityConfig]);
//--------------------------------------------
useEffect(() => {
if (map) {
var x = 51.41321407879154;
var y = 7.739617925303934;
var zoom = 7;
if (map && map.flyTo) {
map.flyTo([x, y], zoom);
} else {
console.error("Map object is not ready or does not have flyTo method");
}
}
}, [map, zoomTrigger]);
//--------------------------------------------
//-----------------------------------------------------------------
//Tooltip an mouse position anzeigen für die Linien
useEffect(() => {
if (!map) return;
// Entferne alte Marker und Polylinien
markers.forEach((marker) => marker.remove());
polylines.forEach((polyline) => polyline.remove());
// 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
);
newPolylines.forEach((polyline, index) => {
//console.log("polyline: ", polyline);
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.on("mouseover", (e) => {
const tooltip = polyline.getTooltip();
if (tooltip) {
const mousePos = e.containerPoint;
const mapSize = map.getSize();
let 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";
}
tooltip.options.direction = direction;
polyline.openTooltip(e.latlng);
}
});
polyline.on("mouseout", () => {
polyline.closeTooltip();
});
});
setMarkers(newMarkers);
setPolylines(newPolylines);
}, [map, linePositions, lineColors, tooltipContents, newPoint, newCoords, tempMarker, polylineVisible]);
//--------------------------------------------
//Test in useEffect
useEffect(() => {
if (map) {
console.log("🗺️ Map-Einstellungen werden wiederhergestellt...");
restoreMapSettings(map);
}
}, [map]);
//--------------------------------------------
useEffect(() => {
if (map) {
console.log("map in MapComponent: ", map);
const handleMapMoveEnd = (event) => {
const newCenter = map.getCenter();
const newZoom = map.getZoom();
setCurrentCenter([newCenter.lat, newCenter.lng]);
setCurrentZoom(newZoom);
localStorage.setItem("mapCenter", JSON.stringify([newCenter.lat, newCenter.lng]));
localStorage.setItem("mapZoom", newZoom);
};
map.on("moveend", handleMapMoveEnd);
map.on("zoomend", handleMapMoveEnd);
return () => {
map.off("moveend", handleMapMoveEnd);
map.off("zoomend", handleMapMoveEnd);
};
}
}, [map]);
//--------------------------------------------
// Area in DataSheet ->dropdownmenu
useEffect(() => {
//console.log("🔍 GisStationsStaticDistrict Inhalt:", GisStationsStaticDistrict);
// Sicherstellen, dass `Points` existiert und ein Array ist
const points = GisStationsStaticDistrict?.Points;
if (selectedArea && map) {
const station = points.find((s) => s.Area_Name === selectedArea);
if (station) {
console.log("📌 Gefundene Station:", station);
map.flyTo([station.X, station.Y], 14);
} else {
console.warn("⚠️ Keine passende Station für die Area gefunden:", selectedArea);
}
}
}, [selectedArea, map, GisStationsStaticDistrict]);
//-------------------------------------
useEffect(() => {
if (zoomTrigger && map) {
map.flyTo([51.41321407879154, 7.739617925303934], 7);
}
}, [zoomTrigger, map]);
//--------------------------------------------
useEffect(() => {
if (map && poiLayerRef.current && isPoiTypLoaded && !menuItemAdded && isRightsLoaded) {
addItemsToMapContextMenu(map, menuItemAdded, setMenuItemAdded, hasRights, setShowPopup, setPopupCoordinates);
}
}, [map, poiLayerRef, isPoiTypLoaded, menuItemAdded, hasRights, isRightsLoaded]);
//--------------------------------------------
// rote Marker ganz oben wenn überlappen sind
//--------------------------------------------
useEffect(() => {
if (map) {
if (polylineEventsDisabled) {
disablePolylineEvents(window.polylines);
} else {
enablePolylineEvents(window.polylines, window.lineColors);
}
}
}, [map, polylineEventsDisabled]);
//--------------------------------------------
useEffect(() => {
if (map) {
console.log("6- Karteninstanz (map) wurde jetzt erfolgreich initialisiert");
}
}, [map]);
//--------------------------------------------
useEffect(() => {
const initializeContextMenu = () => {
if (map) {
map.whenReady(() => {
setTimeout(() => {
if (map.contextmenu) {
//console.log("Contextmenu ist vorhanden");
} else {
console.warn("Contextmenu ist nicht verfügbar.");
}
}, 500);
});
}
};
initializeContextMenu();
}, [map]);
//---------------------------------------
// Initialisiere Leaflet-Karte
// Rufe useAreaMarkersLayer direkt auf
const urlParams = new URLSearchParams(window.location.search); // URL-Parameter parsen
const mValue = urlParams.get("m"); // Wert von "m" holen
const hostname = window.location.hostname; // Dynamischer Hostname
const port = 3000; // Definiere den gewünschten Port
const areaUrl = `http://${hostname}:${port}/api/talas_v5_DB/area/readArea?m=${mValue}`; // Dynamischer Hostname und Port
// Areas-Marker basierend auf dynamischer URL laden
const handleLocationUpdate = async (idLocation, idMap, newCoords) => {
try {
await dispatch(updateAreaThunk({ idLocation, idMap, newCoords })).unwrap();
console.log("Koordinaten erfolgreich aktualisiert:", result);
} catch (error) {
console.error("Fehler beim Aktualisieren der Location:", error);
}
};
// Areas-Marker basierend auf dynamischer URL laden
const areaMarkers = useAreaMarkersLayer(map, oms, areaUrl, handleLocationUpdate);
/*
Areamarker werden jetzt nur angezeigt, wenn der editMode aktiviert ist.
Marker werden bei deaktiviertem editMode aus der Karte entfernt.
Dynamische Überwachung von Änderungen im editMode über localStorage und Event Listener implementiert.
Dragging für Marker im editMode aktiviert und Z-Index angepasst.
*/
useEffect(() => {
const editMode = localStorage.getItem("editMode") === "true";
// Prüfe, ob der editMode deaktiviert ist
if (!editMode) {
// Entferne alle Marker aus der Karte
if (!map) return; // Sicherstellen, dass map existiert
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) => {
if (!map.hasLayer(marker)) {
marker.addTo(map); // Layer hinzufügen
}
marker.dragging.enable();
marker.setZIndexOffset(1000); // Marker nach oben setzen
});
}
}, [areaMarkers, map]);
//----------------------------------
const { markerStates, layerRefs } = useDynamicDeviceLayers(map, GisSystemStatic, mapLayersVisibility, priorityConfig, oms);
useEffect(() => {
const handleVisibilityChange = () => {
if (!map) return;
const allMarkers = Object.values(markerStates).filter(Array.isArray).flat();
checkOverlappingMarkers(map, allMarkers, plusRoundIcon, oms);
};
window.addEventListener("visibilityChanged", handleVisibilityChange);
return () => {
window.removeEventListener("visibilityChanged", handleVisibilityChange);
};
}, [map, markerStates]);
//---------------------------------------
useEffect(() => {
if (map) {
// initGeocoderFeature(map); // Geocoder-Feature initialisieren, kann von .env.local ausgeschaltet werden
}
}, [map]);
//--------------------------------------------
useEffect(() => {
if (map && !menuItemAdded) {
addItemsToMapContextMenu(
map,
menuItemAdded,
setMenuItemAdded,
setShowCoordinatesModal,
setShowPoiModal,
setPopupCoordinates,
openPopupWithCoordinates // Diese Funktion wird jetzt übergeben!
);
}
}, [map, menuItemAdded]);
//--------------------------------------------
useEffect(() => {
dispatch(fetchUserRightsThunk());
}, [dispatch]);
//--------------------------------------------
// Beim ersten Client-Render den Wert aus localStorage laden
useEffect(() => {
const storedPolylineVisible = localStorage.getItem("polylineVisible") === "true";
dispatch(setPolylineVisible(storedPolylineVisible));
}, [dispatch]);
//--------------------------------------------
useEffect(() => {
if (statusStaticDistrict === "idle") {
dispatch(fetchGisStationsStaticDistrictThunk());
}
}, [statusStaticDistrict, dispatch]);
useEffect(() => {
if (statusStatusDistrict === "idle") {
dispatch(fetchGisStationsStatusDistrictThunk());
}
}, [statusStatusDistrict, dispatch]);
useEffect(() => {
if (statusMeasurements === "idle") {
dispatch(fetchGisStationsMeasurementsThunk());
}
}, [statusMeasurements, dispatch]);
useEffect(() => {
if (statusSystem === "idle") {
dispatch(fetchGisSystemStaticThunk());
}
}, [statusSystem, dispatch]);
useEffect(() => {
dispatch(fetchGisSystemStaticThunk());
}, [dispatch]);
useEffect(() => {
dispatch(fetchLocationDevicesThunk());
}, [dispatch]);
//---------------------------------------------------------------
useEffect(() => {
const params = new URL(window.location.href).searchParams;
dispatch(setMapId(params.get("m")));
dispatch(setUserId(params.get("u")));
}, [dispatch]);
//---------------------------------------------------------------
useEffect(() => {
dispatch(fetchPriorityConfigThunk());
}, [dispatch]);
//--------------------------------------------------------
useEffect(() => {
if (gisLinesStatus === "idle") {
dispatch(fetchGisLinesThunk());
}
}, [gisLinesStatus, dispatch]);
//--------------------------------------------------------
useEffect(() => {
dispatch(fetchGisLinesStatusThunk());
}, [dispatch]);
//---------------------------------------------------------
const rights = useSelector((state) => state.gisUserRightsFromWebservice.rights);
useEffect(() => {
dispatch(fetchUserRightsThunk());
}, [dispatch]);
//----------------------------------------------------
useEffect(() => {
if (poiTypStatus === "idle") {
dispatch(fetchPoiTypThunk());
}
}, [poiTypStatus, dispatch]);
//--------------------------------------
useEffect(() => {
if (isPolylineContextMenuOpen && countdownActive) {
//console.log("🔄 Starte Redux-Countdown für Kontextmenü!");
const interval = setInterval(() => {
dispatch(updateCountdown());
// console.log(`⏳ Redux Countdown: ${countdown} Sekunden`);
if (countdown <= 2) {
console.log("🚀 Kontextmenü wird wegen Countdown < 2 geschlossen.");
dispatch(closePolylineContextMenu());
if (window.map?.contextmenu) {
window.map.contextmenu.hide();
}
clearInterval(interval);
}
}, 1000);
return () => {
clearInterval(interval);
};
}
}, [isPolylineContextMenuOpen, countdown, countdownActive, dispatch, window.map]);
//----------------------------------
// map in window setzen für mehr Debugging
useEffect(() => {
if (map) {
window.map = map;
console.log("✅ window.map wurde gesetzt:", window.map);
}
}, [map]);
//---------------------------------------
// **Fehlerbehandlung für `contextmenu`**
// damit den Fehler mit contextmenu nicht angezeigt wird und überspringt wird und die Seite neu geladen wird
useEffect(() => {
window.onerror = function (message, source, lineno, colno, error) {
if (message.includes("Cannot read properties of null (reading 'contextmenu')")) {
console.warn("⚠️ Fehler mit `contextmenu` erkannt - Neuladen der Seite.");
setTimeout(() => {
window.location.reload();
}, 0); // **Seite nach Sekunde neu laden**
return true; // **Fehler unterdrücken, damit React ihn nicht anzeigt**
}
};
return () => {
window.onerror = null; // **Fehlerbehandlung entfernen, wenn Komponente unmounted wird**
};
}, []);
//------------------------------------------------
useEffect(() => {
if (poiTypStatus === "succeeded" && Array.isArray(poiTypData)) {
const map = new Map();
poiTypData.forEach((item) => map.set(item.idPoiTyp, item.name));
setPoiTypMap(map);
}
}, [poiTypData, poiTypStatus]);
//------------------------------------------------
useEffect(() => {
if (poiIconsStatus === "succeeded") {
setPoiData(poiIconsData);
}
}, [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]);
//---------------------------------------------
// 🧠 Optional für Debugging für überlappende Markers
useEffect(() => {
if (oms) {
console.log("📌 OMS ready:", oms);
window.oms = oms; // Für Debugging global
}
}, [oms]);
//---------------------------------------------
//--------------------------------------------
return (
<>
{/* Zeigt das POI-Modal, wenn `showPoiModal` true ist */}
{showPoiModal && <AddPOIModal latlng={popupCoordinates} onClose={() => setShowPoiModal(false)} />}
<ToastContainer />
<div>{showPoiUpdateModal && <PoiUpdateModal onClose={() => setShowPoiUpdateModal(false)} poiData={currentPoiData} onSubmit={() => {}} latlng={popupCoordinates} />}</div>
<div>
{showPopup && (
<div className="fixed inset-0 bg-black bg-opacity-10 flex justify-center items-center z-[1000]" onClick={closePopup}>
<div className="relative bg-white p-6 rounded-lg shadow-lg" onClick={(e) => e.stopPropagation()}>
<button onClick={closePopup} className="absolute top-0 right-0 mt-2 mr-2 p-1 text-gray-700 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-gray-600" aria-label="Close">
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
)}
</div>
{GisStationsStaticDistrict && GisStationsStaticDistrict.Points?.length > 0 && <MapLayersControlPanel className="z-50" />}
<CoordinateInput onCoordinatesSubmit={handleCoordinatesSubmit} />
<div id="map" ref={mapRef} className="z-0" style={{ height: "100vh", width: "100vw" }}></div>
<CoordinatePopup isOpen={isPopupOpen} coordinates={currentCoordinates} onClose={closePopup} />
<div className="absolute bottom-3 left-3 w-72 p-4 bg-white rounded-lg shadow-md z-50">
<div className="flex justify-between items-center">
<div>
<span className="text-black text-lg font-semibold"> TALAS.Map </span>
<br />
<span className="text-black text-lg">Version {APP_VERSION}</span>
</div>
<div>
<button onClick={openVersionInfoModal}>
<InformationCircleIcon className="text-blue-900 h-8 w-8 pr-1" title="Weitere Infos" />
</button>
</div>
</div>
</div>
<VersionInfoModal showVersionInfoModal={showVersionInfoModal} closeVersionInfoModal={closeVersionInfoModal} APP_VERSION={APP_VERSION} />
</>
);
};
export default MapComponent;