polylines tooltip content

This commit is contained in:
ISA
2024-08-10 10:32:37 +02:00
parent b1f7b700ca
commit b7116a1e6f
142 changed files with 14451 additions and 4281 deletions

View File

@@ -1,3 +1,4 @@
// components/DataSheet.js
import React, { useEffect, useState } from "react";
import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil";
import { gisStationsStaticDistrictState } from "../store/atoms/gisStationState";
@@ -5,18 +6,15 @@ import { gisSystemStaticState } from "../store/atoms/gisSystemState";
import { mapLayersState } from "../store/atoms/mapLayersState";
import { selectedAreaState } from "../store/atoms/selectedAreaState";
import { zoomTriggerState } from "../store/atoms/zoomTriggerState";
import { poiLayerVisibleState } from "../store/atoms/poiLayerVisible";
import { poiLayerVisibleState } from "../store/atoms/poiLayerVisibleState";
function DataSheet() {
const [poiVisible, setPoiVisible] = useRecoilState(poiLayerVisibleState);
const setSelectedArea = useSetRecoilState(selectedAreaState);
const [mapLayersVisibility, setMapLayersVisibility] =
useRecoilState(mapLayersState);
const [mapLayersVisibility, setMapLayersVisibility] = useRecoilState(mapLayersState);
const [stationListing, setStationListing] = useState([]);
const [systemListing, setSystemListing] = useState([]);
const GisStationsStaticDistrict = useRecoilValue(
gisStationsStaticDistrictState
);
const GisStationsStaticDistrict = useRecoilValue(gisStationsStaticDistrictState);
const GisSystemStatic = useRecoilValue(gisSystemStaticState);
const setZoomTrigger = useSetRecoilState(zoomTriggerState);
@@ -24,30 +22,31 @@ function DataSheet() {
const selectedIndex = event.target.options.selectedIndex;
const areaName = event.target.options[selectedIndex].text;
setSelectedArea(areaName);
console.log("Area selected oder areaName in DataSheet.js:", areaName);
};
useEffect(() => {
const allowedSystems = new Set(
GisSystemStatic.filter((system) => system.Allow === 1).map(
(system) => system.IdSystem
)
);
console.log("GisStationsStaticDistrict:", GisStationsStaticDistrict);
console.log("GisSystemStatic:", GisSystemStatic);
const allowedSystems = new Set(GisSystemStatic.filter((system) => system.Allow === 1).map((system) => system.IdSystem));
console.log("allowedSystems:", allowedSystems);
const seenNames = new Set();
const filteredAreas = GisStationsStaticDistrict.filter((item) => {
const isUnique =
!seenNames.has(item.Area_Name) && allowedSystems.has(item.System);
const isUnique = !seenNames.has(item.Area_Name) && allowedSystems.has(item.System);
if (isUnique) {
seenNames.add(item.Area_Name);
}
return isUnique;
});
setStationListing(
filteredAreas.map((area, index) => ({
id: index + 1,
name: area.Area_Name,
}))
);
//console.log("filteredAreas:", filteredAreas);
const seenSystemNames = new Set();
const filteredSystems = GisSystemStatic.filter((item) => {
const formattedName = item.Name.replace(/[\s\-]+/g, "");
@@ -57,6 +56,7 @@ function DataSheet() {
}
return isUnique;
});
setSystemListing(
filteredSystems.map((system, index) => ({
id: index + 1,
@@ -64,19 +64,21 @@ function DataSheet() {
}))
);
}, [GisStationsStaticDistrict, GisSystemStatic]);
//}, []);
//-----------------------------------------
//-----------------------------------------
const handleCheckboxChange = (name, event) => {
const { checked } = event.target;
console.log(`Checkbox ${name} checked state:`, checked);
setMapLayersVisibility((prev) => {
const newState = {
...prev,
[name]: checked,
};
console.log(`New mapLayersVisibility state:`, newState);
return newState;
});
console.log("mapLayersVisibility:", mapLayersVisibility);
};
const handleIconClick = () => {
@@ -85,16 +87,14 @@ function DataSheet() {
};
return (
<div
id="mainDataSheet"
className="absolute top-3 right-3 w-1/6 min-w-[300px] z-10 bg-white p-2 rounded-lg shadow-lg"
>
<div id="mainDataSheet" className="absolute top-3 right-3 w-1/6 min-w-[300px] z-10 bg-white p-2 rounded-lg shadow-lg">
<div className="flex flex-col gap-4 p-4">
<div className="flex items-center justify-between">
<select
onChange={handleAreaChange}
id="stationListing"
className="border-solid-1 p-2 rounded ml-1 font-semibold"
role="combobox" // Ensure the correct role is set
>
<option value="Station wählen">Station wählen</option>
{stationListing.map((station) => (
@@ -103,22 +103,15 @@ function DataSheet() {
</option>
))}
</select>
<img
src="/img/expand-icon.svg"
alt="Expand"
className="h-6 w-6 ml-2 cursor-pointer"
onClick={handleIconClick}
/>
<img src="/img/expand-icon.svg" alt="Expand" className="h-6 w-6 ml-2 cursor-pointer" onClick={handleIconClick} />
</div>
<div>
{systemListing.map((system) => (
<React.Fragment key={system.id}>
<input
type="checkbox"
checked={mapLayersVisibility[system.name] || false}
onChange={(e) => handleCheckboxChange(system.name, e)}
/>
<label className="text-sm ml-2">{system.name}</label>
<input type="checkbox" checked={mapLayersVisibility[system.name] || false} onChange={(e) => handleCheckboxChange(system.name, e)} id={`system-${system.id}`} />
<label htmlFor={`system-${system.id}`} className="text-sm ml-2">
{system.name}
</label>
<br />
</React.Fragment>
))}
@@ -128,12 +121,12 @@ function DataSheet() {
onChange={(e) => {
const checked = e.target.checked;
setPoiVisible(checked);
console.log(
`POIs sind jetzt ${checked ? "sichtbar" : "nicht sichtbar"}.`
);
}}
id="poi-checkbox"
/>
<label className="text-sm ml-2">POIs</label>
<label htmlFor="poi-checkbox" className="text-sm ml-2">
POIs
</label>
</div>
</div>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
// components/VersionInfoModal.js
import React from "react";
const VersionInfoModal = ({ showVersionInfoModal, closeVersionInfoModal, MAP_VERSION }) => {
return (
<>
{showVersionInfoModal && (
<div className="fixed inset-0 flex items-center justify-center z-50">
<div className="fixed inset-0 bg-black bg-opacity-50" onClick={closeVersionInfoModal}></div>
<div className="bg-white p-6 rounded-lg shadow-lg z-60 max-w-lg mx-auto">
<img src="img/Logo_TALAS.png" alt="TALAS V5 Logo" className="w-1/2 mx-auto my-4" />
<div className="bg-white border p-6 rounded-lg shadow-lg z-60 max-w-lg mx-auto">
<h2 className="text-xl font-bold mb-6 text-start leading-tight">Littwin Systemtechnik GmbH & Co. KG</h2>
<h4 className="text-lg font-bold mb-2 text-start leading-tight">Bürgermeister-Brötje Str. 28</h4>
<h4 className="text-lg font-bold mb-2 text-start leading-tight">D-26180 Rastede</h4>
<h5 className="text-md mb-2 text-start leading-snug">T: +49 4402 9725 77-0</h5>
<h5 className="text-md mb-2 text-start leading-snug">E: kontakt@littwin-systemtechnik.de</h5>
</div>
<p className="text-gray-700 text-center font-bold mt-4 leading-relaxed">TALAS.Map Version {MAP_VERSION}</p>
<button onClick={closeVersionInfoModal} className="mt-4 bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-700 mx-auto block">
Schließen
</button>
</div>
</div>
)}
</>
);
};
export default VersionInfoModal;

View File

@@ -0,0 +1,12 @@
// /components/gisPolylines/icons/CircleIcon.js
import L from "leaflet";
import "leaflet/dist/leaflet.css";
const CircleIcon = new L.DivIcon({
className: "custom-circle-icon leaflet-marker-icon",
html: '<div style="background-color:gray; width:10px; height:10px; border-radius:50%; border: solid black 1px;"></div>',
iconSize: [25, 25],
iconAnchor: [5, 5],
});
export default CircleIcon;

View File

@@ -0,0 +1,11 @@
// /components/gisPolylines/icons/EndIcon.js
// Viereck als End-Icon für die Route
import L from "leaflet";
const EndIcon = L.divIcon({
className: "custom-end-icon",
html: "<div style='background-color: gray; width: 14px; height: 14px; border: solid black 2px;'></div>", // Graues Viereck
iconSize: [14, 14],
iconAnchor: [7, 7], // Mittelpunkt des Vierecks als Anker
});
export default EndIcon;

View File

@@ -0,0 +1,17 @@
// /components/gisPolylines/icons/StartIcon.js
//Custom triangle icon for draggable markers
import L from "leaflet";
const StartIcon = L.divIcon({
className: "custom-start-icon",
html: `
<svg width="18" height="18" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<polygon points="10,2 18,18 2,18" fill="black" />
<polygon points="10,5 16,16 4,16" fill="gray" />
</svg>
`, // Schwarzes Dreieck innerhalb eines grauen Dreiecks
iconSize: [18, 18],
iconAnchor: [9, 10],
});
export default StartIcon;

View File

@@ -0,0 +1,27 @@
// komponents/gisPolylines/icons/SupportPointIcons
import L from "leaflet";
import "leaflet/dist/leaflet.css";
// Icon für Stützpunkt hinzufügen
export const AddSupportPointIcon = L.divIcon({
className: "custom-add-support-point-icon",
html: `
<div style='background-color:green;border-radius:50%;width:12px;height:12px;border: solid white 2px;'>
<div style='color: white; font-size: 10px; text-align: center; line-height: 12px;'>+</div>
</div>
`,
iconSize: [24, 24],
iconAnchor: [12, 12],
});
// Icon für Stützpunkt entfernen
export const RemoveSupportPointIcon = L.divIcon({
className: "custom-remove-support-point-icon",
html: `
<div style='background-color:red;border-radius:50%;width:12px;height:12px;border: solid white 2px;'>
<div style='color: white; font-size: 10px; text-align: center; line-height: 12px;'>-</div>
</div>
`,
iconSize: [24, 24],
iconAnchor: [12, 12],
});

160
components/imports.js Normal file
View File

@@ -0,0 +1,160 @@
// imports.js
import React, { useEffect, useRef, useState, useCallback } from "react";
import L, { marker } from "leaflet";
import "leaflet/dist/leaflet.css";
import "leaflet-contextmenu/dist/leaflet.contextmenu.css";
import "leaflet-contextmenu";
import * as config from "../config/config.js";
import * as urls from "../config/urls.js";
import "leaflet.smooth_marker_bouncing";
import OverlappingMarkerSpiderfier from "overlapping-marker-spiderfier-leaflet";
import DataSheet from "./DataSheet.js";
import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil";
import { gisStationsStaticDistrictState } from "../store/atoms/gisStationState.js";
import { gisSystemStaticState } from "../store/atoms/gisSystemState.js";
import { mapLayersState } from "../store/atoms/mapLayersState.js";
import { selectedAreaState } from "../store/atoms/selectedAreaState.js";
import { zoomTriggerState } from "../store/atoms/zoomTriggerState.js";
import { poiTypState } from "../store/atoms/poiTypState.js";
import AddPoiModalWindow from "./pois/AddPoiModalWindow.js";
import { poiReadFromDbTriggerAtom } from "../store/atoms/poiReadFromDbTriggerAtom.js";
import { InformationCircleIcon } from "@heroicons/react/20/solid"; // oder 'outline'
import PoiUpdateModal from "./pois/PoiUpdateModal.js";
import { selectedPoiState } from "../store/atoms/poiState.js";
import { currentPoiState } from "../store/atoms/currentPoiState.js";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { mapIdState, userIdState } from "../store/atoms/urlParameterState.js";
import { poiLayerVisibleState } from "../store/atoms/poiLayerVisibleState.js";
import plusRoundIcon from "./PlusRoundIcon.js";
import { parsePoint, findClosestPoints } from "../utils/geometryUtils.js";
import { insertNewMarker, removeMarker, createAndSetMarkers, handleEditPoi } from "../utils/markerUtils.js";
import { redrawPolyline, restoreMapSettings, checkOverlappingMarkers } from "../utils/mapUtils.js";
import circleIcon from "./gisPolylines/icons/CircleIcon.js";
import startIcon from "./gisPolylines/icons/StartIcon.js";
import endIcon from "./gisPolylines/icons/EndIcon.js";
import { fetchGisStatusStations, fetchPriorityConfig, fetchPoiData, updateLocationInDatabase, fetchUserRights, fetchDeviceNameById } from "../services/apiService.js";
import { addContextMenuToMarker } from "../utils/contextMenuUtils.js";
import { MAP_VERSION } from "../config/settings.js";
import * as layers from "../config/layers.js";
import { zoomIn, zoomOut, centerHere } from "../utils/zoomAndCenterUtils.js";
import { initializeMap } from "../utils/mapInitialization.js";
import { addItemsToMapContextMenu } from "./useMapContextMenu.js";
import useGmaMarkersLayer from "../hooks/useGmaMarkersLayer.js"; // Import the custom hook
import useTalasMarkersLayer from "../hooks/talasMarkersLayer.js"; // Import the custom hook
import useEciMarkersLayer from "../hooks/useEciMarkersLayer.js";
import useGsmModemMarkersLayer from "../hooks/useGsmModemMarkersLayer.js";
import useCiscoRouterMarkersLayer from "../hooks/useCiscoRouterMarkersLayer.js";
import useWagoMarkersLayer from "../hooks/useWagoMarkersLayer.js";
import useSiemensMarkersLayer from "../hooks/useSiemensMarkersLayer.js";
import useOtdrMarkersLayer from "../hooks/useOtdrMarkersLayer.js";
import useWdmMarkersLayer from "../hooks/useWdmMarkersLayer.js";
import useMessstellenMarkersLayer from "../hooks/useMessstellenMarkersLayer.js";
import useTalasiclMarkersLayer from "../hooks/useTalasiclMarkersLayer.js";
import useDauzMarkersLayer from "../hooks/useDauzMarkersLayer.js";
import useSmsfunkmodemMarkersLayer from "../hooks/useSmsfunkmodemMarkersLayer.js";
import useUlafMarkersLayer from "../hooks/useUlafMarkersLayer.js";
import useSonstigeMarkersLayer from "../hooks/useSonstigeMarkersLayer.js";
import handlePoiSelect from "../utils/handlePoiSelect.js";
import { fetchGisStationsStaticDistrict, fetchGisStationsStatusDistrict, fetchGisStationsMeasurements, fetchGisSystemStatic } from "../services/fetchData.js";
import { setupPolylines, setupMarkers } from "../utils/mapFeatures.js"; // Import the functions
import VersionInfoModal from "./VersionInfoModal.js";
//--------------------------------------------
import PoiUpdateModalWrapper from "./pois/PoiUpdateModalWrapper";
import AddPoiModalWindowWrapper from "./pois/AddPoiModalWindowWrapper";
import useFetchPoiData from "../hooks/useFetchPoiData";
import usePoiTypData from "../hooks/usePoiTypData";
import useMarkerLayers from "../hooks/useMarkerLayers";
import useLayerVisibility from "../hooks/useLayerVisibility";
import useLineData from "../hooks/useLineData.js";
export {
React,
useEffect,
useRef,
useState,
useCallback,
L,
marker,
config,
urls,
OverlappingMarkerSpiderfier,
DataSheet,
useRecoilState,
useRecoilValue,
useSetRecoilState,
gisStationsStaticDistrictState,
gisSystemStaticState,
mapLayersState,
selectedAreaState,
zoomTriggerState,
poiTypState,
AddPoiModalWindow,
poiReadFromDbTriggerAtom,
InformationCircleIcon,
PoiUpdateModal,
selectedPoiState,
currentPoiState,
ToastContainer,
toast,
mapIdState,
userIdState,
poiLayerVisibleState,
plusRoundIcon,
parsePoint,
findClosestPoints,
insertNewMarker,
removeMarker,
createAndSetMarkers,
handleEditPoi,
redrawPolyline,
restoreMapSettings,
checkOverlappingMarkers,
circleIcon,
startIcon,
endIcon,
fetchGisStatusStations,
fetchPriorityConfig,
fetchPoiData,
updateLocationInDatabase,
fetchUserRights,
fetchDeviceNameById,
addContextMenuToMarker,
MAP_VERSION,
layers,
zoomIn,
zoomOut,
centerHere,
initializeMap,
addItemsToMapContextMenu,
useGmaMarkersLayer,
useTalasMarkersLayer,
useEciMarkersLayer,
useGsmModemMarkersLayer,
useCiscoRouterMarkersLayer,
useWagoMarkersLayer,
useSiemensMarkersLayer,
useOtdrMarkersLayer,
useWdmMarkersLayer,
useMessstellenMarkersLayer,
useTalasiclMarkersLayer,
useDauzMarkersLayer,
useSmsfunkmodemMarkersLayer,
useUlafMarkersLayer,
useSonstigeMarkersLayer,
handlePoiSelect,
fetchGisStationsStaticDistrict,
fetchGisStationsStatusDistrict,
fetchGisStationsMeasurements,
fetchGisSystemStatic,
setupPolylines,
setupMarkers,
VersionInfoModal,
PoiUpdateModalWrapper,
AddPoiModalWindowWrapper,
useFetchPoiData,
usePoiTypData,
useMarkerLayers,
useLayerVisibility,
useLineData,
};

View File

@@ -0,0 +1,145 @@
// components/pois/AddPoiModalWindow.js
import React, { useState, useEffect, use } from "react";
import ReactDOM from "react-dom";
import { useRecoilValue, useRecoilState, useSetRecoilState } from "recoil";
import { readPoiMarkersStore } from "../../store/selectors/readPoiMarkersStore";
import { poiReadFromDbTriggerAtom } from "../../store/atoms/poiReadFromDbTriggerAtom";
const AddPoiModalWindow = ({ onClose, map, latlng }) => {
const [poiTypData, setpoiTypData] = useState(); // Recoil State verwenden
const [name, setName] = useState("");
const [poiTypeId, setPoiTypeId] = useState(""); // Initialize as string
const [poiTypeName, setPoiTypeName] = useState(""); // Initialize as string
const [latitude] = useState(latlng.lat.toFixed(5));
const [longitude] = useState(latlng.lng.toFixed(5));
const setLoadData = useSetRecoilState(readPoiMarkersStore);
const setTrigger = useSetRecoilState(poiReadFromDbTriggerAtom);
const [locationDeviceData, setLocationDeviceData] = useState([]);
const [deviceName, setDeviceName] = useState("");
//------------------------------------------------------------------------------------------
useEffect(() => {
const fetchInitialData = async () => {
try {
const [poiTypResponse, locationDeviceResponse] = await Promise.all([fetch("/api/talas_v5_DB/poiTyp/readPoiTyp"), fetch("/api/talas5/location_device")]);
const poiTypData = await poiTypResponse.json();
setpoiTypData(poiTypData);
if (poiTypData.length > 0) {
setPoiTypeId(poiTypData[0].idPoiTyp); // Set initial poiTypeId to the id of the first poiType
if (poiTypData[1]) {
setPoiTypeName(poiTypData[1].name);
}
}
const locationDeviceData = await locationDeviceResponse.json();
setLocationDeviceData(locationDeviceData);
if (locationDeviceData.length > 0) {
setDeviceName(locationDeviceData[0].name); // Set initial device name
}
} catch (error) {
console.error("Fehler beim Abrufen der Daten:", error);
}
};
fetchInitialData();
}, []);
//------------------------------------------------------------------------------------------
//-----------------handleSubmit-------------------
const handleSubmit = async (event) => {
event.preventDefault();
const formData = {
name,
poiTypeId,
latitude,
longitude,
idLD: locationDeviceData.find((device) => device.name === deviceName).idLD,
};
const response = await fetch("/api/talas_v5_DB/pois/addLocation", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(formData),
});
if (response.ok) {
setTrigger((trigger) => {
//console.log("Aktueller Trigger-Wert:", trigger); // Vorheriger Wert
const newTrigger = trigger + 1;
//console.log("Neuer Trigger-Wert:", newTrigger); // Aktualisierter Wert
onClose();
return newTrigger;
});
// Browser aktualisieren
window.location.reload();
} else {
console.error("Fehler beim Hinzufügen des POI");
}
if (map && typeof map.closePopup === "function") {
map.closePopup();
}
};
//-----------------handleSubmit-------------------
return (
<form onSubmit={handleSubmit} className="m-0 p-2 w-full ">
<div className="flex items-center mb-4">
<label htmlFor="name" className="block mr-2 flex-none">
Name :
</label>
<input type="text" id="name" name="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="Name der Station" className="block p-2 w-full border-2 border-gray-200 rounded-md text-sm" />
</div>
{/* {locationDeviceData.----------------------------------------------*/}
<div className="flex items-center mb-4">
<label htmlFor="deviceName" className="block mr-2 flex-none">
Gerät :
</label>
<select id="deviceName" name="deviceName" value={deviceName} onChange={(e) => setDeviceName(e.target.value)} className="block p-2 w-full border-2 border-gray-200 rounded-md text-sm">
{locationDeviceData.map((device, index) => (
<option key={index} value={device.name}>
{device.name}
</option>
))}
</select>
</div>
{/* {locationDeviceData.----------------------------------------------*/}
<div className="flex items-center mb-4">
<label htmlFor="idPoiTyp2" className="block mr-2 flex-none">
Typ:
</label>
<select id="idPoiTyp2" name="idPoiTyp2" value={poiTypeId} onChange={(e) => setPoiTypeId(e.target.value)} className="block p-2 w-full border-2 border-gray-200 rounded-md text-sm">
{poiTypData &&
poiTypData.map((poiTyp, index) => (
<option key={poiTyp.idPoiTyp || index} value={poiTyp.idPoiTyp}>
{poiTyp.name}
</option>
))}
</select>
</div>
<div className="flex flex-row items-center justify-center">
<div className="flex items-center mb-4">
<label htmlFor="lat" className="block mr-2 flex-none text-xs">
Lat : {latitude}
</label>
</div>
<div className="flex items-center mb-4">
<label htmlFor="lng" className="block mr-2 flex-none text-xs">
Lng : {longitude}
</label>
</div>
</div>
<button type="submit" className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded w-full">
POI hinzufügen
</button>
</form>
);
};
export default AddPoiModalWindow;

View File

@@ -0,0 +1,24 @@
// components/pois/AddPoiModalWindowPopup.js
import React from "react";
import AddPoiModalWindow from "./AddPoiModalWindow.js";
const AddPoiModalWindowPopup = ({ showPopup, closePopup, handleAddStation, popupCoordinates }) => {
return (
<>
{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>
<AddPoiModalWindow onClose={closePopup} onSubmit={handleAddStation} latlng={popupCoordinates} />
</div>
</div>
)}
</>
);
};
export default AddPoiModalWindowPopup;

View File

@@ -0,0 +1,30 @@
// components/pois/AddPoiModalWindowWrapper.js
import React from "react";
import AddPoiModalWindow from "./AddPoiModalWindow";
const AddPoiModalWindowWrapper = ({ show, onClose, latlng, handleAddStation }) => {
return (
show && (
<div
className="fixed inset-0 bg-black bg-opacity-10 flex justify-center items-center z-[1000]"
onClick={onClose}
data-testid="modal-overlay" // Hinzugefügt, um den Hintergrund zu identifizieren
>
<div
className="relative bg-white p-6 rounded-lg shadow-lg"
onClick={(e) => e.stopPropagation()}
role="dialog" // Hinzugefügt, um das Dialog-Element zu identifizieren
>
<button onClick={onClose} 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>
<AddPoiModalWindow onClose={onClose} onSubmit={handleAddStation} latlng={latlng} />
</div>
</div>
)
);
};
export default AddPoiModalWindowWrapper;

View File

@@ -0,0 +1,213 @@
// components/pois/poiUpdateModal.js
import React, { useState, useEffect } from "react";
import { useRecoilValue } from "recoil";
import { selectedPoiState } from "../../store/atoms/poiState";
import { currentPoiState } from "../../store/atoms/currentPoiState";
const PoiUpdateModal = ({ onClose, poiData, onSubmit }) => {
const currentPoi = useRecoilValue(currentPoiState);
const selectedPoi = useRecoilValue(selectedPoiState);
const [poiId, setPoiId] = useState(poiData ? poiData.idPoi : "");
const [name, setName] = useState(poiData ? poiData.name : "");
const [poiTypData, setPoiTypData] = useState([]);
const [poiTypeId, setPoiTypeId] = useState("");
const [locationDeviceData, setLocationDeviceData] = useState([]);
const [deviceName, setDeviceName] = useState("");
const [idLD, setIdLD] = useState(poiData ? poiData.idLD : "");
const [description, setDescription] = useState(poiData ? poiData.description : "");
useEffect(() => {
if (poiData) {
//console.log("Initial poiData:", poiData);
setPoiId(poiData.idPoi);
setName(poiData.name);
setPoiTypeId(poiData.idPoiTyp);
setIdLD(poiData.idLD);
setDescription(poiData.description);
setDeviceName(poiData.idLD);
//console.log("Loaded POI Data for editing:", poiData);
}
}, [poiData]);
useEffect(() => {
const fetchDeviceId = async () => {
if (poiData && poiData.idLD) {
try {
const response = await fetch(`/api/talas_v5_DB/locationDevice/getDeviceIdById?idLD=${poiData.idLD}`);
const data = await response.json();
if (data) setDeviceName(data.name);
} catch (error) {
console.error("Fehler beim Abrufen der Geräteinformation in PoiUpdateModel.js: ", error);
}
}
};
fetchDeviceId();
}, [poiData]);
const handleDeletePoi = async () => {
if (confirm("Sind Sie sicher, dass Sie diesen POI löschen möchten?")) {
try {
const response = await fetch(`/api/talas_v5_DB/pois/deletePoi?id=${poiId}`, {
method: "DELETE",
});
if (response.ok) {
alert("POI wurde erfolgreich gelöscht.");
onClose();
window.location.reload();
} else {
throw new Error("Fehler beim Löschen des POI.");
}
} catch (error) {
console.error("Fehler beim Löschen des POI:", error);
alert("Fehler beim Löschen des POI.");
}
}
};
useEffect(() => {
const fetchPoiTypData = async () => {
try {
const response = await fetch("/api/talas_v5_DB/poiTyp/readPoiTyp");
const data = await response.json();
setPoiTypData(data);
if (selectedPoi && data) {
const matchingType = data.find((pt) => pt.name === selectedPoi.typ);
if (matchingType) {
setPoiTypeId(matchingType.idPoiTyp);
}
}
} catch (error) {
console.error("Fehler beim Abrufen der poiTyp Daten:", error);
}
};
fetchPoiTypData();
}, [selectedPoi]);
useEffect(() => {
const fetchData = async () => {
try {
// const response = await fetch("/api/talas_v5/location_device"); //"/api/talas_v5_DB/locationDevice/location_device"
const response = await fetch("/api/talas_v5_DB/locationDevice/locationDevices");
const data = await response.json();
setLocationDeviceData(data);
if (poiData && poiData.idLD) {
const selectedDevice = data.find((device) => device.id === poiData.idLD);
setDeviceName(selectedDevice ? selectedDevice.id : data[0].id);
//console.log("Selected Device in poiUpdate:", selectedDevice);
}
} catch (error) {
console.error("Fehler beim Abrufen der Standort- und Gerätedaten:", error);
}
};
fetchData();
}, []);
useEffect(() => {
fetch("/api/talas_v5_DB/locationDevice/locationDevices")
.then((response) => response.json())
.then((data) => {
setLocationDeviceData(data);
const currentDevice = data.find((device) => device.idLD === currentPoi.idLD);
if (currentDevice) {
setDeviceName(currentDevice.name);
//console.log("Current Device name in poiUpdate:", currentDevice.name);
}
})
.catch((error) => {
console.error("Fehler beim Abrufen der Gerätedaten:", error);
setLocationDeviceData([]);
});
}, [poiData?.idLD, currentPoi]);
const handleSubmit = async (event) => {
event.preventDefault();
const idLDResponse = await fetch(`/api/talas_v5_DB/locationDevice/getDeviceId?deviceName=${encodeURIComponent(deviceName)}`);
const idLDData = await idLDResponse.json();
const idLD = idLDData.idLD;
try {
const response = await fetch("/api/talas_v5_DB/pois/updatePoi", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
idPoi: poiId,
name: name,
description: description,
idPoiTyp: poiTypeId,
idLD: idLD,
}),
});
if (response.ok) {
onClose();
window.location.reload();
} else {
const errorResponse = await response.json();
throw new Error(errorResponse.error || "Fehler beim Aktualisieren des POI.");
}
} catch (error) {
console.error("Fehler beim Aktualisieren des POI:", error);
alert("Fehler beim Aktualisieren des POI.");
}
};
return (
<div className="fixed inset-0 bg-black bg-opacity-10 flex justify-center items-center z-[1000]" onClick={onClose}>
<div className="relative bg-white p-6 rounded-lg shadow-lg" onClick={(e) => e.stopPropagation()}>
<button onClick={onClose} 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>
<form onSubmit={handleSubmit} className="m-0 p-2 w-full">
<div className="flex items-center mb-4">
<label htmlFor="description" className="block mr-2 flex-none">
Beschreibung:
</label>
<input type="text" id="description" name="description" value={description} onChange={(e) => setDescription(e.target.value)} placeholder="Beschreibung der Station" className="block p-2 w-full border-2 border-gray-200 rounded-md text-sm" />
</div>
<div className="flex items-center mb-4">
<label htmlFor="deviceName" className="block mr-2 flex-none">
Gerät:
</label>
<select id="deviceName" name="deviceName" value={deviceName} onChange={(e) => setDeviceName(e.target.value)} className="block p-2 w-full border-2 border-gray-200 rounded-md text-sm">
{locationDeviceData.map((device, index) => (
<option key={index} value={device.id}>
{device.name}
</option>
))}
</select>
</div>
<div className="flex items-center mb-4">
<label htmlFor="idPoiTyp2" className="block mr-2 flex-none">
Typ:
</label>
<select id="idPoiTyp2" name="idPoiTyp2" value={poiTypeId} onChange={(e) => setPoiTypeId(e.target.value)} className="block p-2 w-full border-2 border-gray-200 rounded-md text-sm">
{poiTypData.map((poiTyp, index) => (
<option key={index} value={poiTyp.idPoiTyp}>
{poiTyp.name}
</option>
))}
</select>
</div>
<button type="button" onClick={handleDeletePoi} className="bg-red-400 hover:bg-red-600 text-white font-bold py-2 px-4 rounded w-full mb-4">
POI löschen
</button>
<button type="submit" className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded w-full">
POI aktualisieren
</button>
</form>
</div>
</div>
);
};
export default PoiUpdateModal;

View File

@@ -0,0 +1,9 @@
// components/pois/PoiUpdateModalWindow.js
import React from "react";
import PoiUpdateModal from "./PoiUpdateModal.js";
const PoiUpdateModalWindow = ({ showPoiUpdateModal, closePoiUpdateModal, currentPoiData, popupCoordinates }) => {
return <>{showPoiUpdateModal && <PoiUpdateModal onClose={closePoiUpdateModal} poiData={currentPoiData} onSubmit={() => {}} latlng={popupCoordinates} />}</>;
};
export default PoiUpdateModalWindow;

View File

@@ -0,0 +1,26 @@
// components/pois/PoiUpdateModalWrapper.js
import React, { useState } from "react";
import PoiUpdateModal from "./PoiUpdateModal";
import { useRecoilValue, useSetRecoilState } from "recoil";
import { currentPoiState, selectedPoiState } from "../../store/atoms/poiState";
import { poiReadFromDbTriggerAtom } from "../../store/atoms/poiReadFromDbTriggerAtom";
const PoiUpdateModalWrapper = ({ show, onClose, latlng }) => {
const setSelectedPoi = useSetRecoilState(selectedPoiState);
const setCurrentPoi = useSetRecoilState(currentPoiState);
const currentPoi = useRecoilValue(currentPoiState);
const poiReadTrigger = useRecoilValue(poiReadFromDbTriggerAtom);
return (
show && (
<PoiUpdateModal
onClose={onClose}
poiData={currentPoi}
onSubmit={() => {}} // Add your submit logic here
latlng={latlng}
/>
)
);
};
export default PoiUpdateModalWrapper;

View File

@@ -0,0 +1,52 @@
// components/pois/PoiUtils.js
import L from "leaflet";
// Funktion, um POI Markers zu erstellen
export const createPoiMarkers = (poiData, iconPath) => {
return poiData.map((location) => {
return L.marker([location.latitude, location.longitude], {
icon: L.icon({
iconUrl: iconPath,
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
draggable: true,
}),
id: location.idPoi,
});
});
};
// Funktion zum Hinzufügen von Markern zur Karte und zum Umgang mit Events
export const addMarkersToMap = (markers, map, layerGroup) => {
markers.forEach((marker) => {
marker.addTo(layerGroup);
marker.on("mouseover", () => marker.openPopup());
marker.on("mouseout", () => marker.closePopup());
marker.on("dragend", (e) => {
const newLat = e.target.getLatLng().lat;
const newLng = e.target.getLatLng().lng;
const markerId = e.target.options.id;
updateLocationInDatabase(markerId, newLat, newLng);
});
});
};
// Funktion zum Aktualisieren der Standorte in der Datenbank
export const updateLocationInDatabase = async (id, newLatitude, newLongitude) => {
const response = await fetch("/api/talas_v5_DB/pois/updateLocation", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
id,
latitude: newLatitude,
longitude: newLongitude,
}),
});
if (!response.ok) {
console.error("Fehler beim Aktualisieren der Position");
}
};
// Weitere Funktionen können hier hinzugefügt werden

View File

@@ -0,0 +1,77 @@
// /components/useMapContextMenu.js
import { toast } from "react-toastify";
import { zoomIn, zoomOut, centerHere } from "../utils/zoomAndCenterUtils"; // Assuming these are imported correctly
const zoomInCallback = (e, map) => {
zoomIn(e, map);
};
const zoomOutCallback = (map) => {
zoomOut(map);
};
const centerHereCallback = (e, map) => {
centerHere(e, map);
};
// Funktion zum Anzeigen der Koordinaten
const showCoordinates = (e) => {
alert("Breitengrad: " + e.latlng.lat.toFixed(5) + "\nLängengrad: " + e.latlng.lng.toFixed(5));
};
// Kontextmenü Callback für "POI hinzufügen"
const addStationCallback = (event, hasRights, setShowPopup, setPopupCoordinates) => {
if (hasRights) {
setPopupCoordinates(event.latlng);
setShowPopup(true);
} else {
toast.error("Benutzer hat keine Berechtigung zum Hinzufügen.", {
position: "top-center",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
}
};
export const addItemsToMapContextMenu = (map, menuItemAdded, setMenuItemAdded, hasRights, setShowPopup, setPopupCoordinates) => {
if (!menuItemAdded && map && map.contextmenu) {
map.contextmenu.addItem({
text: "Koordinaten anzeigen",
icon: "img/not_listed_location.png",
callback: showCoordinates,
});
map.contextmenu.addItem({ separator: true });
map.contextmenu.addItem({
text: "Reinzoomen",
icon: "img/zoom_in.png",
callback: (e) => zoomInCallback(e, map),
});
map.contextmenu.addItem({
text: "Rauszoomen",
icon: "img/zoom_out.png",
callback: () => zoomOutCallback(map),
});
map.contextmenu.addItem({
text: "Hier zentrieren",
icon: "img/center_focus.png",
callback: (e) => centerHereCallback(e, map),
});
map.contextmenu.addItem({ separator: true });
map.contextmenu.addItem({
text: "POI hinzufügen",
icon: "img/add_station.png",
className: "background-red",
callback: (event) => addStationCallback(event, hasRights, setShowPopup, setPopupCoordinates),
});
setMenuItemAdded(true);
}
};